diff --git a/samples/interaction.ts b/samples/interaction.ts index 8026f6114..5d9c70583 100644 --- a/samples/interaction.ts +++ b/samples/interaction.ts @@ -5,19 +5,20 @@ import { graphDataTestRunner } from "~/utils/runner"; import { interactiveInputTextAgent } from "./agents/interactiveInputAgent"; const graph_data = { + version: 0.3, loop: { count: 3, }, nodes: { node1: { value: {}, - update: "node3", + update: ":node3", }, node2: { agent: "input", }, node3: { - inputs: ["node1", "node2"], + inputs: [":node1", ":node2"], agent: "merge", }, }, diff --git a/src/graphai.ts b/src/graphai.ts index 1bd1e0a8e..539e98744 100644 --- a/src/graphai.ts +++ b/src/graphai.ts @@ -13,7 +13,7 @@ type GraphNodes = Record; const defaultConcurrency = 8; export class GraphAI { - private readonly version: number; + public readonly version: number; private readonly graphId: string; private readonly data: GraphData; private readonly loop?: LoopData; @@ -62,7 +62,7 @@ export class GraphAI { } private getValueFromResults(key: string, results: ResultDataDictonary) { - const source = parseNodeName(key); + const source = parseNodeName(key, this.version); return getDataFromSource(source.nodeId ? results[source.nodeId] : undefined, source); } @@ -96,6 +96,9 @@ export class GraphAI { console.log("------------ no version"); } this.version = data.version ?? 0.2; + if (this.version < 0.3) { + console.log("------------ upgrade to 0.3!"); + } this.retryLimit = data.retry; // optional this.graphId = URL.createObjectURL(new Blob()).slice(-36); this.data = data; diff --git a/src/node.ts b/src/node.ts index 96b89b309..8fdcb2b99 100644 --- a/src/node.ts +++ b/src/node.ts @@ -92,21 +92,17 @@ export class ComputedNode extends Node { this.priority = data.priority ?? 0; this.anyInput = data.anyInput ?? false; - this.dataSources = (data.inputs ?? []).map((input) => parseNodeName(input)); + this.dataSources = (data.inputs ?? []).map((input) => parseNodeName(input, graph.version)); this.pendings = new Set(this.dataSources.filter((source) => source.nodeId).map((source) => source.nodeId!)); if (data.if) { - this.ifSource = parseNodeName(data.if); + this.ifSource = parseNodeName(data.if, graph.version); assert(!!this.ifSource.nodeId, `Invalid data source ${data.if}`); this.pendings.add(this.ifSource.nodeId); } - const regex = /^\$\{([^{}]+)\}$/; this.dynamicParams = Object.keys(this.params).reduce((tmp: Record, key) => { - const value = this.params[key]; - const match = typeof value === "string" ? value.match(regex) : null; - if (match) { - const dataSource = parseNodeName(match[1]); + const dataSource = parseNodeName(this.params[key], graph.version < 0.3 ? 0.3 : graph.version); + if (dataSource.nodeId) { tmp[key] = dataSource; - assert(!!dataSource.nodeId, `Invalid data source ${key}:${value}`); this.pendings.add(dataSource.nodeId); } return tmp; diff --git a/src/type.ts b/src/type.ts index 932438a37..718037c3c 100644 --- a/src/type.ts +++ b/src/type.ts @@ -31,7 +31,7 @@ export type StaticNodeData = { update?: string; // nodeId (+.propId) to get value after a loop isResult?: boolean; }; -export type AgentNamelessFunction = (...param: any[]) => unknown +export type AgentNamelessFunction = (...param: any[]) => unknown; export type ComputedNodeData = { agent: string | AgentNamelessFunction; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 3056eb106..3306154c5 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -4,7 +4,7 @@ export const sleep = async (milliseconds: number) => { return await new Promise((resolve) => setTimeout(resolve, milliseconds)); }; -export const parseNodeName = (inputNodeId: any): DataSource => { +const parseNodeName_02 = (inputNodeId: any) => { if (typeof inputNodeId === "string") { const regex = /^"(.*)"$/; const match = inputNodeId.match(regex); @@ -20,6 +20,25 @@ export const parseNodeName = (inputNodeId: any): DataSource => { return { value: inputNodeId }; // non-string literal }; +export const parseNodeName = (inputNodeId: any, version: number): DataSource => { + if (version === 0.2) { + return parseNodeName_02(inputNodeId); + } + if (typeof inputNodeId === "string") { + const regex = /^:(.*)$/; + const match = inputNodeId.match(regex); + if (!match) { + return { value: inputNodeId }; // string literal + } + const parts = match[1].split("."); + if (parts.length == 1) { + return { nodeId: parts[0] }; + } + return { nodeId: parts[0], propIds: parts.slice(1) }; + } + return { value: inputNodeId }; // non-string literal +}; + export function assert(condition: boolean, message: string, isWarn: boolean = false): asserts condition { if (!condition) { if (!isWarn) { diff --git a/src/validators/relation_validator.ts b/src/validators/relation_validator.ts index 267095dd5..5530e3ff7 100644 --- a/src/validators/relation_validator.ts +++ b/src/validators/relation_validator.ts @@ -13,7 +13,7 @@ export const relationValidator = (data: GraphData, staticNodeIds: string[], comp pendings[computedNodeId] = new Set(); if ("inputs" in nodeData && nodeData && nodeData.inputs) { nodeData.inputs.forEach((inputNodeId) => { - const sourceNodeId = parseNodeName(inputNodeId).nodeId; + const sourceNodeId = parseNodeName(inputNodeId, data.version ?? 0.02).nodeId; if (sourceNodeId) { if (!nodeIds.has(sourceNodeId)) { throw new Error(`Inputs not match: NodeId ${computedNodeId}, Inputs: ${sourceNodeId}`); @@ -31,7 +31,7 @@ export const relationValidator = (data: GraphData, staticNodeIds: string[], comp const nodeData = data.nodes[staticNodeId]; if ("value" in nodeData && nodeData.update) { const update = nodeData.update; - const updateNodeId = parseNodeName(update).nodeId; + const updateNodeId = parseNodeName(update, data.version ?? 0.02).nodeId; if (!updateNodeId) { throw new Error("Update it a literal"); } diff --git a/tests/graphai/test_params.ts b/tests/graphai/test_params.ts index f5c28bbc1..4e0245ddd 100644 --- a/tests/graphai/test_params.ts +++ b/tests/graphai/test_params.ts @@ -10,7 +10,7 @@ const testAgent: AgentFunction, any> = async ({ params }) = }; const graphData_literal = { - version: 0.2, + version: 0.3, nodes: { source1: { value: { apple: "red" }, @@ -20,28 +20,28 @@ const graphData_literal = { }, delayed1: { agent: "sleeperAgent", - inputs: ["source1"], + inputs: [":source1"], }, delayed2: { agent: "sleeperAgent", params: { duration: 100, }, - inputs: ["source2"], + inputs: [":source2"], }, test1: { agent: "testAgent", params: { - fruit: "${source1}", - color: "${source2.lemon}", + fruit: ":source1", + color: ":source2.lemon", }, isResult: true, }, test2: { agent: "testAgent", params: { - fruit: "${delayed1}", - color: "${delayed2.lemon}", + fruit: ":delayed1", + color: ":delayed2.lemon", }, isResult: true, }, diff --git a/tests/graphs/test_base.yml b/tests/graphs/test_base.yml index bcdb16bd6..51b5c1b05 100644 --- a/tests/graphs/test_base.yml +++ b/tests/graphs/test_base.yml @@ -1,4 +1,4 @@ -version: 0.2 +version: 0.3 nodes: node1: agent: sleeperAgent @@ -18,18 +18,18 @@ nodes: duration: 500 value: node3: output - inputs: [node1, node2] + inputs: [:node1, :node2] node4: agent: sleeperAgent params: duration: 100 value: node4: output - inputs: [node3] + inputs: [:node3] node5: agent: sleeperAgent params: duration: 500 value: node5: output - inputs: [node2, node4] + inputs: [:node2, :node4] diff --git a/tests/units/test_utils.ts b/tests/units/test_utils.ts index d519263bd..56bcc1bcc 100644 --- a/tests/units/test_utils.ts +++ b/tests/units/test_utils.ts @@ -8,7 +8,7 @@ test("test getDataFromSource", async () => { const result = { data: "123" }; const data = { data: "123" }; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); }); @@ -18,7 +18,7 @@ test("test getDataFromSource parseId", async () => { const result = { data: "123" }; const data = "123"; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); }); @@ -28,7 +28,7 @@ test("test getDataFromSource array", async () => { const result = ["123"]; const data = ["123"]; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); }); @@ -38,7 +38,7 @@ test("test getDataFromSource array $0", async () => { const result = ["000", "111"]; const data = "000"; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); }); @@ -48,7 +48,7 @@ test("test getDataFromSource array $1", async () => { const result = ["000", "111"]; const data = "111"; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); }); @@ -60,7 +60,7 @@ test("test getDataFromSource nested object", async () => { const result = { data: { sample: "123" } }; const data = "123"; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); }); @@ -70,7 +70,7 @@ test("test getDataFromSource nested array", async () => { const result = { data: { sample: [0, 1, 2, 3] } }; const data = 2; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); }); @@ -80,7 +80,7 @@ test("test getDataFromSource nested array last", async () => { const result = { data: { sample: [0, 1, 2, 3] } }; const data = 3; - const source = parseNodeName(inputId); + const source = parseNodeName(inputId, 0.2); const res = getDataFromSource(result, source); assert.deepStrictEqual(res, data); });