Browse Source

feat(docs): runtime node & schema docs (#503)

* feat(docs): runtime workflow schema docs

* feat(docs): runtime node docs
Louis Young 6 months ago
parent
commit
7220014b9b

+ 4 - 2
apps/docs/src/en/guide/runtime/_meta.json

@@ -1,6 +1,8 @@
 [
   "introduction",
   "quick-start",
-  "source-code-guide",
-  "api"
+  "schema",
+  "node",
+  "api",
+  "source-code-guide"
 ]

+ 1 - 1
apps/docs/src/en/guide/runtime/introduction.mdx

@@ -128,7 +128,7 @@ Currently only JavaScript/TypeScript version is available, with plans to develop
 
 ### Feature Enhancements
 
-- Add more node types: Code, Intent, Batch, Break, Continue.
+- Add more node types: Code, Intent, Batch, Break, Continue, HTTP.
 - Improve error handling and exception recovery mechanisms
 - Add complete server-side validation, including schema validation and input validation.
 - Support `Docker` deployment.

+ 600 - 0
apps/docs/src/en/guide/runtime/node.mdx

@@ -0,0 +1,600 @@
+# Nodes
+
+This document provides a detailed introduction to the node system in FlowGram Runtime, including basic node concepts, existing node types and their usage, and how to create custom nodes.
+
+Existing Nodes:
+
+- Start Node
+- End Node
+- LLM Node
+- Condition Node
+- Loop Node
+
+> Future support will include Code, Intent, Batch, Break, Continue, and HTTP nodes
+
+## Node Overview
+
+### The Role of Nodes in FlowGram Runtime
+
+Nodes are the basic execution units of FlowGram workflows, with each node representing a specific operation or function. A FlowGram workflow is essentially a directed graph formed by multiple nodes connected by edges, describing the execution process of a task. The core responsibilities of the node system include:
+
+1. **Executing Specific Operations**: Each type of node has its specific functionality, such as starting a workflow, calling an LLM model, performing conditional judgments, etc.
+2. **Processing Inputs and Outputs**: Nodes receive input data, perform operations, and produce output data
+3. **Controlling Execution Flow**: Control the execution path of the workflow through condition nodes and loop nodes
+
+### Introduction to INodeExecutor Interface
+
+All node executors must implement the `INodeExecutor` interface, which defines the basic structure of a node executor:
+
+```typescript
+interface INodeExecutor {
+  // Node type, used to identify different kinds of nodes
+  type: string;
+
+  // Execute method, handles the specific logic of the node
+  execute(context: ExecutionContext): Promise<ExecutionResult>;
+}
+```
+
+Where:
+- `type`: Node type identifier, such as 'start', 'end', 'llm', etc.
+- `execute`: Node execution method, receives execution context, returns execution result
+
+### Node Execution Process
+
+The node execution process is as follows:
+
+1. **Preparation Phase**:
+   - Get the node's input data from the execution context
+   - Validate whether the input data meets the requirements
+
+2. **Execution Phase**:
+   - Execute the node-specific business logic
+   - Handle possible exception situations
+
+3. **Completion Phase**:
+   - Generate the node's output data
+   - Update the node status
+   - Return the execution result
+
+The workflow engine schedules the execution of nodes in sequence according to the connection relationships between nodes. For special nodes (such as condition nodes and loop nodes), the engine will decide the next execution path based on the execution results of the node.
+
+## Detailed Introduction to Existing Nodes
+
+FlowGram Runtime currently implements five types of nodes: Start, End, LLM, Condition, and Loop. Below is a detailed introduction to each type of node's functionality, configuration, and usage examples.
+
+### Start Node
+
+#### Functionality
+
+The Start node is the starting node of the workflow, used to receive the input data of the workflow and begin the execution of the workflow. Each workflow must have one and only one Start node.
+
+#### Configuration Options
+
+| Option | Type | Required | Description |
+|------|------|------|------|
+| outputs | JSONSchema | Yes | Defines the input data structure of the workflow |
+
+#### Usage Example
+
+```json
+{
+  "id": "start_0",
+  "type": "start",
+  "data": {
+    "title": "Start Node",
+    "outputs": {
+      "type": "object",
+      "properties": {
+        "prompt": {
+          "type": "string",
+          "description": "User input prompt"
+        }
+      },
+      "required": ["prompt"]
+    }
+  }
+}
+```
+
+In this example, the Start node defines that the workflow needs a string type input named `prompt`.
+
+### End Node
+
+#### Functionality
+
+The End node is the ending node of the workflow, used to collect the output data of the workflow and end the execution of the workflow. Each workflow must have at least one End node.
+
+#### Configuration Options
+
+| Option | Type | Required | Description |
+|------|------|------|------|
+| inputs | JSONSchema | Yes | Defines the output data structure of the workflow |
+| inputsValues | `Record<string, ValueSchema>` | Yes | Defines the output data values of the workflow, can be references or constants |
+
+#### Usage Example
+
+```json
+{
+  "id": "end_0",
+  "type": "end",
+  "data": {
+    "title": "End Node",
+    "inputs": {
+      "type": "object",
+      "properties": {
+        "result": {
+          "type": "string",
+          "description": "Output result of the workflow"
+        }
+      }
+    },
+    "inputsValues": {
+      "result": {
+        "type": "ref",
+        "content": ["llm_0", "result"]
+      }
+    }
+  }
+}
+```
+
+In this example, the End node defines that the output of the workflow contains a string named `result`, whose value is referenced from the `result` output of the node with ID `llm_0`.
+
+### LLM Node
+
+#### Functionality
+
+The LLM node is used to call large language models to perform natural language processing tasks, and is one of the most commonly used node types in FlowGram workflows.
+
+#### Configuration Options
+
+| Option | Type | Required | Description |
+|------|------|------|------|
+| modelName | string | Yes | Model name, such as "gpt-3.5-turbo" |
+| apiKey | string | Yes | API key |
+| apiHost | string | Yes | API host address |
+| temperature | number | Yes | Temperature parameter, controls the randomness of the output |
+| systemPrompt | string | No | System prompt, sets the role and behavior of the AI assistant |
+| prompt | string | Yes | User prompt, i.e., the question or request posed to the AI |
+
+#### Usage Example
+
+```json
+{
+  "id": "llm_0",
+  "type": "llm",
+  "data": {
+    "title": "LLM Node",
+    "inputsValues": {
+      "modelName": {
+        "type": "constant",
+        "content": "gpt-3.5-turbo"
+      },
+      "apiKey": {
+        "type": "constant",
+        "content": "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+      },
+      "apiHost": {
+        "type": "constant",
+        "content": "https://api.openai.com/v1"
+      },
+      "temperature": {
+        "type": "constant",
+        "content": 0.7
+      },
+      "systemPrompt": {
+        "type": "constant",
+        "content": "You are a helpful assistant."
+      },
+      "prompt": {
+        "type": "ref",
+        "content": ["start_0", "prompt"]
+      }
+    },
+    "inputs": {
+      "type": "object",
+      "required": ["modelName", "apiKey", "apiHost", "temperature", "prompt"],
+      "properties": {
+        "modelName": { "type": "string" },
+        "apiKey": { "type": "string" },
+        "apiHost": { "type": "string" },
+        "temperature": { "type": "number" },
+        "systemPrompt": { "type": "string" },
+        "prompt": { "type": "string" }
+      }
+    },
+    "outputs": {
+      "type": "object",
+      "properties": {
+        "result": { "type": "string" }
+      }
+    }
+  }
+}
+```
+
+In this example, the LLM node uses the gpt-3.5-turbo model, with a temperature parameter of 0.7, a system prompt set to "You are a helpful assistant", and a user prompt referenced from the input of the Start node.
+
+### Condition Node
+
+#### Functionality
+
+The Condition node is used to select different execution branches based on conditions, implementing conditional logic in the workflow.
+
+#### Configuration Options
+
+| Option | Type | Required | Description |
+|------|------|------|------|
+| conditions | Array | Yes | Array of conditions, each condition contains key and value |
+
+Structure of condition value:
+
+| Option | Type | Required | Description |
+|------|------|------|------|
+| left | ValueSchema | Yes | Left value, can be a reference or constant |
+| operator | string | Yes | Operator, such as "eq", "gt", etc. |
+| right | ValueSchema | Yes | Right value, can be a reference or constant |
+
+Supported operators:
+
+| Operator | Description | Applicable Types |
+|--------|------|----------|
+| eq | Equal to | All types |
+| neq | Not equal to | All types |
+| gt | Greater than | Numbers, strings |
+| gte | Greater than or equal to | Numbers, strings |
+| lt | Less than | Numbers, strings |
+| lte | Less than or equal to | Numbers, strings |
+| includes | Contains | Strings, arrays |
+| startsWith | Starts with | Strings |
+| endsWith | Ends with | Strings |
+
+#### Usage Example
+
+```json
+{
+  "id": "condition_0",
+  "type": "condition",
+  "data": {
+    "title": "Condition Node",
+    "conditions": [
+      {
+        "key": "if_true",
+        "value": {
+          "left": {
+            "type": "ref",
+            "content": ["start_0", "value"]
+          },
+          "operator": "gt",
+          "right": {
+            "type": "constant",
+            "content": 10
+          }
+        }
+      },
+      {
+        "key": "if_false",
+        "value": {
+          "left": {
+            "type": "ref",
+            "content": ["start_0", "value"]
+          },
+          "operator": "lte",
+          "right": {
+            "type": "constant",
+            "content": 10
+          }
+        }
+      }
+    ]
+  }
+}
+```
+
+In this example, the condition node defines two branches: when the value output of the Start node is greater than 10, it takes the "if_true" branch, otherwise it takes the "if_false" branch.
+
+### Loop Node
+
+#### Functionality
+
+The Loop node is used to perform the same operation on each element in an array, implementing loop logic in the workflow.
+
+#### Configuration Options
+
+| Option | Type | Required | Description |
+|------|------|------|------|
+| batchFor | ValueSchema | Yes | The array to iterate over, usually a reference |
+| blocks | `Array<NodeSchema>` | Yes | Array of nodes within the loop body |
+
+#### Usage Example
+
+```json
+{
+  "id": "loop_0",
+  "type": "loop",
+  "data": {
+    "title": "Loop Node",
+    "batchFor": {
+      "type": "ref",
+      "content": ["start_0", "items"]
+    }
+  },
+  "blocks": [
+    {
+      "id": "llm_1",
+      "type": "llm",
+      "data": {
+        "inputsValues": {
+          "prompt": {
+            "type": "ref",
+            "content": ["loop_0_locals", "item"]
+          }
+        }
+      }
+    }
+  ]
+}
+```
+
+In this example, the loop node iterates over the items output of the Start node (assuming it's an array), calling an LLM node for each element. Within the loop body, the current iteration element can be referenced via `loop_0_locals.item`.
+
+## How to Add Custom Nodes
+
+FlowGram Runtime is designed to be extensible, allowing developers to add custom node types. Below are the steps to implement and register custom nodes.
+
+### Steps to Implement the INodeExecutor Interface
+
+1. **Create a Node Executor Class**:
+
+```typescript
+import { ExecutionContext, ExecutionResult, INodeExecutor } from '@flowgram.ai/runtime-interface';
+
+export class CustomNodeExecutor implements INodeExecutor {
+  // Define node type
+  public type = 'custom';
+
+  // Implement execute method
+  public async execute(context: ExecutionContext): Promise<ExecutionResult> {
+    // 1. Get inputs from context
+    const inputs = context.inputs as CustomNodeInputs;
+
+    // 2. Validate inputs
+    if (!inputs.requiredParam) {
+      throw new Error('Required parameter missing');
+    }
+
+    // 3. Execute node logic
+    const result = await this.processCustomLogic(inputs);
+
+    // 4. Return outputs
+    return {
+      outputs: {
+        result: result
+      }
+    };
+  }
+
+  // Custom processing logic
+  private async processCustomLogic(inputs: CustomNodeInputs): Promise<string> {
+    // Implement custom logic
+    return `Processing result: ${inputs.requiredParam}`;
+  }
+}
+
+// Define input interface
+interface CustomNodeInputs {
+  requiredParam: string;
+  optionalParam?: number;
+}
+```
+
+2. **Handle Exception Situations**:
+
+```typescript
+public async execute(context: ExecutionContext): Promise<ExecutionResult> {
+  try {
+    const inputs = context.inputs as CustomNodeInputs;
+
+    // Validate inputs
+    if (!inputs.requiredParam) {
+      throw new Error('Required parameter missing');
+    }
+
+    // Execute node logic
+    const result = await this.processCustomLogic(inputs);
+
+    return {
+      outputs: {
+        result: result
+      }
+    };
+  } catch (error) {
+    // Handle exceptions
+    console.error('Node execution failed:', error);
+    throw error; // Or return specific error output
+  }
+}
+```
+
+### Method to Register Custom Nodes
+
+Add the custom node executor to the node executor registry of FlowGram Runtime:
+
+```typescript
+import { WorkflowRuntimeNodeExecutors } from './nodes';
+import { CustomNodeExecutor } from './nodes/custom';
+
+// Register custom node executor
+WorkflowRuntimeNodeExecutors.push(new CustomNodeExecutor());
+```
+
+### Best Practices for Custom Node Development
+
+1. **Clear Node Responsibility**:
+   - Each node should have a clear single responsibility
+   - Avoid implementing multiple unrelated functionalities in one node
+
+2. **Input Validation**:
+   - Validate all required inputs before executing node logic
+   - Provide clear error messages for debugging
+
+3. **Exception Handling**:
+   - Catch and handle possible exception situations
+   - Avoid letting unhandled exceptions cause the entire workflow to crash
+
+4. **Performance Considerations**:
+   - Consider implementing timeout mechanisms for time-consuming operations
+   - Avoid long-time synchronous operations that block the main thread
+
+5. **Testability**:
+   - Consider the convenience of unit testing when designing nodes
+   - Separate core logic from external dependencies for easier mock testing
+
+6. **Documentation and Comments**:
+   - Provide detailed documentation for custom nodes
+   - Add necessary comments in the code, especially for complex logic parts
+
+### Complete Custom Node Example
+
+Below is a complete example of a custom HTTP request node, used to send HTTP requests and handle responses:
+
+```typescript
+import { ExecutionContext, ExecutionResult, INodeExecutor } from '@flowgram.ai/runtime-interface';
+import axios from 'axios';
+
+// Define HTTP node input interface
+interface HTTPNodeInputs {
+  url: string;
+  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
+  headers?: Record<string, string>;
+  body?: any;
+  timeout?: number;
+}
+
+// Define HTTP node output interface
+interface HTTPNodeOutputs {
+  status: number;
+  data: any;
+  headers: Record<string, string>;
+}
+
+export class HTTPNodeExecutor implements INodeExecutor {
+  // Define node type
+  public type = 'http';
+
+  // Implement execute method
+  public async execute(context: ExecutionContext): Promise<ExecutionResult> {
+    // 1. Get inputs from context
+    const inputs = context.inputs as HTTPNodeInputs;
+
+    // 2. Validate inputs
+    if (!inputs.url) {
+      throw new Error('URL parameter missing');
+    }
+
+    if (!inputs.method) {
+      throw new Error('Request method parameter missing');
+    }
+
+    // 3. Execute HTTP request
+    try {
+      const response = await axios({
+        url: inputs.url,
+        method: inputs.method,
+        headers: inputs.headers || {},
+        data: inputs.body,
+        timeout: inputs.timeout || 30000
+      });
+
+      // 4. Process response
+      const outputs: HTTPNodeOutputs = {
+        status: response.status,
+        data: response.data,
+        headers: response.headers as Record<string, string>
+      };
+
+      // 5. Return outputs
+      return {
+        outputs
+      };
+    } catch (error) {
+      if (axios.isAxiosError(error)) {
+        // Handle Axios errors
+        if (error.response) {
+          // Server returned an error status code
+          return {
+            outputs: {
+              status: error.response.status,
+              data: error.response.data,
+              headers: error.response.headers as Record<string, string>
+            }
+          };
+        } else if (error.request) {
+          // Request was sent but no response was received
+          throw new Error(`Request timeout or no response: ${error.message}`);
+        } else {
+          // Request configuration error
+          throw new Error(`Request configuration error: ${error.message}`);
+        }
+      } else {
+        // Handle non-Axios errors
+        throw error;
+      }
+    }
+  }
+}
+
+// Register HTTP node executor
+import { WorkflowRuntimeNodeExecutors } from './nodes';
+WorkflowRuntimeNodeExecutors.push(new HTTPNodeExecutor());
+```
+
+Usage example:
+
+```json
+{
+  "id": "http_0",
+  "type": "http",
+  "data": {
+    "title": "HTTP Request Node",
+    "inputsValues": {
+      "url": {
+        "type": "constant",
+        "content": "https://api.example.com/data"
+      },
+      "method": {
+        "type": "constant",
+        "content": "GET"
+      },
+      "headers": {
+        "type": "constant",
+        "content": {
+          "Authorization": "Bearer token123"
+        }
+      }
+    },
+    "inputs": {
+      "type": "object",
+      "required": ["url", "method"],
+      "properties": {
+        "url": { "type": "string" },
+        "method": { "type": "string", "enum": ["GET", "POST", "PUT", "DELETE"] },
+        "headers": { "type": "object" },
+        "body": { "type": "object" },
+        "timeout": { "type": "number" }
+      }
+    },
+    "outputs": {
+      "type": "object",
+      "properties": {
+        "status": { "type": "number" },
+        "data": { "type": "object" },
+        "headers": { "type": "object" }
+      }
+    }
+  }
+}
+```
+
+Through the above steps and examples, you can develop and register custom nodes according to your own needs, extending the functionality of FlowGram Runtime.

+ 138 - 0
apps/docs/src/en/guide/runtime/schema.mdx

@@ -0,0 +1,138 @@
+---
+title: Schema
+description: Detailed introduction to FlowGram Schema structure and configuration
+---
+
+# Workflow Schema
+
+This document provides a detailed introduction to the Workflow Schema structure. The Workflow Schema is the core configuration that defines workflows, describing nodes, edges, and their relationships.
+
+## Basic Structure
+
+A complete Workflow Schema contains the following main components:
+
+```typescript
+interface WorkflowSchema {
+  nodes: WorkflowNodeSchema[];
+  edges: WorkflowEdgeSchema[];
+}
+```
+
+## Node Schema
+
+Nodes are the basic units in a workflow, each with its specific type and configuration:
+
+```typescript
+interface WorkflowNodeSchema<T = string, D = any> {
+  id: string;           // Unique identifier for the node
+  type: T;              // Node type
+  meta: WorkflowNodeMetaSchema;  // Node metadata
+  data: D & {          // Node data
+    title?: string;    // Node title
+    inputsValues?: Record<string, IFlowValue>;  // Input values
+    inputs?: IJsonSchema;   // Input schema definition
+    outputs?: IJsonSchema;  // Output schema definition
+    [key: string]: any;     // Other custom data
+  };
+  blocks?: WorkflowNodeSchema[];  // Child nodes (for composite nodes)
+  edges?: WorkflowEdgeSchema[];   // Connections between child nodes
+}
+```
+
+### Node Metadata
+
+```typescript
+interface WorkflowNodeMetaSchema {
+  position: PositionSchema;        // Node position in canvas
+}
+```
+
+## Edge Schema
+
+Edges define the connections between nodes:
+
+```typescript
+interface WorkflowEdgeSchema {
+  sourceNodeID: string;    // Source node ID
+  targetNodeID: string;    // Target node ID
+  sourcePortID?: string;   // Source port ID (optional)
+  targetPortID?: string;   // Target port ID (optional)
+}
+```
+
+## Variable Types
+
+Workflow supports various variable types:
+
+```typescript
+enum WorkflowVariableType {
+  String = 'string',    // String
+  Integer = 'integer',  // Integer
+  Number = 'number',    // Number
+  Boolean = 'boolean',  // Boolean
+  Object = 'object',    // Object
+  Array = 'array',      // Array
+  Null = 'null'         // Null
+}
+```
+
+### Flow Values
+
+In node input values, the following types are supported:
+
+```typescript
+type IFlowValue =
+  | IFlowConstantValue     // Constant value
+  | IFlowRefValue          // Reference value
+  | IFlowExpressionValue   // Expression value
+  | IFlowTemplateValue;    // Template value
+```
+
+Detailed definition for each type:
+
+```typescript
+interface IFlowConstantValue {
+  type: 'constant';
+  content?: string | number | boolean;
+}
+
+interface IFlowRefValue {
+  type: 'ref';
+  content?: string[];
+}
+
+interface IFlowExpressionValue {
+  type: 'expression';
+  content?: string;
+}
+
+interface IFlowTemplateValue {
+  type: 'template';
+  content?: string;
+}
+```
+
+## JSON Schema
+
+Node input and output definitions use JSON Schema format:
+
+```typescript
+interface IJsonSchema<T = string> {
+  type: T;                 // Data type
+  default?: any;           // Default value
+  title?: string;          // Title
+  description?: string;    // Description
+  enum?: (string | number)[];  // Enumeration values
+  properties?: Record<string, IJsonSchema<T>>;  // Object properties
+  additionalProperties?: IJsonSchema<T>;        // Additional properties
+  items?: IJsonSchema<T>;                       // Array item definition
+  required?: string[];                          // Required fields
+  $ref?: string;                                // Reference
+  extra?: {                                     // Extra configuration
+    index?: number;                             // Index
+    weak?: boolean;                             // Weak type comparison
+    formComponent?: string;                     // Form component
+    [key: string]: any;
+  };
+}
+```

+ 0 - 106
apps/docs/src/en/guide/runtime/source-code-guide.mdx

@@ -524,109 +524,3 @@ export class Container {
   }
 }
 ```
-
-## Adding New Node Types (WIP)
-
-To add a new node type, you need to implement the `INodeExecutor` interface and register it with the node executor factory.
-
-Steps:
-
-1. Create a new node executor file in the `js-core/src/nodes/` directory
-2. Implement the `INodeExecutor` interface
-3. Register the new node executor in `js-core/src/nodes/index.ts`
-
-Example: Adding a data transformation node
-
-```typescript
-// js-core/src/nodes/transform.ts
-import { ExecutionContext, ExecutionResult, INodeExecutor } from '@flowgram.ai/runtime-interface';
-
-export interface TransformExecutorInputs {
-  data: any;
-  template: string;
-}
-
-export class TransformExecutor implements INodeExecutor {
-  public type = 'transform';
-
-  public async execute(context: ExecutionContext): Promise<ExecutionResult> {
-    const inputs = context.inputs as TransformExecutorInputs;
-
-    // Transform data using template
-    const result = this.transform(inputs.data, inputs.template);
-
-    return {
-      outputs: {
-        result
-      }
-    };
-  }
-
-  private transform(data: any, template: string): any {
-    // Implement data transformation logic
-    // This is just a simple example
-    return eval(`(data) => { return ${template} }`)(data);
-  }
-}
-
-// js-core/src/nodes/index.ts
-import { TransformExecutor } from './transform';
-
-// Add the new node executor to the array
-WorkflowRuntimeNodeExecutors.push(new TransformExecutor());
-```
-
-## Unit Testing
-
-FlowGram Runtime uses Vitest for unit testing.
-
-Running tests:
-
-```bash
-cd package/runtime/js-core
-npm run test
-```
-
-Writing tests:
-
-```typescript
-// engine.test.ts
-import { WorkflowRuntimeEngine } from '../src/domain/engine';
-import { EngineServices } from '@flowgram.ai/runtime-interface';
-
-describe('WorkflowRuntimeEngine', () => {
-  let engine: WorkflowRuntimeEngine;
-
-  beforeEach(() => {
-    // Create test services
-    const services: EngineServices = {
-      // Use mock objects
-    };
-
-    engine = new WorkflowRuntimeEngine(services);
-  });
-
-  test('should execute workflow successfully', async () => {
-    // Prepare test data
-    const schema = {
-      nodes: [
-        // Test nodes
-      ],
-      edges: [
-        // Test edges
-      ]
-    };
-
-    const inputs = {
-      prompt: 'Test prompt'
-    };
-
-    // Execute workflow
-    const result = await engine.run({ schema, inputs });
-
-    // Verify results
-    expect(result.status).toBe('success');
-    expect(result.outputs).toBeDefined();
-  });
-});
-```

+ 4 - 2
apps/docs/src/zh/guide/runtime/_meta.json

@@ -1,6 +1,8 @@
 [
   "introduction",
   "quick-start",
-  "source-code-guide",
-  "api"
+  "schema",
+  "node",
+  "api",
+  "source-code-guide"
 ]

+ 1 - 1
apps/docs/src/zh/guide/runtime/api.mdx

@@ -1,5 +1,5 @@
 ---
-title: API
+title: 运行时 API
 description: FlowGram Runtime API
 sidebar_position: 2
 ---

+ 1 - 1
apps/docs/src/zh/guide/runtime/introduction.mdx

@@ -128,7 +128,7 @@ FlowGram Runtime 的未来发展计划包括:
 ### 功能增强
 
 - 支持固定布局
-- 增加更多节点类型:代码节点、意图识别节点、批处理节点、终止循环节点、继续循环节点
+- 增加更多节点类型:代码节点、意图识别节点、批处理节点、终止循环节点、继续循环节点、HTTP节点
 - 完善错误处理和异常恢复机制
 - 完善的服务端校验接口,包括 schema 校验和输入校验等
 - 支持 `Docker` 运行

+ 600 - 0
apps/docs/src/zh/guide/runtime/node.mdx

@@ -0,0 +1,600 @@
+# 内置节点
+
+本文档详细介绍FlowGram Runtime中的节点系统,包括节点的基本概念、现有节点类型及其用法,以及如何创建自定义节点。
+
+现有节点:
+
+- 开始节点
+- 结束节点
+- LLM节点
+- 条件节点
+- 循环节点
+
+> 后续会支持代码节点、意图识别节点、批处理节点、终止循环节点、继续循环节点、HTTP节点
+
+## 节点概述
+
+### 节点在FlowGram Runtime中的作用
+
+节点是FlowGram工作流的基本执行单元,每个节点代表一个特定的操作或功能。FlowGram工作流本质上是由多个节点通过边连接形成的有向图,描述了任务的执行流程。节点系统的核心职责包括:
+
+1. **执行特定操作**:每种类型的节点都有其特定的功能,如启动工作流、调用LLM模型、执行条件判断等
+2. **处理输入输出**:节点接收输入数据,执行操作后产生输出数据
+3. **控制执行流程**:通过条件节点和循环节点控制工作流的执行路径
+
+### INodeExecutor接口介绍
+
+所有节点执行器都必须实现`INodeExecutor`接口,该接口定义了节点执行器的基本结构:
+
+```typescript
+interface INodeExecutor {
+  // 节点类型,用于标识不同种类的节点
+  type: string;
+
+  // 执行方法,处理节点的具体逻辑
+  execute(context: ExecutionContext): Promise<ExecutionResult>;
+}
+```
+
+其中:
+- `type`:节点类型标识符,如'start'、'end'、'llm'等
+- `execute`:节点执行方法,接收执行上下文,返回执行结果
+
+### 节点执行流程
+
+节点的执行流程如下:
+
+1. **准备阶段**:
+   - 从执行上下文中获取节点的输入数据
+   - 验证输入数据是否符合要求
+
+2. **执行阶段**:
+   - 执行节点特定的业务逻辑
+   - 处理可能出现的异常情况
+
+3. **完成阶段**:
+   - 生成节点的输出数据
+   - 更新节点状态
+   - 返回执行结果
+
+工作流引擎会根据节点间的连接关系,按顺序调度节点的执行。对于特殊节点(如条件节点和循环节点),引擎会根据节点的执行结果决定下一步的执行路径。
+
+## 现有节点详细介绍
+
+FlowGram Runtime目前实现了五种类型的节点:Start、End、LLM、Condition和Loop。下面将详细介绍每种节点的功能、配置和使用示例。
+
+### 开始节点 `Start`
+
+#### 功能
+
+Start节点是工作流的起始节点,用于接收工作流的输入数据并开始工作流的执行。每个工作流必须有且只有一个Start节点。
+
+#### 配置选项
+
+| 选项 | 类型 | 必填 | 描述 |
+|------|------|------|------|
+| outputs | JSONSchema | 是 | 定义工作流的输入数据结构 |
+
+#### 使用示例
+
+```json
+{
+  "id": "start_0",
+  "type": "start",
+  "data": {
+    "title": "开始节点",
+    "outputs": {
+      "type": "object",
+      "properties": {
+        "prompt": {
+          "type": "string",
+          "description": "用户输入的提示词"
+        }
+      },
+      "required": ["prompt"]
+    }
+  }
+}
+```
+
+在这个例子中,Start节点定义了工作流需要一个名为`prompt`的字符串类型输入。
+
+### 结束节点 `End`
+
+#### 功能
+
+End节点是工作流的结束节点,用于收集工作流的输出数据并结束工作流的执行。每个工作流必须有至少一个End节点。
+
+#### 配置选项
+
+| 选项 | 类型 | 必填 | 描述 |
+|------|------|------|------|
+| inputs | JSONSchema | 是 | 定义工作流的输出数据结构 |
+| inputsValues | `Record<string, ValueSchema>` | 是 | 定义工作流的输出数据值,可以是引用或常量 |
+
+#### 使用示例
+
+```json
+{
+  "id": "end_0",
+  "type": "end",
+  "data": {
+    "title": "结束节点",
+    "inputs": {
+      "type": "object",
+      "properties": {
+        "result": {
+          "type": "string",
+          "description": "工作流的输出结果"
+        }
+      }
+    },
+    "inputsValues": {
+      "result": {
+        "type": "ref",
+        "content": ["llm_0", "result"]
+      }
+    }
+  }
+}
+```
+
+在这个例子中,End节点定义了工作流的输出包含一个名为`result`的字符串,其值引用自ID为`llm_0`的节点的`result`输出。
+
+### LLM节点 `LLM`
+
+#### 功能
+
+LLM节点用于调用大型语言模型执行自然语言处理任务,是FlowGram工作流中最常用的节点类型之一。
+
+#### 配置选项
+
+| 选项 | 类型 | 必填 | 描述 |
+|------|------|------|------|
+| modelName | string | 是 | 模型名称,如"gpt-3.5-turbo" |
+| apiKey | string | 是 | API密钥 |
+| apiHost | string | 是 | API主机地址 |
+| temperature | number | 是 | 温度参数,控制输出的随机性 |
+| systemPrompt | string | 否 | 系统提示词,设置AI助手的角色和行为 |
+| prompt | string | 是 | 用户提示词,即向AI提出的问题或请求 |
+
+#### 使用示例
+
+```json
+{
+  "id": "llm_0",
+  "type": "llm",
+  "data": {
+    "title": "LLM节点",
+    "inputsValues": {
+      "modelName": {
+        "type": "constant",
+        "content": "gpt-3.5-turbo"
+      },
+      "apiKey": {
+        "type": "constant",
+        "content": "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+      },
+      "apiHost": {
+        "type": "constant",
+        "content": "https://api.openai.com/v1"
+      },
+      "temperature": {
+        "type": "constant",
+        "content": 0.7
+      },
+      "systemPrompt": {
+        "type": "constant",
+        "content": "你是一个有帮助的助手。"
+      },
+      "prompt": {
+        "type": "ref",
+        "content": ["start_0", "prompt"]
+      }
+    },
+    "inputs": {
+      "type": "object",
+      "required": ["modelName", "apiKey", "apiHost", "temperature", "prompt"],
+      "properties": {
+        "modelName": { "type": "string" },
+        "apiKey": { "type": "string" },
+        "apiHost": { "type": "string" },
+        "temperature": { "type": "number" },
+        "systemPrompt": { "type": "string" },
+        "prompt": { "type": "string" }
+      }
+    },
+    "outputs": {
+      "type": "object",
+      "properties": {
+        "result": { "type": "string" }
+      }
+    }
+  }
+}
+```
+
+在这个例子中,LLM节点使用gpt-3.5-turbo模型,温度参数为0.7,系统提示词设置为"你是一个有帮助的助手",用户提示词引用自Start节点的输入。
+
+### 条件节点 `Condition`
+
+#### 功能
+
+Condition节点用于根据条件选择不同的执行分支,实现工作流的条件逻辑。
+
+#### 配置选项
+
+| 选项 | 类型 | 必填 | 描述 |
+|------|------|------|------|
+| conditions | Array | 是 | 条件数组,每个条件包含key和value |
+
+条件value的结构:
+
+| 选项 | 类型 | 必填 | 描述 |
+|------|------|------|------|
+| left | ValueSchema | 是 | 左值,可以是引用或常量 |
+| operator | string | 是 | 操作符,如"eq"、"gt"等 |
+| right | ValueSchema | 是 | 右值,可以是引用或常量 |
+
+支持的操作符:
+
+| 操作符 | 描述 | 适用类型 |
+|--------|------|----------|
+| eq | 等于 | 所有类型 |
+| neq | 不等于 | 所有类型 |
+| gt | 大于 | 数字、字符串 |
+| gte | 大于等于 | 数字、字符串 |
+| lt | 小于 | 数字、字符串 |
+| lte | 小于等于 | 数字、字符串 |
+| includes | 包含 | 字符串、数组 |
+| startsWith | 以...开始 | 字符串 |
+| endsWith | 以...结束 | 字符串 |
+
+#### 使用示例
+
+```json
+{
+  "id": "condition_0",
+  "type": "condition",
+  "data": {
+    "title": "条件节点",
+    "conditions": [
+      {
+        "key": "if_true",
+        "value": {
+          "left": {
+            "type": "ref",
+            "content": ["start_0", "value"]
+          },
+          "operator": "gt",
+          "right": {
+            "type": "constant",
+            "content": 10
+          }
+        }
+      },
+      {
+        "key": "if_false",
+        "value": {
+          "left": {
+            "type": "ref",
+            "content": ["start_0", "value"]
+          },
+          "operator": "lte",
+          "right": {
+            "type": "constant",
+            "content": 10
+          }
+        }
+      }
+    ]
+  }
+}
+```
+
+在这个例子中,条件节点定义了两个分支:当Start节点的value输出大于10时走"if_true"分支,否则走"if_false"分支。
+
+### 循环节点 `Loop`
+
+#### 功能
+
+Loop节点用于对数组中的每个元素执行相同的操作,实现工作流的循环逻辑。
+
+#### 配置选项
+
+| 选项 | 类型 | 必填 | 描述 |
+|------|------|------|------|
+| batchFor | ValueSchema | 是 | 要迭代的数组,通常是一个引用 |
+| blocks | `Array<NodeSchema>` | 是 | 循环体内的节点数组 |
+
+#### 使用示例
+
+```json
+{
+  "id": "loop_0",
+  "type": "loop",
+  "data": {
+    "title": "循环节点",
+    "batchFor": {
+      "type": "ref",
+      "content": ["start_0", "items"]
+    }
+  },
+  "blocks": [
+    {
+      "id": "llm_1",
+      "type": "llm",
+      "data": {
+        "inputsValues": {
+          "prompt": {
+            "type": "ref",
+            "content": ["loop_0_locals", "item"]
+          }
+        }
+      }
+    }
+  ]
+}
+```
+
+在这个例子中,循环节点对Start节点的items输出(假设是一个数组)进行迭代,对每个元素调用一个LLM节点。在循环体内,可以通过`loop_0_locals.item`引用当前迭代的元素。
+
+## 如何新增自定义节点
+
+FlowGram Runtime设计为可扩展的,允许开发者添加自定义节点类型。以下是实现和注册自定义节点的步骤。
+
+### 实现INodeExecutor接口的步骤
+
+1. **创建节点执行器类**:
+
+```typescript
+import { ExecutionContext, ExecutionResult, INodeExecutor } from '@flowgram.ai/runtime-interface';
+
+export class CustomNodeExecutor implements INodeExecutor {
+  // 定义节点类型
+  public type = 'custom';
+
+  // 实现execute方法
+  public async execute(context: ExecutionContext): Promise<ExecutionResult> {
+    // 1. 从上下文中获取输入
+    const inputs = context.inputs as CustomNodeInputs;
+
+    // 2. 验证输入
+    if (!inputs.requiredParam) {
+      throw new Error('必需参数缺失');
+    }
+
+    // 3. 执行节点逻辑
+    const result = await this.processCustomLogic(inputs);
+
+    // 4. 返回输出
+    return {
+      outputs: {
+        result: result
+      }
+    };
+  }
+
+  // 自定义处理逻辑
+  private async processCustomLogic(inputs: CustomNodeInputs): Promise<string> {
+    // 实现自定义逻辑
+    return `处理结果: ${inputs.requiredParam}`;
+  }
+}
+
+// 定义输入接口
+interface CustomNodeInputs {
+  requiredParam: string;
+  optionalParam?: number;
+}
+```
+
+2. **处理异常情况**:
+
+```typescript
+public async execute(context: ExecutionContext): Promise<ExecutionResult> {
+  try {
+    const inputs = context.inputs as CustomNodeInputs;
+
+    // 验证输入
+    if (!inputs.requiredParam) {
+      throw new Error('必需参数缺失');
+    }
+
+    // 执行节点逻辑
+    const result = await this.processCustomLogic(inputs);
+
+    return {
+      outputs: {
+        result: result
+      }
+    };
+  } catch (error) {
+    // 处理异常
+    console.error('节点执行失败:', error);
+    throw error; // 或者返回特定的错误输出
+  }
+}
+```
+
+### 注册自定义节点的方法
+
+将自定义节点执行器添加到FlowGram Runtime的节点执行器注册表中:
+
+```typescript
+import { WorkflowRuntimeNodeExecutors } from './nodes';
+import { CustomNodeExecutor } from './nodes/custom';
+
+// 注册自定义节点执行器
+WorkflowRuntimeNodeExecutors.push(new CustomNodeExecutor());
+```
+
+### 自定义节点开发最佳实践
+
+1. **明确节点职责**:
+   - 每个节点应该有明确的单一职责
+   - 避免在一个节点中实现多个不相关的功能
+
+2. **输入验证**:
+   - 在执行节点逻辑前验证所有必需的输入
+   - 提供清晰的错误信息,便于调试
+
+3. **异常处理**:
+   - 捕获并处理可能的异常情况
+   - 避免让未处理的异常导致整个工作流崩溃
+
+4. **性能考虑**:
+   - 对于耗时操作,考虑实现超时机制
+   - 避免阻塞主线程的长时间同步操作
+
+5. **可测试性**:
+   - 设计节点时考虑单元测试的便利性
+   - 将核心逻辑与外部依赖分离,便于模拟测试
+
+6. **文档和注释**:
+   - 为自定义节点提供详细的文档
+   - 在代码中添加必要的注释,特别是复杂逻辑部分
+
+### 自定义节点示例
+
+下面是一个完整的自定义HTTP请求节点示例,用于发送HTTP请求并处理响应:
+
+```typescript
+import { ExecutionContext, ExecutionResult, INodeExecutor } from '@flowgram.ai/runtime-interface';
+import axios from 'axios';
+
+// 定义HTTP节点的输入接口
+interface HTTPNodeInputs {
+  url: string;
+  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
+  headers?: Record<string, string>;
+  body?: any;
+  timeout?: number;
+}
+
+// 定义HTTP节点的输出接口
+interface HTTPNodeOutputs {
+  status: number;
+  data: any;
+  headers: Record<string, string>;
+}
+
+export class HTTPNodeExecutor implements INodeExecutor {
+  // 定义节点类型
+  public type = 'http';
+
+  // 实现execute方法
+  public async execute(context: ExecutionContext): Promise<ExecutionResult> {
+    // 1. 从上下文中获取输入
+    const inputs = context.inputs as HTTPNodeInputs;
+
+    // 2. 验证输入
+    if (!inputs.url) {
+      throw new Error('URL参数缺失');
+    }
+
+    if (!inputs.method) {
+      throw new Error('请求方法参数缺失');
+    }
+
+    // 3. 执行HTTP请求
+    try {
+      const response = await axios({
+        url: inputs.url,
+        method: inputs.method,
+        headers: inputs.headers || {},
+        data: inputs.body,
+        timeout: inputs.timeout || 30000
+      });
+
+      // 4. 处理响应
+      const outputs: HTTPNodeOutputs = {
+        status: response.status,
+        data: response.data,
+        headers: response.headers as Record<string, string>
+      };
+
+      // 5. 返回输出
+      return {
+        outputs
+      };
+    } catch (error) {
+      if (axios.isAxiosError(error)) {
+        // 处理Axios错误
+        if (error.response) {
+          // 服务器返回了错误状态码
+          return {
+            outputs: {
+              status: error.response.status,
+              data: error.response.data,
+              headers: error.response.headers as Record<string, string>
+            }
+          };
+        } else if (error.request) {
+          // 请求已发送但未收到响应
+          throw new Error(`请求超时或无响应: ${error.message}`);
+        } else {
+          // 请求配置有误
+          throw new Error(`请求配置错误: ${error.message}`);
+        }
+      } else {
+        // 处理非Axios错误
+        throw error;
+      }
+    }
+  }
+}
+
+// 注册HTTP节点执行器
+import { WorkflowRuntimeNodeExecutors } from './nodes';
+WorkflowRuntimeNodeExecutors.push(new HTTPNodeExecutor());
+```
+
+使用示例:
+
+```json
+{
+  "id": "http_0",
+  "type": "http",
+  "data": {
+    "title": "HTTP请求节点",
+    "inputsValues": {
+      "url": {
+        "type": "constant",
+        "content": "https://api.example.com/data"
+      },
+      "method": {
+        "type": "constant",
+        "content": "GET"
+      },
+      "headers": {
+        "type": "constant",
+        "content": {
+          "Authorization": "Bearer token123"
+        }
+      }
+    },
+    "inputs": {
+      "type": "object",
+      "required": ["url", "method"],
+      "properties": {
+        "url": { "type": "string" },
+        "method": { "type": "string", "enum": ["GET", "POST", "PUT", "DELETE"] },
+        "headers": { "type": "object" },
+        "body": { "type": "object" },
+        "timeout": { "type": "number" }
+      }
+    },
+    "outputs": {
+      "type": "object",
+      "properties": {
+        "status": { "type": "number" },
+        "data": { "type": "object" },
+        "headers": { "type": "object" }
+      }
+    }
+  }
+}
+```
+
+通过以上步骤和示例,您可以根据自己的需求开发和注册自定义节点,扩展FlowGram Runtime的功能。

+ 138 - 0
apps/docs/src/zh/guide/runtime/schema.mdx

@@ -0,0 +1,138 @@
+---
+title: 认识 Schema
+description: FlowGram Schema 结构和配置的详细介绍
+---
+
+# Workflow Schema
+
+本文档详细介绍了 Workflow 的 Schema 结构。Workflow Schema 是定义工作流程的核心配置,它描述了节点、边以及它们之间的关系。
+
+## 基本结构
+
+一个完整的 Workflow Schema 包含以下主要部分:
+
+```typescript
+interface WorkflowSchema {
+  nodes: WorkflowNodeSchema[];
+  edges: WorkflowEdgeSchema[];
+}
+```
+
+## 节点 Schema
+
+节点是工作流中的基本单元,每个节点都有其特定的类型和配置:
+
+```typescript
+interface WorkflowNodeSchema<T = string, D = any> {
+  id: string;           // 节点唯一标识符
+  type: T;              // 节点类型
+  meta: WorkflowNodeMetaSchema;  // 节点元数据
+  data: D & {          // 节点数据
+    title?: string;    // 节点标题
+    inputsValues?: Record<string, IFlowValue>;  // 输入值
+    inputs?: IJsonSchema;   // 输入模式定义
+    outputs?: IJsonSchema;  // 输出模式定义
+    [key: string]: any;     // 其他自定义数据
+  };
+  blocks?: WorkflowNodeSchema[];  // 子节点(用于复合节点)
+  edges?: WorkflowEdgeSchema[];   // 子节点之间的连接
+}
+```
+
+### 节点元数据
+
+```typescript
+interface WorkflowNodeMetaSchema {
+  position: PositionSchema;        // 节点在画布中的位置
+}
+```
+
+## 边 Schema
+
+边定义了节点之间的连接关系:
+
+```typescript
+interface WorkflowEdgeSchema {
+  sourceNodeID: string;    // 源节点ID
+  targetNodeID: string;    // 目标节点ID
+  sourcePortID?: string;   // 源节点端口ID(可选)
+  targetPortID?: string;   // 目标节点端口ID(可选)
+}
+```
+
+## 值类型
+
+Workflow 支持多种值类型:
+
+```typescript
+enum WorkflowVariableType {
+  String = 'string',    // 字符串
+  Integer = 'integer',  // 整数
+  Number = 'number',    // 数字
+  Boolean = 'boolean',  // 布尔值
+  Object = 'object',    // 对象
+  Array = 'array',      // 数组
+  Null = 'null'         // 空值
+}
+```
+
+### 流程值
+
+在节点的输入值中,支持以下几种类型:
+
+```typescript
+type IFlowValue =
+  | IFlowConstantValue     // 常量值
+  | IFlowRefValue          // 引用值
+  | IFlowExpressionValue   // 表达式值
+  | IFlowTemplateValue;    // 模板值
+```
+
+每种类型的具体定义:
+
+```typescript
+interface IFlowConstantValue {
+  type: 'constant';
+  content?: string | number | boolean;
+}
+
+interface IFlowRefValue {
+  type: 'ref';
+  content?: string[];
+}
+
+interface IFlowExpressionValue {
+  type: 'expression';
+  content?: string;
+}
+
+interface IFlowTemplateValue {
+  type: 'template';
+  content?: string;
+}
+```
+
+## JSON Schema
+
+节点的输入输出定义使用 JSON Schema 格式:
+
+```typescript
+interface IJsonSchema<T = string> {
+  type: T;                 // 数据类型
+  default?: any;           // 默认值
+  title?: string;          // 标题
+  description?: string;    // 描述
+  enum?: (string | number)[];  // 枚举值
+  properties?: Record<string, IJsonSchema<T>>;  // 对象属性
+  additionalProperties?: IJsonSchema<T>;        // 额外属性
+  items?: IJsonSchema<T>;                       // 数组项定义
+  required?: string[];                          // 必需字段
+  $ref?: string;                                // 引用
+  extra?: {                                     // 额外配置
+    index?: number;                             // 索引
+    weak?: boolean;                             // 弱类型比较
+    formComponent?: string;                     // 表单组件
+    [key: string]: any;
+  };
+}
+```

+ 0 - 106
apps/docs/src/zh/guide/runtime/source-code-guide.mdx

@@ -524,109 +524,3 @@ export class Container {
   }
 }
 ```
-
-## 添加新的节点类型 (WIP)
-
-要添加新的节点类型,需要实现 `INodeExecutor` 接口并注册到节点执行器工厂。
-
-步骤:
-
-1. 在 `js-core/src/nodes/` 目录下创建新的节点执行器文件
-2. 实现 `INodeExecutor` 接口
-3. 在 `js-core/src/nodes/index.ts` 中注册新的节点执行器
-
-示例:添加一个数据转换节点
-
-```typescript
-// js-core/src/nodes/transform.ts
-import { ExecutionContext, ExecutionResult, INodeExecutor } from '@flowgram.ai/runtime-interface';
-
-export interface TransformExecutorInputs {
-  data: any;
-  template: string;
-}
-
-export class TransformExecutor implements INodeExecutor {
-  public type = 'transform';
-
-  public async execute(context: ExecutionContext): Promise<ExecutionResult> {
-    const inputs = context.inputs as TransformExecutorInputs;
-
-    // 使用模板转换数据
-    const result = this.transform(inputs.data, inputs.template);
-
-    return {
-      outputs: {
-        result
-      }
-    };
-  }
-
-  private transform(data: any, template: string): any {
-    // 实现数据转换逻辑
-    // 这里只是一个简单的示例
-    return eval(`(data) => { return ${template} }`)(data);
-  }
-}
-
-// js-core/src/nodes/index.ts
-import { TransformExecutor } from './transform';
-
-// 将新的节点执行器添加到数组中
-WorkflowRuntimeNodeExecutors.push(new TransformExecutor());
-```
-
-## 单元测试
-
-FlowGram Runtime 使用 Vitest 进行单元测试。
-
-运行测试:
-
-```bash
-cd package/runtime/js-core
-npm run test
-```
-
-编写测试:
-
-```typescript
-// engine.test.ts
-import { WorkflowRuntimeEngine } from '../src/domain/engine';
-import { EngineServices } from '@flowgram.ai/runtime-interface';
-
-describe('WorkflowRuntimeEngine', () => {
-  let engine: WorkflowRuntimeEngine;
-
-  beforeEach(() => {
-    // 创建测试服务
-    const services: EngineServices = {
-      // 使用 mock 对象
-    };
-
-    engine = new WorkflowRuntimeEngine(services);
-  });
-
-  test('should execute workflow successfully', async () => {
-    // 准备测试数据
-    const schema = {
-      nodes: [
-        // 测试节点
-      ],
-      edges: [
-        // 测试边
-      ]
-    };
-
-    const inputs = {
-      prompt: 'Test prompt'
-    };
-
-    // 执行工作流
-    const result = await engine.run({ schema, inputs });
-
-    // 验证结果
-    expect(result.status).toBe('success');
-    expect(result.outputs).toBeDefined();
-  });
-});
-```