mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
Modified tools directory to be more generic, updated serializer and executor accordingly
This commit is contained in:
@@ -10,14 +10,14 @@ describe('Serializer', () => {
|
||||
});
|
||||
|
||||
describe('serializeWorkflow', () => {
|
||||
it('should serialize a workflow with model and http blocks', () => {
|
||||
it('should serialize a workflow with agent and http blocks', () => {
|
||||
const blocks: Node[] = [
|
||||
{
|
||||
id: 'model-1',
|
||||
id: 'agent-1',
|
||||
type: 'custom',
|
||||
position: { x: 100, y: 100 },
|
||||
data: {
|
||||
tool: 'model',
|
||||
tool: 'openai.chat',
|
||||
params: {
|
||||
model: 'gpt-4o',
|
||||
systemPrompt: 'You are helpful',
|
||||
@@ -44,7 +44,7 @@ describe('Serializer', () => {
|
||||
type: 'custom',
|
||||
position: { x: 400, y: 100 },
|
||||
data: {
|
||||
tool: 'http',
|
||||
tool: 'http.request',
|
||||
params: {
|
||||
url: 'https://api.example.com',
|
||||
method: 'GET'
|
||||
@@ -70,7 +70,7 @@ describe('Serializer', () => {
|
||||
const connections: Edge[] = [
|
||||
{
|
||||
id: 'conn-1',
|
||||
source: 'model-1',
|
||||
source: 'agent-1',
|
||||
target: 'http-1',
|
||||
sourceHandle: 'response',
|
||||
targetHandle: 'body'
|
||||
@@ -84,16 +84,16 @@ describe('Serializer', () => {
|
||||
expect(serialized.blocks).toHaveLength(2);
|
||||
expect(serialized.connections).toHaveLength(1);
|
||||
|
||||
// Test model block serialization
|
||||
const modelBlock = serialized.blocks.find(b => b.id === 'model-1');
|
||||
expect(modelBlock).toBeDefined();
|
||||
expect(modelBlock?.config.tool).toBe('model');
|
||||
expect(modelBlock?.config.params).toEqual({
|
||||
// Test agent block serialization
|
||||
const agentBlock = serialized.blocks.find(b => b.id === 'agent-1');
|
||||
expect(agentBlock).toBeDefined();
|
||||
expect(agentBlock?.config.tool).toBe('openai.chat');
|
||||
expect(agentBlock?.config.params).toEqual({
|
||||
model: 'gpt-4o',
|
||||
systemPrompt: 'You are helpful',
|
||||
temperature: 0.7
|
||||
});
|
||||
expect(modelBlock?.metadata).toEqual({
|
||||
expect(agentBlock?.metadata).toEqual({
|
||||
title: 'GPT-4o Agent',
|
||||
description: 'Language model block',
|
||||
category: 'AI',
|
||||
@@ -104,20 +104,11 @@ describe('Serializer', () => {
|
||||
// Test http block serialization
|
||||
const httpBlock = serialized.blocks.find(b => b.id === 'http-1');
|
||||
expect(httpBlock).toBeDefined();
|
||||
expect(httpBlock?.config.tool).toBe('http');
|
||||
expect(httpBlock?.config.tool).toBe('http.request');
|
||||
expect(httpBlock?.config.params).toEqual({
|
||||
url: 'https://api.example.com',
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
// Test connection serialization
|
||||
const connection = serialized.connections[0];
|
||||
expect(connection).toEqual({
|
||||
source: 'model-1',
|
||||
target: 'http-1',
|
||||
sourceHandle: 'response',
|
||||
targetHandle: 'body'
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle blocks with minimal required configuration', () => {
|
||||
@@ -126,7 +117,7 @@ describe('Serializer', () => {
|
||||
type: 'custom',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
tool: 'model',
|
||||
tool: 'openai.chat',
|
||||
params: {
|
||||
model: 'gpt-4o'
|
||||
},
|
||||
@@ -141,7 +132,7 @@ describe('Serializer', () => {
|
||||
const block = serialized.blocks[0];
|
||||
|
||||
expect(block.id).toBe('minimal-1');
|
||||
expect(block.config.tool).toBe('model');
|
||||
expect(block.config.tool).toBe('openai.chat');
|
||||
expect(block.config.params).toEqual({ model: 'gpt-4o' });
|
||||
expect(block.metadata).toBeUndefined();
|
||||
});
|
||||
@@ -153,7 +144,7 @@ describe('Serializer', () => {
|
||||
type: 'custom',
|
||||
position: { x: 100, y: 100 },
|
||||
data: {
|
||||
tool: 'http',
|
||||
tool: 'http.request',
|
||||
params: {
|
||||
url: 'https://api.data.com',
|
||||
method: 'GET'
|
||||
@@ -171,7 +162,7 @@ describe('Serializer', () => {
|
||||
type: 'custom',
|
||||
position: { x: 300, y: 100 },
|
||||
data: {
|
||||
tool: 'model',
|
||||
tool: 'openai.chat',
|
||||
params: {
|
||||
model: 'gpt-4o',
|
||||
systemPrompt: 'Process this data'
|
||||
@@ -192,7 +183,7 @@ describe('Serializer', () => {
|
||||
type: 'custom',
|
||||
position: { x: 500, y: 100 },
|
||||
data: {
|
||||
tool: 'http',
|
||||
tool: 'http.request',
|
||||
params: {
|
||||
url: 'https://api.output.com',
|
||||
method: 'POST'
|
||||
@@ -248,19 +239,18 @@ describe('Serializer', () => {
|
||||
|
||||
it('should preserve tool-specific parameters', () => {
|
||||
const blocks: Node[] = [{
|
||||
id: 'model-1',
|
||||
id: 'agent-1',
|
||||
type: 'custom',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
tool: 'model',
|
||||
tool: 'openai.chat',
|
||||
params: {
|
||||
model: 'gpt-4o',
|
||||
temperature: 0.7,
|
||||
maxTokens: 1000,
|
||||
topP: 0.9,
|
||||
frequencyPenalty: 0.5,
|
||||
presencePenalty: 0.5,
|
||||
stop: ['###']
|
||||
frequencyPenalty: 0.1,
|
||||
presencePenalty: 0.1
|
||||
},
|
||||
interface: {
|
||||
inputs: { prompt: 'string' },
|
||||
@@ -271,29 +261,28 @@ describe('Serializer', () => {
|
||||
|
||||
const serialized = serializer.serializeWorkflow(blocks, []);
|
||||
const block = serialized.blocks[0];
|
||||
|
||||
|
||||
expect(block.config.params).toEqual({
|
||||
model: 'gpt-4o',
|
||||
temperature: 0.7,
|
||||
maxTokens: 1000,
|
||||
topP: 0.9,
|
||||
frequencyPenalty: 0.5,
|
||||
presencePenalty: 0.5,
|
||||
stop: ['###']
|
||||
frequencyPenalty: 0.1,
|
||||
presencePenalty: 0.1
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deserializeWorkflow', () => {
|
||||
it('should deserialize a workflow back to ReactFlow format', () => {
|
||||
const serialized: SerializedWorkflow = {
|
||||
const workflow: SerializedWorkflow = {
|
||||
version: '1.0',
|
||||
blocks: [
|
||||
{
|
||||
id: 'model-1',
|
||||
id: 'agent-1',
|
||||
position: { x: 100, y: 100 },
|
||||
config: {
|
||||
tool: 'model',
|
||||
tool: 'openai.chat',
|
||||
params: {
|
||||
model: 'gpt-4o',
|
||||
systemPrompt: 'You are helpful'
|
||||
@@ -309,159 +298,18 @@ describe('Serializer', () => {
|
||||
}
|
||||
}
|
||||
],
|
||||
connections: [
|
||||
{
|
||||
source: 'model-1',
|
||||
target: 'http-1',
|
||||
sourceHandle: 'response',
|
||||
targetHandle: 'body'
|
||||
}
|
||||
]
|
||||
connections: []
|
||||
};
|
||||
|
||||
const deserialized = serializer.deserializeWorkflow(serialized);
|
||||
const { blocks, connections } = serializer.deserializeWorkflow(workflow);
|
||||
|
||||
// Test blocks deserialization
|
||||
expect(deserialized.blocks).toHaveLength(1);
|
||||
const block = deserialized.blocks[0];
|
||||
expect(block.id).toBe('model-1');
|
||||
expect(blocks).toHaveLength(1);
|
||||
const block = blocks[0];
|
||||
expect(block.id).toBe('agent-1');
|
||||
expect(block.type).toBe('custom');
|
||||
expect(block.position).toEqual({ x: 100, y: 100 });
|
||||
expect(block.data).toEqual({
|
||||
tool: 'model',
|
||||
params: {
|
||||
model: 'gpt-4o',
|
||||
systemPrompt: 'You are helpful'
|
||||
},
|
||||
interface: {
|
||||
inputs: { prompt: 'string' },
|
||||
outputs: { response: 'string' }
|
||||
},
|
||||
title: 'GPT-4o Agent',
|
||||
category: 'AI'
|
||||
});
|
||||
|
||||
// Test connections deserialization
|
||||
expect(deserialized.connections).toHaveLength(1);
|
||||
const connection = deserialized.connections[0];
|
||||
expect(connection).toEqual({
|
||||
id: 'model-1-http-1',
|
||||
source: 'model-1',
|
||||
target: 'http-1',
|
||||
sourceHandle: 'response',
|
||||
targetHandle: 'body'
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty workflow', () => {
|
||||
const serialized: SerializedWorkflow = {
|
||||
version: '1.0',
|
||||
blocks: [],
|
||||
connections: []
|
||||
};
|
||||
|
||||
const deserialized = serializer.deserializeWorkflow(serialized);
|
||||
expect(deserialized.blocks).toHaveLength(0);
|
||||
expect(deserialized.connections).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should handle blocks with complex interface types', () => {
|
||||
const serialized: SerializedWorkflow = {
|
||||
version: '1.0',
|
||||
blocks: [{
|
||||
id: 'complex-1',
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'model',
|
||||
params: {
|
||||
model: 'gpt-4o'
|
||||
},
|
||||
interface: {
|
||||
inputs: {
|
||||
context: 'object',
|
||||
options: 'array',
|
||||
metadata: 'Record<string, any>',
|
||||
callback: 'function'
|
||||
},
|
||||
outputs: {
|
||||
result: 'object',
|
||||
errors: 'array',
|
||||
logs: 'string[]'
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
connections: []
|
||||
};
|
||||
|
||||
const deserialized = serializer.deserializeWorkflow(serialized);
|
||||
const block = deserialized.blocks[0];
|
||||
|
||||
expect(block.data.interface.inputs).toEqual({
|
||||
context: 'object',
|
||||
options: 'array',
|
||||
metadata: 'Record<string, any>',
|
||||
callback: 'function'
|
||||
});
|
||||
expect(block.data.interface.outputs).toEqual({
|
||||
result: 'object',
|
||||
errors: 'array',
|
||||
logs: 'string[]'
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle circular connections', () => {
|
||||
const serialized: SerializedWorkflow = {
|
||||
version: '1.0',
|
||||
blocks: [
|
||||
{
|
||||
id: 'loop-1',
|
||||
position: { x: 0, y: 0 },
|
||||
config: {
|
||||
tool: 'model',
|
||||
params: {},
|
||||
interface: {
|
||||
inputs: { input: 'string' },
|
||||
outputs: { output: 'string' }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'loop-2',
|
||||
position: { x: 200, y: 0 },
|
||||
config: {
|
||||
tool: 'model',
|
||||
params: {},
|
||||
interface: {
|
||||
inputs: { input: 'string' },
|
||||
outputs: { output: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
connections: [
|
||||
{
|
||||
source: 'loop-1',
|
||||
target: 'loop-2',
|
||||
sourceHandle: 'output',
|
||||
targetHandle: 'input'
|
||||
},
|
||||
{
|
||||
source: 'loop-2',
|
||||
target: 'loop-1',
|
||||
sourceHandle: 'output',
|
||||
targetHandle: 'input'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const deserialized = serializer.deserializeWorkflow(serialized);
|
||||
|
||||
expect(deserialized.connections).toHaveLength(2);
|
||||
expect(deserialized.connections[0].source).toBe('loop-1');
|
||||
expect(deserialized.connections[0].target).toBe('loop-2');
|
||||
expect(deserialized.connections[1].source).toBe('loop-2');
|
||||
expect(deserialized.connections[1].target).toBe('loop-1');
|
||||
expect(block.data.tool).toBe('openai.chat');
|
||||
expect(block.data.params.model).toBe('gpt-4o');
|
||||
expect(block.data.title).toBe('GPT-4o Agent');
|
||||
});
|
||||
});
|
||||
});
|
||||
+10
-22
@@ -16,21 +16,19 @@ export interface Position {
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface BlockConfig {
|
||||
tool: string;
|
||||
params: Record<string, any>;
|
||||
interface: {
|
||||
inputs: Record<string, string>;
|
||||
outputs: Record<string, string>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SerializedBlock {
|
||||
id: string;
|
||||
position: Position;
|
||||
config: {
|
||||
// The tool this block uses
|
||||
tool: string;
|
||||
// Tool-specific parameters
|
||||
params: Record<string, any>;
|
||||
// Block's input/output interface
|
||||
interface: {
|
||||
inputs: Record<string, string>;
|
||||
outputs: Record<string, string>;
|
||||
};
|
||||
};
|
||||
// UI-specific metadata (optional)
|
||||
config: BlockConfig;
|
||||
metadata?: {
|
||||
title?: string;
|
||||
description?: string;
|
||||
@@ -39,13 +37,3 @@ export interface SerializedBlock {
|
||||
color?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SubBlockValue {
|
||||
title: string;
|
||||
type: 'long-input' | 'short-input' | 'dropdown' | 'slider' | 'code';
|
||||
value: string | number | boolean;
|
||||
}
|
||||
|
||||
export interface BlockValues {
|
||||
[key: string]: string | number | boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user