agency-lang 0.0.46 → 0.0.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/backends/typescriptGenerator.js +19 -5
- package/dist/lib/templates/backends/graphGenerator/graphNode.d.ts +1 -1
- package/dist/lib/templates/backends/graphGenerator/graphNode.js +6 -12
- package/dist/lib/templates/backends/graphGenerator/imports.d.ts +1 -1
- package/dist/lib/templates/backends/graphGenerator/imports.js +31 -12
- package/dist/lib/templates/backends/typescriptGenerator/functionCallAssignment.d.ts +9 -0
- package/dist/lib/templates/backends/typescriptGenerator/functionCallAssignment.js +18 -0
- package/dist/lib/templates/backends/typescriptGenerator/functionDefinition.d.ts +1 -1
- package/dist/lib/templates/backends/typescriptGenerator/functionDefinition.js +9 -6
- package/dist/lib/templates/backends/typescriptGenerator/internalFunctionCall.d.ts +8 -0
- package/dist/lib/templates/backends/typescriptGenerator/internalFunctionCall.js +9 -0
- package/dist/lib/templates/backends/typescriptGenerator/promptFunction.d.ts +2 -1
- package/dist/lib/templates/backends/typescriptGenerator/promptFunction.js +11 -6
- package/dist/lib/templates/backends/typescriptGenerator/toolCall.d.ts +1 -1
- package/dist/lib/templates/backends/typescriptGenerator/toolCall.js +1 -1
- package/package.json +4 -4
|
@@ -3,6 +3,8 @@ import * as renderSpecialVar from "../templates/backends/graphGenerator/specialV
|
|
|
3
3
|
import * as renderTime from "../templates/backends/typescriptGenerator/builtinFunctions/time.js";
|
|
4
4
|
import * as builtinTools from "../templates/backends/typescriptGenerator/builtinTools.js";
|
|
5
5
|
import * as renderFunctionDefinition from "../templates/backends/typescriptGenerator/functionDefinition.js";
|
|
6
|
+
import * as renderInternalFunctionCall from "../templates/backends/typescriptGenerator/internalFunctionCall.js";
|
|
7
|
+
import * as renderFunctionCallAssignment from "../templates/backends/typescriptGenerator/functionCallAssignment.js";
|
|
6
8
|
import * as renderImports from "../templates/backends/typescriptGenerator/imports.js";
|
|
7
9
|
import * as promptFunction from "../templates/backends/typescriptGenerator/promptFunction.js";
|
|
8
10
|
import * as renderTool from "../templates/backends/typescriptGenerator/tool.js";
|
|
@@ -167,8 +169,12 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
167
169
|
}
|
|
168
170
|
// Direct assignment for other literal types
|
|
169
171
|
const code = this.processNode(value);
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
+
return renderFunctionCallAssignment.default({
|
|
173
|
+
variableName: `${this.getScopeVar()}.${variableName}`,
|
|
174
|
+
typeAnnotation,
|
|
175
|
+
functionCode: code.trim(),
|
|
176
|
+
nodeContext: this.getCurrentScope() === "node",
|
|
177
|
+
});
|
|
172
178
|
}
|
|
173
179
|
else if (value.type === "timeBlock") {
|
|
174
180
|
const timingVarName = variableName;
|
|
@@ -314,7 +320,16 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
314
320
|
let argsString = "";
|
|
315
321
|
if (this.isInternalFunction(node.functionName)) {
|
|
316
322
|
argsString = parts.join(", ");
|
|
317
|
-
|
|
323
|
+
const metadata = `{
|
|
324
|
+
statelogClient,
|
|
325
|
+
graph: __graph,
|
|
326
|
+
messages: __messages,
|
|
327
|
+
}`;
|
|
328
|
+
return renderInternalFunctionCall.default({
|
|
329
|
+
functionName,
|
|
330
|
+
argsString,
|
|
331
|
+
metadata,
|
|
332
|
+
});
|
|
318
333
|
}
|
|
319
334
|
else {
|
|
320
335
|
// must be a builtin function or imported function
|
|
@@ -424,8 +439,6 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
424
439
|
const clientConfig = prompt.config ? this.processNode(prompt.config) : "{}";
|
|
425
440
|
const metadataObj = `{
|
|
426
441
|
messages: __messages,
|
|
427
|
-
interruptResponse: __interruptResponse,
|
|
428
|
-
toolCall: __toolCall,
|
|
429
442
|
}`;
|
|
430
443
|
this.toolsUsed = []; // reset after use
|
|
431
444
|
const scopedFunctionArgs = functionArgs.map((arg) => this.generateScopedVariableName(arg));
|
|
@@ -440,6 +453,7 @@ export class TypeScriptGenerator extends BaseGenerator {
|
|
|
440
453
|
tools,
|
|
441
454
|
functionCalls,
|
|
442
455
|
clientConfig,
|
|
456
|
+
nodeContext: this.getCurrentScope() === "node",
|
|
443
457
|
});
|
|
444
458
|
}
|
|
445
459
|
processImportStatement(node) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const template = "\ngraph.node(\"{{{name}}}\", async (state): Promise<any> => {\n const __messages: Message[] = state.messages || [];\n const __graph = state.__metadata?.graph || graph;\n const statelogClient = state.__metadata?.statelogClient || __statelogClient;\n \n // if `state.__metadata?.__stateStack` is set, that means we are resuming execution\n // at this node after an interrupt. In that case, this is the line that restores the state.\n if (state.__metadata?.__stateStack) {\n __stateStack =
|
|
1
|
+
export declare const template = "\ngraph.node(\"{{{name}}}\", async (state): Promise<any> => {\n const __messages: Message[] = state.messages || [];\n const __graph = state.__metadata?.graph || graph;\n const statelogClient = state.__metadata?.statelogClient || __statelogClient;\n \n // if `state.__metadata?.__stateStack` is set, that means we are resuming execution\n // at this node after an interrupt. In that case, this is the line that restores the state.\n if (state.__metadata?.__stateStack) {\n __stateStack = state.__metadata.__stateStack;\n \n // restore global state\n if (state.__metadata?.__stateStack?.global) {\n __global = state.__metadata.__stateStack.global;\n }\n\n // clear the state stack from metadata so it doesn't propagate to other nodes.\n state.__metadata.__stateStack = undefined;\n }\n\n // either creates a new stack for this node,\n // or restores the stack if we're resuming after an interrupt,\n // depending on the mode of the state stack (serialize or deserialize).\n const __stack = __stateStack.getNewState();\n \n // We're going to modify __stack.step to keep track of what line we're on,\n // but first we save this value. This will help us figure out if we should execute\n // from the start of this node or from a specific line.\n const __step = __stack.step;\n\n const __self: Record<string, any> = __stack.locals;\n\n {{#hasParam}}\n \n const __params = {{{paramNames}}};\n \n // Any arguments that were passed into this node,\n // save them onto the stack, unless we are restoring the stack after an interrupt,\n // in which case leave as is\n if (state.data !== \"<from-stack>\") {\n (state.data).forEach((item, index) => {\n __stack.args[__params[index]] = item;\n });\n }\n {{/hasParam}}\n {{{body}}}\n \n // this is just here to have a default return value from a node if the user doesn't specify one\n return { ...state, data: undefined };\n});\n";
|
|
2
2
|
export type TemplateType = {
|
|
3
3
|
name: string | boolean | number;
|
|
4
4
|
hasParam: boolean;
|
|
@@ -11,8 +11,13 @@ graph.node("{{{name}}}", async (state): Promise<any> => {
|
|
|
11
11
|
// if \`state.__metadata?.__stateStack\` is set, that means we are resuming execution
|
|
12
12
|
// at this node after an interrupt. In that case, this is the line that restores the state.
|
|
13
13
|
if (state.__metadata?.__stateStack) {
|
|
14
|
-
__stateStack =
|
|
14
|
+
__stateStack = state.__metadata.__stateStack;
|
|
15
15
|
|
|
16
|
+
// restore global state
|
|
17
|
+
if (state.__metadata?.__stateStack?.global) {
|
|
18
|
+
__global = state.__metadata.__stateStack.global;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
// clear the state stack from metadata so it doesn't propagate to other nodes.
|
|
17
22
|
state.__metadata.__stateStack = undefined;
|
|
18
23
|
}
|
|
@@ -29,17 +34,6 @@ graph.node("{{{name}}}", async (state): Promise<any> => {
|
|
|
29
34
|
|
|
30
35
|
const __self: Record<string, any> = __stack.locals;
|
|
31
36
|
|
|
32
|
-
// If we're resuming after an interrupt, these will be set.
|
|
33
|
-
// There should be a cleaner way to handle this,
|
|
34
|
-
// instead of littering this scope with these variables
|
|
35
|
-
const __interruptResponse: InterruptResponseType | undefined = state.__metadata?.interruptResponse;
|
|
36
|
-
const __toolCall: Record<string, any>|undefined = __stateStack.other?.toolCall;
|
|
37
|
-
|
|
38
|
-
// TODO pretty sure this isn't needed, check and remove
|
|
39
|
-
if (state.__metadata?.state?.global) {
|
|
40
|
-
__global = state.__metadata.state.global;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
37
|
{{#hasParam}}
|
|
44
38
|
|
|
45
39
|
const __params = {{{paramNames}}};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const template = "import { z } from \"zod\";\nimport * as readline from \"readline\";\nimport fs from \"fs\";\nimport { PieMachine, goToNode } from \"piemachine\";\nimport { StatelogClient } from \"statelog-client\";\nimport { nanoid } from \"nanoid\";\nimport { assistantMessage, getClient, userMessage, toolMessage, messageFromJSON } from \"smoltalk\";\nimport type { Message } from \"smoltalk\";\n\nconst statelogHost = \"https://statelog.adit.io\";\nconst traceId = nanoid();\nconst statelogConfig = {\n host: statelogHost,\n traceId: traceId,\n apiKey: process.env.STATELOG_API_KEY || \"\",\n projectId: \"agency-lang\",\n debugMode: false,\n };\nconst __statelogClient = new StatelogClient(statelogConfig);\nconst __model: ModelName = \"gpt-4o-mini\";\n\nconst getClientWithConfig = (config = {}) => {\n const defaultConfig = {\n openAiApiKey: process.env.OPENAI_API_KEY || \"\",\n googleApiKey: process.env.GEMINI_API_KEY || \"\",\n model: __model,\n logLevel: \"warn\",\n };\n\n return getClient({ ...defaultConfig, ...config });\n};\n\nlet __client = getClientWithConfig();\n\ntype State = {\n messages: string[];\n data: any;\n}\n\n// enable debug logging\nconst graphConfig = {\n debug: {\n log: true,\n logData: false,\n },\n statelog: statelogConfig,\n};\n\nconst graph = new PieMachine<State>(graphConfig);\n\n// builtins\n\nconst not = (val: any): boolean => !val;\nconst eq = (a: any, b: any): boolean => a === b;\nconst neq = (a: any, b: any): boolean => a !== b;\nconst lt = (a: any, b: any): boolean => a < b;\nconst lte = (a: any, b: any): boolean => a <= b;\nconst gt = (a: any, b: any): boolean => a > b;\nconst gte = (a: any, b: any): boolean => a >= b;\nconst and = (a: any, b: any): boolean => a && b;\nconst or = (a: any, b: any): boolean => a || b;\nconst head = <T>(arr: T[]): T | undefined => arr[0];\nconst tail = <T>(arr: T[]): T[] => arr.slice(1);\nconst empty = <T>(arr: T[]): boolean => arr.length === 0;\n\n// interrupts\n\nexport type Interrupt<T> = {\n type: \"interrupt\";\n data: T;\n\n // JSONified StateStack, i.e. serialized execution state\n __state?: Record<string, any>;\n};\n\nexport function interrupt<T>(data: T): Interrupt<T> {\n return {\n type: \"interrupt\",\n data,\n };\n}\n\nexport function isInterrupt<T>(obj: any): obj is Interrupt<T> {\n return obj && obj.type === \"interrupt\";\n}\n\nfunction printJSON(obj: any) {\n console.log(JSON.stringify(obj, null, 2));\n}\n\nexport type InterruptResponseType = InterruptResponseApprove | InterruptResponseReject
|
|
1
|
+
export declare const template = "import { z } from \"zod\";\nimport * as readline from \"readline\";\nimport fs from \"fs\";\nimport { PieMachine, goToNode } from \"piemachine\";\nimport { StatelogClient } from \"statelog-client\";\nimport { nanoid } from \"nanoid\";\nimport { assistantMessage, getClient, userMessage, toolMessage, messageFromJSON } from \"smoltalk\";\nimport type { Message } from \"smoltalk\";\n\nconst statelogHost = \"https://statelog.adit.io\";\nconst traceId = nanoid();\nconst statelogConfig = {\n host: statelogHost,\n traceId: traceId,\n apiKey: process.env.STATELOG_API_KEY || \"\",\n projectId: \"agency-lang\",\n debugMode: false,\n };\nconst __statelogClient = new StatelogClient(statelogConfig);\nconst __model: ModelName = \"gpt-4o-mini\";\n\nconst getClientWithConfig = (config = {}) => {\n const defaultConfig = {\n openAiApiKey: process.env.OPENAI_API_KEY || \"\",\n googleApiKey: process.env.GEMINI_API_KEY || \"\",\n model: __model,\n logLevel: \"warn\",\n };\n\n return getClient({ ...defaultConfig, ...config });\n};\n\nlet __client = getClientWithConfig();\n\ntype State = {\n messages: string[];\n data: any;\n}\n\n// enable debug logging\nconst graphConfig = {\n debug: {\n log: true,\n logData: false,\n },\n statelog: statelogConfig,\n};\n\nconst graph = new PieMachine<State>(graphConfig);\n\n// builtins\n\nconst not = (val: any): boolean => !val;\nconst eq = (a: any, b: any): boolean => a === b;\nconst neq = (a: any, b: any): boolean => a !== b;\nconst lt = (a: any, b: any): boolean => a < b;\nconst lte = (a: any, b: any): boolean => a <= b;\nconst gt = (a: any, b: any): boolean => a > b;\nconst gte = (a: any, b: any): boolean => a >= b;\nconst and = (a: any, b: any): boolean => a && b;\nconst or = (a: any, b: any): boolean => a || b;\nconst head = <T>(arr: T[]): T | undefined => arr[0];\nconst tail = <T>(arr: T[]): T[] => arr.slice(1);\nconst empty = <T>(arr: T[]): boolean => arr.length === 0;\n\n// interrupts\n\nexport type Interrupt<T> = {\n type: \"interrupt\";\n data: T;\n\n // JSONified StateStack, i.e. serialized execution state\n __state?: Record<string, any>;\n};\n\nexport function interrupt<T>(data: T): Interrupt<T> {\n return {\n type: \"interrupt\",\n data,\n };\n}\n\nexport function isInterrupt<T>(obj: any): obj is Interrupt<T> {\n return obj && obj.type === \"interrupt\";\n}\n\nfunction printJSON(obj: any) {\n console.log(JSON.stringify(obj, null, 2));\n}\n\nexport type InterruptResponseType = InterruptResponseApprove | InterruptResponseReject;\n\nexport type InterruptResponseApprove = {\n type: \"approve\";\n newArguments?: Record<string, any>;\n};\nexport type InterruptResponseReject = {\n type: \"reject\";\n};\n\nexport async function respondToInterrupt(_interrupt: Interrupt, _interruptResponse: InterruptResponseType) {\n const interrupt = structuredClone(_interrupt);\n const interruptResponse = structuredClone(_interruptResponse);\n\n __stateStack = StateStack.fromJSON(interrupt.__state || {});\n __stateStack.deserializeMode();\n \n const messages = (__stateStack.interruptData.messages || []).map((json: any) => {\n // create message objects from JSON\n return messageFromJSON(json);\n });\n __stateStack.interruptData.messages = messages;\n __stateStack.interruptData.interruptResponse = interruptResponse;\n\n if (interruptResponse.type === \"approve\" && interruptResponse.newArguments) {\n __stateStack.interruptData.toolCall = {\n ...__stateStack.interruptData.toolCall,\n arguments: { ...__stateStack.interruptData.toolCall.arguments, ...interruptResponse.newArguments },\n };\n // Error:\n // TypeError: Cannot set property arguments of #<ToolCall> which has only a getter\n // toolCall.arguments = { ...toolCall.arguments, ...interruptResponse.newArguments };\n //\n // const lastMessage = __stateStack.interruptData.messages[__stateStack.interruptData.messages.length - 1];\n // if (lastMessage && lastMessage.role === \"assistant\") {\n // const toolCall = lastMessage.toolCalls?.[lastMessage.toolCalls.length - 1];\n // if (toolCall) {\n // toolCall.arguments = { ...toolCall.arguments, ...interruptResponse.newArguments };\n // }\n // }\n }\n\n\n // start at the last node we visited\n const nodesTraversed = __stateStack.interruptData.nodesTraversed || [];\n const nodeName = nodesTraversed[nodesTraversed.length - 1];\n const result = await graph.run(nodeName, {\n messages: messages,\n __metadata: {\n graph: graph,\n statelogClient: __statelogClient,\n __stateStack: __stateStack,\n },\n\n // restore args from the state stack\n data: \"<from-stack>\"\n });\n return result.data;\n}\n\nexport async function approveInterrupt(interrupt: Interrupt, newArguments?: Record<string, any>) {\n return await respondToInterrupt(interrupt, { type: \"approve\", newArguments });\n}\n\nexport async function rejectInterrupt(interrupt: Interrupt) {\n return await respondToInterrupt(interrupt, { type: \"reject\" });\n}\n\ntype StackFrame = {\n args: Record<string, any>;\n locals: Record<string, any>;\n step: number;\n};\n\n// See docs for notes on how this works.\nclass StateStack {\n public stack: StackFrame[] = [];\n private mode: \"serialize\" | \"deserialize\" = \"serialize\";\n public globals: Record<string, any> = {};\n public other: Record<string, any> = {};\n public interruptData: Record<string, any> = {};\n\n private deserializeStackLength = 0;\n\n constructor(stack: StackFrame[] = [], mode: \"serialize\" | \"deserialize\" = \"serialize\") {\n this.stack = stack;\n this.mode = mode;\n }\n\n getNewState(): StackFrame | null {\n if (this.mode === \"deserialize\" && this.deserializeStackLength <= 0) {\n console.log(\"Forcing mode to serialize, nothing left to deserialize\");\n this.mode = \"serialize\";\n }\n if (this.mode === \"serialize\") {\n const newState: StackFrame = {\n args: {},\n locals: {},\n step: 0,\n };\n this.stack.push(newState);\n return newState;\n } else if (this.mode === \"deserialize\") {\n this.deserializeStackLength -= 1;\n const item = this.stack.shift();\n this.stack.push(item);\n return item;\n }\n return null;\n }\n\n deserializeMode() {\n this.mode = \"deserialize\";\n this.deserializeStackLength = this.stack.length;\n }\n\n pop(): StackFrame | undefined {\n return this.stack.pop();\n }\n\n toJSON() {\n return structuredClone({\n stack: this.stack,\n globals: this.globals,\n other: this.other,\n interruptData: this.interruptData,\n mode: this.mode,\n deserializeStackLength: this.deserializeStackLength,\n });\n }\n\n static fromJSON(json: any): StateStack {\n const stateStack = new StateStack([], \"serialize\");\n stateStack.stack = json.stack || [];\n stateStack.globals = json.globals || {};\n stateStack.other = json.other || {};\n stateStack.interruptData = json.interruptData || {};\n stateStack.mode = json.mode || \"serialize\";\n stateStack.deserializeStackLength = json.deserializeStackLength || 0;\n return stateStack;\n }\n}\n\nlet __stateStack = new StateStack();";
|
|
2
2
|
export type TemplateType = {};
|
|
3
3
|
declare const render: (args: TemplateType) => string;
|
|
4
4
|
export default render;
|
|
@@ -92,18 +92,15 @@ function printJSON(obj: any) {
|
|
|
92
92
|
console.log(JSON.stringify(obj, null, 2));
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
export type InterruptResponseType = InterruptResponseApprove | InterruptResponseReject
|
|
95
|
+
export type InterruptResponseType = InterruptResponseApprove | InterruptResponseReject;
|
|
96
|
+
|
|
96
97
|
export type InterruptResponseApprove = {
|
|
97
98
|
type: "approve";
|
|
99
|
+
newArguments?: Record<string, any>;
|
|
98
100
|
};
|
|
99
101
|
export type InterruptResponseReject = {
|
|
100
102
|
type: "reject";
|
|
101
103
|
};
|
|
102
|
-
export type InterruptResponseModify = {
|
|
103
|
-
type: "modify";
|
|
104
|
-
newArguments: Record<string, any>;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
104
|
|
|
108
105
|
export async function respondToInterrupt(_interrupt: Interrupt, _interruptResponse: InterruptResponseType) {
|
|
109
106
|
const interrupt = structuredClone(_interrupt);
|
|
@@ -112,21 +109,40 @@ export async function respondToInterrupt(_interrupt: Interrupt, _interruptRespon
|
|
|
112
109
|
__stateStack = StateStack.fromJSON(interrupt.__state || {});
|
|
113
110
|
__stateStack.deserializeMode();
|
|
114
111
|
|
|
115
|
-
const messages = (__stateStack.
|
|
112
|
+
const messages = (__stateStack.interruptData.messages || []).map((json: any) => {
|
|
116
113
|
// create message objects from JSON
|
|
117
114
|
return messageFromJSON(json);
|
|
118
115
|
});
|
|
116
|
+
__stateStack.interruptData.messages = messages;
|
|
117
|
+
__stateStack.interruptData.interruptResponse = interruptResponse;
|
|
118
|
+
|
|
119
|
+
if (interruptResponse.type === "approve" && interruptResponse.newArguments) {
|
|
120
|
+
__stateStack.interruptData.toolCall = {
|
|
121
|
+
...__stateStack.interruptData.toolCall,
|
|
122
|
+
arguments: { ...__stateStack.interruptData.toolCall.arguments, ...interruptResponse.newArguments },
|
|
123
|
+
};
|
|
124
|
+
// Error:
|
|
125
|
+
// TypeError: Cannot set property arguments of #<ToolCall> which has only a getter
|
|
126
|
+
// toolCall.arguments = { ...toolCall.arguments, ...interruptResponse.newArguments };
|
|
127
|
+
//
|
|
128
|
+
// const lastMessage = __stateStack.interruptData.messages[__stateStack.interruptData.messages.length - 1];
|
|
129
|
+
// if (lastMessage && lastMessage.role === "assistant") {
|
|
130
|
+
// const toolCall = lastMessage.toolCalls?.[lastMessage.toolCalls.length - 1];
|
|
131
|
+
// if (toolCall) {
|
|
132
|
+
// toolCall.arguments = { ...toolCall.arguments, ...interruptResponse.newArguments };
|
|
133
|
+
// }
|
|
134
|
+
// }
|
|
135
|
+
}
|
|
136
|
+
|
|
119
137
|
|
|
120
138
|
// start at the last node we visited
|
|
121
|
-
const nodesTraversed = __stateStack.
|
|
139
|
+
const nodesTraversed = __stateStack.interruptData.nodesTraversed || [];
|
|
122
140
|
const nodeName = nodesTraversed[nodesTraversed.length - 1];
|
|
123
141
|
const result = await graph.run(nodeName, {
|
|
124
142
|
messages: messages,
|
|
125
143
|
__metadata: {
|
|
126
144
|
graph: graph,
|
|
127
145
|
statelogClient: __statelogClient,
|
|
128
|
-
interruptResponse: interruptResponse,
|
|
129
|
-
state: interrupt.__state,
|
|
130
146
|
__stateStack: __stateStack,
|
|
131
147
|
},
|
|
132
148
|
|
|
@@ -136,8 +152,8 @@ export async function respondToInterrupt(_interrupt: Interrupt, _interruptRespon
|
|
|
136
152
|
return result.data;
|
|
137
153
|
}
|
|
138
154
|
|
|
139
|
-
export async function approveInterrupt(interrupt: Interrupt) {
|
|
140
|
-
return await respondToInterrupt(interrupt, { type: "approve" });
|
|
155
|
+
export async function approveInterrupt(interrupt: Interrupt, newArguments?: Record<string, any>) {
|
|
156
|
+
return await respondToInterrupt(interrupt, { type: "approve", newArguments });
|
|
141
157
|
}
|
|
142
158
|
|
|
143
159
|
export async function rejectInterrupt(interrupt: Interrupt) {
|
|
@@ -156,6 +172,7 @@ class StateStack {
|
|
|
156
172
|
private mode: "serialize" | "deserialize" = "serialize";
|
|
157
173
|
public globals: Record<string, any> = {};
|
|
158
174
|
public other: Record<string, any> = {};
|
|
175
|
+
public interruptData: Record<string, any> = {};
|
|
159
176
|
|
|
160
177
|
private deserializeStackLength = 0;
|
|
161
178
|
|
|
@@ -200,6 +217,7 @@ class StateStack {
|
|
|
200
217
|
stack: this.stack,
|
|
201
218
|
globals: this.globals,
|
|
202
219
|
other: this.other,
|
|
220
|
+
interruptData: this.interruptData,
|
|
203
221
|
mode: this.mode,
|
|
204
222
|
deserializeStackLength: this.deserializeStackLength,
|
|
205
223
|
});
|
|
@@ -210,6 +228,7 @@ class StateStack {
|
|
|
210
228
|
stateStack.stack = json.stack || [];
|
|
211
229
|
stateStack.globals = json.globals || {};
|
|
212
230
|
stateStack.other = json.other || {};
|
|
231
|
+
stateStack.interruptData = json.interruptData || {};
|
|
213
232
|
stateStack.mode = json.mode || "serialize";
|
|
214
233
|
stateStack.deserializeStackLength = json.deserializeStackLength || 0;
|
|
215
234
|
return stateStack;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const template = "{{{variableName:string}}}{{{typeAnnotation:string}}} = await {{{functionCode:string}}};\n\nif (isInterrupt({{{variableName:string}}})) {\n {{#nodeContext}}\n return { ...state, data: {{{variableName:string}}} };\n {{/nodeContext}}\n {{^nodeContext}}\n return { data: {{{variableName:string}}} };\n {{/nodeContext}}\n}";
|
|
2
|
+
export type TemplateType = {
|
|
3
|
+
variableName: string;
|
|
4
|
+
typeAnnotation: string;
|
|
5
|
+
functionCode: string;
|
|
6
|
+
nodeContext: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare const render: (args: TemplateType) => string;
|
|
9
|
+
export default render;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// THIS FILE WAS AUTO-GENERATED
|
|
2
|
+
// Source: lib/templates/backends/typescriptGenerator/functionCallAssignment.mustache
|
|
3
|
+
// Any manual changes will be lost.
|
|
4
|
+
import { apply } from "typestache";
|
|
5
|
+
export const template = `{{{variableName:string}}}{{{typeAnnotation:string}}} = await {{{functionCode:string}}};
|
|
6
|
+
|
|
7
|
+
if (isInterrupt({{{variableName:string}}})) {
|
|
8
|
+
{{#nodeContext}}
|
|
9
|
+
return { ...state, data: {{{variableName:string}}} };
|
|
10
|
+
{{/nodeContext}}
|
|
11
|
+
{{^nodeContext}}
|
|
12
|
+
return { data: {{{variableName:string}}} };
|
|
13
|
+
{{/nodeContext}}
|
|
14
|
+
}`;
|
|
15
|
+
const render = (args) => {
|
|
16
|
+
return apply(template, args);
|
|
17
|
+
};
|
|
18
|
+
export default render;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const template = "\nexport async function {{{functionName:string}}}(args, __metadata={}) : Promise<{{{returnType}}}> {\n const __messages: Message[] = [];\n const __stack = __stateStack.getNewState();\n const __step = __stack.step;\n const __self: Record<string, any> = __stack.locals;\n
|
|
1
|
+
export declare const template = "\nexport async function {{{functionName:string}}}(args, __metadata={}) : Promise<{{{returnType}}}> {\n const __messages: Message[] = __metadata?.messages || [];\n const __stack = __stateStack.getNewState();\n const __step = __stack.step;\n const __self: Record<string, any> = __stack.locals;\n const __graph = __metadata?.graph || graph;\n const statelogClient = __metadata?.statelogClient || __statelogClient;\n\n // args are always set whether we're restoring from state or not.\n // If we're not restoring from state, args were obviously passed in through the code.\n // If we are restoring from state, the node that called this function had to have passed \n // these arguments into this function call.\n // if we're restoring state, this will override __stack.args (which will be set),\n // but with the same values, so it doesn't matter that those values are being overwritten.\n const __params = [{{{argsStr}}}];\n (args).forEach((item, index) => {\n __stack.args[__params[index]] = item;\n });\n\n\n {{{functionBody}}}\n}";
|
|
2
2
|
export type TemplateType = {
|
|
3
3
|
functionName: string;
|
|
4
4
|
returnType: string | boolean | number;
|
|
@@ -4,16 +4,19 @@
|
|
|
4
4
|
import { apply } from "typestache";
|
|
5
5
|
export const template = `
|
|
6
6
|
export async function {{{functionName:string}}}(args, __metadata={}) : Promise<{{{returnType}}}> {
|
|
7
|
-
const __messages: Message[] = [];
|
|
7
|
+
const __messages: Message[] = __metadata?.messages || [];
|
|
8
8
|
const __stack = __stateStack.getNewState();
|
|
9
9
|
const __step = __stack.step;
|
|
10
10
|
const __self: Record<string, any> = __stack.locals;
|
|
11
|
+
const __graph = __metadata?.graph || graph;
|
|
12
|
+
const statelogClient = __metadata?.statelogClient || __statelogClient;
|
|
11
13
|
|
|
12
|
-
//
|
|
13
|
-
// from state
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
14
|
+
// args are always set whether we're restoring from state or not.
|
|
15
|
+
// If we're not restoring from state, args were obviously passed in through the code.
|
|
16
|
+
// If we are restoring from state, the node that called this function had to have passed
|
|
17
|
+
// these arguments into this function call.
|
|
18
|
+
// if we're restoring state, this will override __stack.args (which will be set),
|
|
19
|
+
// but with the same values, so it doesn't matter that those values are being overwritten.
|
|
17
20
|
const __params = [{{{argsStr}}}];
|
|
18
21
|
(args).forEach((item, index) => {
|
|
19
22
|
__stack.args[__params[index]] = item;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const template = "{{{functionName:string}}}([{{{argsString:string}}}], {{{metadata:string}}});";
|
|
2
|
+
export type TemplateType = {
|
|
3
|
+
functionName: string;
|
|
4
|
+
argsString: string;
|
|
5
|
+
metadata: string;
|
|
6
|
+
};
|
|
7
|
+
declare const render: (args: TemplateType) => string;
|
|
8
|
+
export default render;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// THIS FILE WAS AUTO-GENERATED
|
|
2
|
+
// Source: lib/templates/backends/typescriptGenerator/internalFunctionCall.mustache
|
|
3
|
+
// Any manual changes will be lost.
|
|
4
|
+
import { apply } from "typestache";
|
|
5
|
+
export const template = `{{{functionName:string}}}([{{{argsString:string}}}], {{{metadata:string}}});`;
|
|
6
|
+
const render = (args) => {
|
|
7
|
+
return apply(template, args);
|
|
8
|
+
};
|
|
9
|
+
export default render;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const template = "\nasync function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{typeString:string}}}> {\n const __prompt = {{{promptCode:string}}};\n const startTime = performance.now();\n const __messages: Message[] = __metadata?.messages || [];\n\n // These are to restore state after interrupt.\n // TODO I think this could be implemented in a cleaner way.\n let __toolCalls =
|
|
1
|
+
export declare const template = "\nasync function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{typeString:string}}}> {\n const __prompt = {{{promptCode:string}}};\n const startTime = performance.now();\n const __messages: Message[] = __metadata?.messages || [];\n\n // These are to restore state after interrupt.\n // TODO I think this could be implemented in a cleaner way.\n let __toolCalls = __stateStack.interruptData?.toolCall ? [__stateStack.interruptData.toolCall] : [];\n const __interruptResponse:InterruptResponseType|null = __stateStack.interruptData?.interruptResponse || null;\n const __tools = {{{tools}}};\n\n {{#hasResponseFormat}}\n // Need to make sure this is always an object\n const __responseFormat = z.object({\n response: {{{zodSchema:string}}}\n });\n {{/hasResponseFormat}}\n {{^hasResponseFormat}}\n const __responseFormat = undefined;\n {{/hasResponseFormat}}\n \n const __client = getClientWithConfig({{{clientConfig:string}}});\n let responseMessage:any;\n\n if (__toolCalls.length === 0) {\n __messages.push(userMessage(__prompt));\n \n \n let __completion = await __client.text({\n messages: __messages,\n tools: __tools,\n responseFormat: __responseFormat,\n });\n \n const endTime = performance.now();\n statelogClient.promptCompletion({\n messages: __messages,\n completion: __completion,\n model: __client.getModel(),\n timeTaken: endTime - startTime,\n });\n \n if (!__completion.success) {\n throw new Error(\n `Error getting response from ${__model}: ${__completion.error}`\n );\n }\n \n responseMessage = __completion.value;\n __toolCalls = responseMessage.toolCalls || [];\n\n if (__toolCalls.length > 0) {\n // Add assistant's response with tool calls to message history\n __messages.push(assistantMessage(responseMessage.output, { toolCalls: __toolCalls }));\n }\n }\n\n // Handle function calls\n if (__toolCalls.length > 0) {\n let toolCallStartTime, toolCallEndTime;\n let haltExecution = false;\n let haltToolCall = {}\n let haltInterrupt:any = null;\n\n // Process each tool call\n for (const toolCall of __toolCalls) {\n {{{functionCalls:string}}}\n }\n\n if (haltExecution) {\n statelogClient.debug(`Tool call interrupted execution.`, {\n messages: __messages,\n model: __client.getModel(),\n });\n\n __stateStack.interruptData = {\n messages: __messages.map((msg) => msg.toJSON()),\n nodesTraversed: __graph.getNodesTraversed(),\n toolCall: haltToolCall,\n };\n haltInterrupt.__state = __stateStack.toJSON();\n return haltInterrupt;\n }\n \n const nextStartTime = performance.now();\n let __completion = await __client.text({\n messages: __messages,\n tools: __tools,\n responseFormat: __responseFormat,\n });\n\n const nextEndTime = performance.now();\n\n statelogClient.promptCompletion({\n messages: __messages,\n completion: __completion,\n model: __client.getModel(),\n timeTaken: nextEndTime - nextStartTime,\n });\n\n if (!__completion.success) {\n throw new Error(\n `Error getting response from ${__model}: ${__completion.error}`\n );\n }\n responseMessage = __completion.value;\n }\n\n // Add final assistant response to history\n // not passing tool calls back this time\n __messages.push(assistantMessage(responseMessage.output));\n {{#hasResponseFormat}}\n try {\n const result = JSON.parse(responseMessage.output || \"\");\n return result.response;\n } catch (e) {\n return responseMessage.output;\n // console.error(\"Error parsing response for variable '{{{variableName:string}}}':\", e);\n // console.error(\"Full completion response:\", JSON.stringify(__completion, null, 2));\n // throw e;\n }\n {{/hasResponseFormat}}\n\n {{^hasResponseFormat}}\n return responseMessage.output;\n {{/hasResponseFormat}}\n}\n\n__self.{{{variableName:string}}} = await _{{{variableName:string}}}({{{funcCallParams:string}}});\n\n// return early from node if this is an interrupt\nif (isInterrupt(__self.{{{variableName:string}}})) {\n {{#nodeContext}}\n return { ...state, data: __self.{{{variableName:string}}} };\n {{/nodeContext}}\n {{^nodeContext}}\n return __self.{{{variableName:string}}};\n {{/nodeContext}}\n}";
|
|
2
2
|
export type TemplateType = {
|
|
3
3
|
variableName: string;
|
|
4
4
|
argsStr: string;
|
|
@@ -10,6 +10,7 @@ export type TemplateType = {
|
|
|
10
10
|
clientConfig: string;
|
|
11
11
|
functionCalls: string;
|
|
12
12
|
funcCallParams: string;
|
|
13
|
+
nodeContext: boolean;
|
|
13
14
|
};
|
|
14
15
|
declare const render: (args: TemplateType) => string;
|
|
15
16
|
export default render;
|
|
@@ -10,8 +10,8 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
|
|
|
10
10
|
|
|
11
11
|
// These are to restore state after interrupt.
|
|
12
12
|
// TODO I think this could be implemented in a cleaner way.
|
|
13
|
-
let __toolCalls =
|
|
14
|
-
const __interruptResponse:InterruptResponseType|
|
|
13
|
+
let __toolCalls = __stateStack.interruptData?.toolCall ? [__stateStack.interruptData.toolCall] : [];
|
|
14
|
+
const __interruptResponse:InterruptResponseType|null = __stateStack.interruptData?.interruptResponse || null;
|
|
15
15
|
const __tools = {{{tools}}};
|
|
16
16
|
|
|
17
17
|
{{#hasResponseFormat}}
|
|
@@ -38,7 +38,7 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
const endTime = performance.now();
|
|
41
|
-
|
|
41
|
+
statelogClient.promptCompletion({
|
|
42
42
|
messages: __messages,
|
|
43
43
|
completion: __completion,
|
|
44
44
|
model: __client.getModel(),
|
|
@@ -73,12 +73,12 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
if (haltExecution) {
|
|
76
|
-
|
|
76
|
+
statelogClient.debug(\`Tool call interrupted execution.\`, {
|
|
77
77
|
messages: __messages,
|
|
78
78
|
model: __client.getModel(),
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
__stateStack.
|
|
81
|
+
__stateStack.interruptData = {
|
|
82
82
|
messages: __messages.map((msg) => msg.toJSON()),
|
|
83
83
|
nodesTraversed: __graph.getNodesTraversed(),
|
|
84
84
|
toolCall: haltToolCall,
|
|
@@ -96,7 +96,7 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
|
|
|
96
96
|
|
|
97
97
|
const nextEndTime = performance.now();
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
statelogClient.promptCompletion({
|
|
100
100
|
messages: __messages,
|
|
101
101
|
completion: __completion,
|
|
102
102
|
model: __client.getModel(),
|
|
@@ -135,7 +135,12 @@ __self.{{{variableName:string}}} = await _{{{variableName:string}}}({{{funcCallP
|
|
|
135
135
|
|
|
136
136
|
// return early from node if this is an interrupt
|
|
137
137
|
if (isInterrupt(__self.{{{variableName:string}}})) {
|
|
138
|
+
{{#nodeContext}}
|
|
138
139
|
return { ...state, data: __self.{{{variableName:string}}} };
|
|
140
|
+
{{/nodeContext}}
|
|
141
|
+
{{^nodeContext}}
|
|
142
|
+
return __self.{{{variableName:string}}};
|
|
143
|
+
{{/nodeContext}}
|
|
139
144
|
}`;
|
|
140
145
|
const render = (args) => {
|
|
141
146
|
return apply(template, args);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const template = "if (\n toolCall.name === \"{{{name:string}}}\"\n) {\n const args = toolCall.arguments;\n\n const params = [ {{{paramsStr:string}}} ];\n\n toolCallStartTime = performance.now();\n \n let result: any;\n if (__interruptResponse && __interruptResponse.type === \"reject\") {\n __messages.push(toolMessage(\"tool call rejected\", {\n tool_call_id: toolCall.id,\n name: toolCall.name,\n }));\n } else {\n result = await {{{name}}}(params);\n }\n toolCallEndTime = performance.now();\n\
|
|
1
|
+
export declare const template = "if (\n toolCall.name === \"{{{name:string}}}\"\n) {\n const args = toolCall.arguments;\n\n const params = [ {{{paramsStr:string}}} ];\n\n toolCallStartTime = performance.now();\n \n let result: any;\n if (__interruptResponse && __interruptResponse.type === \"reject\") {\n __messages.push(toolMessage(\"tool call rejected\", {\n tool_call_id: toolCall.id,\n name: toolCall.name,\n }));\n } else {\n result = await {{{name}}}(params);\n }\n toolCallEndTime = performance.now();\n\n statelogClient.toolCall({\n toolName: \"{{{name:string}}}\",\n params,\n output: result,\n model: __client.getModel(),\n timeTaken: toolCallEndTime - toolCallStartTime,\n });\n\n if (isInterrupt(result)) {\n haltInterrupt = result;\n haltToolCall = {\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n }\n haltExecution = true;\n break;\n }\n\n // Add function result to messages\n __messages.push(toolMessage(result, {\n tool_call_id: toolCall.id,\n name: toolCall.name,\n }));\n}";
|
|
2
2
|
export type TemplateType = {
|
|
3
3
|
name: string;
|
|
4
4
|
paramsStr: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agency-lang",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.48",
|
|
4
4
|
"description": "The Agency language",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"homepage": "https://github.com/egonSchiele/agency-lang#readme",
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"egonlog": "^0.0.2",
|
|
44
|
-
"piemachine": "^0.0.
|
|
44
|
+
"piemachine": "^0.0.7",
|
|
45
45
|
"smoltalk": "^0.0.15",
|
|
46
|
-
"statelog-client": "^0.0.
|
|
46
|
+
"statelog-client": "^0.0.35",
|
|
47
47
|
"tarsec": "^0.1.1",
|
|
48
48
|
"typestache": "^0.4.4",
|
|
49
49
|
"zod": "^4.3.5"
|
|
@@ -56,4 +56,4 @@
|
|
|
56
56
|
"typescript": "^5.9.3",
|
|
57
57
|
"vitest": "^4.0.16"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|