agency-lang 0.0.41 → 0.0.43

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.
@@ -12,6 +12,7 @@ import { WhileLoop } from "../types/whileLoop.js";
12
12
  import { IfElse } from "../types/ifElse.js";
13
13
  import { TimeBlock } from "../types/timeBlock.js";
14
14
  import { AwaitStatement } from "../types/await.js";
15
+ type Scope = "global" | "function" | "node";
15
16
  export declare class BaseGenerator {
16
17
  protected typeHints: TypeHintMap;
17
18
  protected graphNodes: GraphNodeDefinition[];
@@ -19,6 +20,7 @@ export declare class BaseGenerator {
19
20
  protected generatedTypeAliases: string[];
20
21
  protected functionScopedVariables: string[];
21
22
  protected globalScopedVariables: string[];
23
+ protected functionParameters: string[];
22
24
  protected toolsUsed: string[];
23
25
  protected typeAliases: Record<string, VariableType>;
24
26
  protected functionsUsed: Set<string>;
@@ -26,6 +28,7 @@ export declare class BaseGenerator {
26
28
  protected importedNodes: ImportNodeStatement[];
27
29
  protected importedTools: ImportToolStatement[];
28
30
  protected functionDefinitions: Record<string, FunctionDefinition>;
31
+ protected currentScope: Scope[];
29
32
  generate(program: AgencyProgram): {
30
33
  output: string;
31
34
  };
@@ -66,4 +69,8 @@ export declare class BaseGenerator {
66
69
  protected generateImports(): string;
67
70
  protected preprocess(): string;
68
71
  protected postprocess(): string;
72
+ protected startScope(scope: Scope): void;
73
+ protected endScope(): void;
74
+ protected getScopeVar(): string;
69
75
  }
76
+ export {};
@@ -5,6 +5,7 @@ export class BaseGenerator {
5
5
  generatedTypeAliases = [];
6
6
  functionScopedVariables = [];
7
7
  globalScopedVariables = [];
8
+ functionParameters = [];
8
9
  // collect tools for a prompt
9
10
  toolsUsed = [];
10
11
  typeAliases = {};
@@ -16,6 +17,7 @@ export class BaseGenerator {
16
17
  // collect function signatures so we can implement named args
17
18
  // TODO also save return types, check if used as a tool, return type cannot be null/void/undefined
18
19
  functionDefinitions = {};
20
+ currentScope = ["global"];
19
21
  generate(program) {
20
22
  // Pass 1: Collect all type aliases
21
23
  for (const node of program.nodes) {
@@ -253,4 +255,20 @@ export class BaseGenerator {
253
255
  postprocess() {
254
256
  return "";
255
257
  }
258
+ startScope(scope) {
259
+ this.currentScope.push(scope);
260
+ }
261
+ endScope() {
262
+ this.currentScope.pop();
263
+ }
264
+ getScopeVar() {
265
+ const currentScope = this.currentScope[this.currentScope.length - 1];
266
+ switch (currentScope) {
267
+ case "global":
268
+ return "__stateStack.globals";
269
+ case "function":
270
+ case "node":
271
+ return "__stack.locals";
272
+ }
273
+ }
256
274
  }
@@ -39,6 +39,7 @@ export class GraphGenerator extends TypeScriptGenerator {
39
39
  this.graphNodes.push(node);
40
40
  }
41
41
  processGraphNode(node) {
42
+ this.startScope("node");
42
43
  const { nodeName, body, parameters } = node;
43
44
  /* if (parameters.length > 1) {
44
45
  throw new Error(
@@ -47,22 +48,22 @@ export class GraphGenerator extends TypeScriptGenerator {
47
48
  } */
48
49
  this.adjacentNodes[nodeName] = [];
49
50
  this.currentAdjacentNodes = [];
50
- this.functionScopedVariables = [];
51
+ this.functionParameters = [];
51
52
  this.isInsideGraphNode = true;
52
53
  for (const param of parameters) {
53
- this.functionScopedVariables.push(param.name);
54
+ this.functionParameters.push(param.name);
54
55
  }
55
- const bodyCode = [];
56
56
  for (const stmt of body) {
57
57
  if (stmt.type === "functionCall" && this.isGraphNode(stmt.functionName)) {
58
58
  throw new Error(`Call to graph node '${stmt.functionName}' inside graph node '${nodeName}' was not returned. All calls to graph nodes must be returned, eg (return ${stmt.functionName}(...)).`);
59
59
  }
60
- bodyCode.push(this.processNode(stmt));
61
60
  }
62
- this.functionScopedVariables = [];
61
+ const bodyCode = this.processBodyAsParts(body);
62
+ this.functionParameters = [];
63
63
  this.adjacentNodes[nodeName] = [...this.currentAdjacentNodes];
64
64
  this.isInsideGraphNode = false;
65
- const paramNames = "[" + parameters.map((p) => p.name).join(", ") + "]";
65
+ this.endScope();
66
+ const paramNames = "[" + parameters.map((p) => `"${p.name}"`).join(", ") + "]";
66
67
  return renderGraphNode.default({
67
68
  name: nodeName,
68
69
  /* returnType: node.returnType
@@ -1,17 +1,17 @@
1
- import { AgencyComment, AgencyProgram, Assignment, Literal, PromptLiteral, PromptSegment, TypeAlias, TypeHint, TypeHintMap, VariableType } from "../types.js";
1
+ import { AgencyComment, AgencyNode, AgencyProgram, Assignment, Literal, PromptLiteral, PromptSegment, TypeAlias, TypeHint, TypeHintMap, VariableType } from "../types.js";
2
+ import { AwaitStatement } from "../types/await.js";
2
3
  import { SpecialVar } from "../types/specialVar.js";
4
+ import { TimeBlock } from "../types/timeBlock.js";
3
5
  import { AccessExpression, DotFunctionCall, DotProperty, IndexAccess } from "../types/access.js";
4
6
  import { AgencyArray, AgencyObject } from "../types/dataStructures.js";
5
7
  import { FunctionCall, FunctionDefinition } from "../types/function.js";
8
+ import { IfElse } from "../types/ifElse.js";
6
9
  import { ImportNodeStatement, ImportStatement, ImportToolStatement } from "../types/importStatement.js";
7
10
  import { MatchBlock } from "../types/matchBlock.js";
8
11
  import { ReturnStatement } from "../types/returnStatement.js";
9
12
  import { UsesTool } from "../types/tools.js";
10
13
  import { WhileLoop } from "../types/whileLoop.js";
11
- import { IfElse } from "../types/ifElse.js";
12
14
  import { BaseGenerator } from "./baseGenerator.js";
13
- import { TimeBlock } from "../types/timeBlock.js";
14
- import { AwaitStatement } from "../types/await.js";
15
15
  export declare class TypeScriptGenerator extends BaseGenerator {
16
16
  constructor();
17
17
  protected generateBuiltins(): string;
@@ -43,6 +43,7 @@ export declare class TypeScriptGenerator extends BaseGenerator {
43
43
  * Generates TypeScript expression for a function call (without semicolon)
44
44
  */
45
45
  protected generateFunctionCallExpression(node: FunctionCall): string;
46
+ protected generateScopedVariableName(variableName: string): string;
46
47
  protected generateLiteral(literal: Literal): string;
47
48
  protected generateImports(): string;
48
49
  buildPromptString(segments: PromptSegment[], typeHints: TypeHintMap): string;
@@ -64,5 +65,6 @@ export declare class TypeScriptGenerator extends BaseGenerator {
64
65
  protected processSpecialVar(node: SpecialVar): string;
65
66
  protected processTimeBlock(node: TimeBlock, timingVarName: string): string;
66
67
  protected processAwaitStatement(node: AwaitStatement): string;
68
+ protected processBodyAsParts(body: AgencyNode[]): string[];
67
69
  }
68
70
  export declare function generateTypeScript(program: AgencyProgram): string;
@@ -1,16 +1,17 @@
1
1
  import * as renderSpecialVar from "../templates/backends/graphGenerator/specialVar.js";
2
+ import * as renderTime from "../templates/backends/typescriptGenerator/builtinFunctions/time.js";
2
3
  import * as builtinTools from "../templates/backends/typescriptGenerator/builtinTools.js";
3
4
  import * as renderFunctionDefinition from "../templates/backends/typescriptGenerator/functionDefinition.js";
4
5
  import * as renderImports from "../templates/backends/typescriptGenerator/imports.js";
5
6
  import * as promptFunction from "../templates/backends/typescriptGenerator/promptFunction.js";
6
7
  import * as renderTool from "../templates/backends/typescriptGenerator/tool.js";
7
- import * as renderTime from "../templates/backends/typescriptGenerator/builtinFunctions/time.js";
8
8
  import * as renderToolCall from "../templates/backends/typescriptGenerator/toolCall.js";
9
- import { escape, uniq, zip } from "../utils.js";
9
+ import { escape, uniq } from "../utils.js";
10
10
  import { BaseGenerator } from "./baseGenerator.js";
11
11
  import { generateBuiltinHelpers, mapFunctionName, } from "./typescriptGenerator/builtins.js";
12
12
  import { variableTypeToString } from "./typescriptGenerator/typeToString.js";
13
13
  import { DEFAULT_SCHEMA, mapTypeToZodSchema, } from "./typescriptGenerator/typeToZodSchema.js";
14
+ import { TYPES_THAT_DONT_TRIGGER_NEW_PART } from "../config.js";
14
15
  export class TypeScriptGenerator extends BaseGenerator {
15
16
  constructor() {
16
17
  super();
@@ -58,7 +59,11 @@ export class TypeScriptGenerator extends BaseGenerator {
58
59
  }
59
60
  processReturnStatement(node) {
60
61
  const returnCode = this.processNode(node.value);
61
- return `return ${returnCode}\n`;
62
+ if (node.value.type === "functionCall" &&
63
+ node.value.functionName === "interrupt") {
64
+ return `__stack.step++;\nreturn ${returnCode}\n`;
65
+ }
66
+ return `__stateStack.pop();\nreturn ${returnCode}\n`;
62
67
  }
63
68
  processAccessExpression(node) {
64
69
  switch (node.expression.type) {
@@ -140,7 +145,8 @@ export class TypeScriptGenerator extends BaseGenerator {
140
145
  }
141
146
  // Direct assignment for other literal types
142
147
  const code = this.processNode(value);
143
- return (`const ${variableName}${typeAnnotation} = await ${code.trim()};` + "\n");
148
+ return (`${this.getScopeVar()}.${variableName}${typeAnnotation} = await ${code.trim()};` +
149
+ "\n");
144
150
  }
145
151
  else if (value.type === "timeBlock") {
146
152
  const timingVarName = variableName;
@@ -150,7 +156,8 @@ export class TypeScriptGenerator extends BaseGenerator {
150
156
  else {
151
157
  // Direct assignment for other literal types
152
158
  const code = this.processNode(value);
153
- return `const ${variableName}${typeAnnotation} = ${code.trim()};` + "\n";
159
+ return (`${this.getScopeVar()}.${variableName}${typeAnnotation} = ${code.trim()};` +
160
+ "\n");
154
161
  }
155
162
  }
156
163
  /*
@@ -180,10 +187,12 @@ export class TypeScriptGenerator extends BaseGenerator {
180
187
  prompt: node,
181
188
  });
182
189
  //this.generatedStatements.push(functionCode);
183
- const argsStr = [...interpolatedVars, "__messages"].join(", ");
190
+ //const argsStr = [...interpolatedVars, "__messages"].join(", ");
184
191
  // Generate the function call
185
- return (`${functionCode}\nconst ${variableName} = await _${variableName}(${argsStr});` +
186
- "\n");
192
+ return functionCode; /* (
193
+ `${functionCode}\nconst __self.${variableName} = await _${variableName}(${argsStr});` +
194
+ "\n"
195
+ ); */
187
196
  }
188
197
  processTool(node) {
189
198
  const { functionName, body, parameters } = node;
@@ -221,17 +230,19 @@ export class TypeScriptGenerator extends BaseGenerator {
221
230
  * Process a function definition node
222
231
  */
223
232
  processFunctionDefinition(node) {
233
+ this.startScope("function");
224
234
  const { functionName, body, parameters } = node;
235
+ const args = parameters.map((p) => p.name);
225
236
  this.functionScopedVariables = [...parameters.map((p) => p.name)];
226
- const bodyCode = [];
227
- for (const stmt of body) {
228
- bodyCode.push(this.processNode(stmt));
229
- }
237
+ this.functionParameters = args;
238
+ const bodyCode = this.processBodyAsParts(body);
230
239
  this.functionScopedVariables = [];
231
- const args = parameters.map((p) => p.name).join(", ") || "";
240
+ this.functionParameters = [];
241
+ this.endScope();
242
+ const argsStr = args.map((arg) => `"${arg}"`).join(", ") || "";
232
243
  return renderFunctionDefinition.default({
233
244
  functionName,
234
- args: "{" + args + "}",
245
+ argsStr,
235
246
  returnType: node.returnType
236
247
  ? variableTypeToString(node.returnType, this.typeAliases)
237
248
  : "any",
@@ -279,22 +290,32 @@ export class TypeScriptGenerator extends BaseGenerator {
279
290
  }
280
291
  });
281
292
  let argsString = "";
282
- const paramNames = this.functionDefinitions[node.functionName]?.parameters.map((p) => p.name) || null;
283
- if (paramNames) {
284
- const partsWithNames = zip(paramNames, parts).map(([paramName, part]) => {
285
- return `${paramName}: ${part}`;
286
- });
287
- argsString = partsWithNames.join(", ");
288
- return `${functionName}({${argsString}})`;
293
+ const isImportedTool = this.importedTools
294
+ .map((node) => node.importedTools)
295
+ .flat()
296
+ .includes(node.functionName);
297
+ if (this.functionDefinitions[node.functionName] || isImportedTool) {
298
+ argsString = parts.join(", ");
299
+ return `${functionName}([${argsString}])`;
289
300
  }
290
301
  else {
291
- // must be a builtin function or imported function,
292
- // as we don't have the signature info
293
- // in that case don't do named parameters
302
+ // must be a builtin function or imported function
294
303
  argsString = parts.join(", ");
295
304
  return `${functionName}(${argsString})`;
296
305
  }
297
306
  }
307
+ generateScopedVariableName(variableName) {
308
+ if (this.functionParameters.includes(variableName)) {
309
+ return `__stack.args.${variableName}`;
310
+ }
311
+ if (this.functionScopedVariables.includes(variableName)) {
312
+ return `__stack.locals.${variableName}`;
313
+ }
314
+ else if (this.globalScopedVariables.includes(variableName)) {
315
+ return `__stateStack.globals.${variableName}`;
316
+ }
317
+ return variableName;
318
+ }
298
319
  generateLiteral(literal) {
299
320
  switch (literal.type) {
300
321
  case "number":
@@ -304,7 +325,7 @@ export class TypeScriptGenerator extends BaseGenerator {
304
325
  case "multiLineString":
305
326
  return this.generateStringLiteral(literal.segments);
306
327
  case "variableName":
307
- return literal.value;
328
+ return this.generateScopedVariableName(literal.value);
308
329
  case "prompt":
309
330
  //return this.processPromptLiteral("asd", literal).trim();
310
331
  // Reconstruct text for comment from segments
@@ -350,7 +371,7 @@ export class TypeScriptGenerator extends BaseGenerator {
350
371
  }
351
372
  else {
352
373
  // Interpolation segment
353
- stringParts.push("${" + segment.variableName + "}");
374
+ stringParts.push("${" + this.generateScopedVariableName(segment.variableName) + "}");
354
375
  }
355
376
  }
356
377
  return "`" + stringParts.join("") + "`";
@@ -371,7 +392,7 @@ export class TypeScriptGenerator extends BaseGenerator {
371
392
  // Build prompt construction code
372
393
  const promptCode = this.buildPromptString(prompt.segments, this.typeHints);
373
394
  const parts = functionArgs.map((arg) => `${arg.replace(".", "_")}: ${variableTypeToString(this.typeHints[arg] || { type: "primitiveType", value: "string" }, this.typeAliases)}`);
374
- parts.push("__messages: Message[] = []");
395
+ parts.push("__metadata?: Record<string, any>");
375
396
  const argsStr = parts.join(", ");
376
397
  const _tools = this.toolsUsed
377
398
  .map((toolName) => `__${toolName}Tool`)
@@ -379,16 +400,33 @@ export class TypeScriptGenerator extends BaseGenerator {
379
400
  const tools = _tools.length > 0 ? `[${_tools}]` : "undefined";
380
401
  const functionCalls = this.toolsUsed
381
402
  .map((toolName) => {
403
+ const func = this.functionDefinitions[toolName];
404
+ if (!func) {
405
+ throw new Error(`Tool '${toolName}' is being used but no function definition found for it. Make sure to define a function for this tool.`);
406
+ }
407
+ const paramsStr = func.parameters
408
+ .map((param, index) => {
409
+ return `args["${param.name}"]`;
410
+ })
411
+ .join(", ");
382
412
  return renderToolCall.default({
383
413
  name: toolName,
414
+ paramsStr,
384
415
  });
385
416
  })
386
417
  .join("\n");
387
418
  const clientConfig = prompt.config ? this.processNode(prompt.config) : "{}";
419
+ const metadataObj = `{
420
+ messages: __messages,
421
+ interruptResponse: __interruptResponse,
422
+ toolCall: __toolCall,
423
+ }`;
388
424
  this.toolsUsed = []; // reset after use
425
+ const scopedFunctionArgs = functionArgs.map((arg) => this.generateScopedVariableName(arg));
389
426
  return promptFunction.default({
390
427
  variableName,
391
428
  argsStr,
429
+ funcCallParams: [...scopedFunctionArgs, metadataObj].join(", "),
392
430
  typeString,
393
431
  promptCode,
394
432
  hasResponseFormat: zodSchema !== DEFAULT_SCHEMA,
@@ -464,6 +502,28 @@ export class TypeScriptGenerator extends BaseGenerator {
464
502
  const code = this.processNode(node.expression);
465
503
  return `await ${code}`;
466
504
  }
505
+ processBodyAsParts(body) {
506
+ const parts = [[]];
507
+ for (const stmt of body) {
508
+ if (!TYPES_THAT_DONT_TRIGGER_NEW_PART.includes(stmt.type)) {
509
+ parts.push([]);
510
+ }
511
+ parts[parts.length - 1].push(this.processNode(stmt));
512
+ }
513
+ const bodyCode = [];
514
+ let partNum = 0;
515
+ for (const part of parts) {
516
+ const partCode = `
517
+ if (__step <= ${partNum}) {
518
+ ${part.join("").trimEnd()}
519
+ __stack.step++;
520
+ }
521
+ `;
522
+ bodyCode.push(partCode);
523
+ partNum++;
524
+ }
525
+ return bodyCode;
526
+ }
467
527
  }
468
528
  export function generateTypeScript(program) {
469
529
  const generator = new TypeScriptGenerator();
@@ -0,0 +1,2 @@
1
+ import { AgencyNode } from "./types.js";
2
+ export declare const TYPES_THAT_DONT_TRIGGER_NEW_PART: AgencyNode["type"][];
@@ -0,0 +1,10 @@
1
+ export const TYPES_THAT_DONT_TRIGGER_NEW_PART = [
2
+ "typeHint",
3
+ "typeAlias",
4
+ "usesTool",
5
+ "comment",
6
+ "newLine",
7
+ "importStatement",
8
+ "importNodeStatement",
9
+ "importToolStatement",
10
+ ];
@@ -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 {{#hasParam}}\n const {{{paramNames}}} = state.data;\n {{/hasParam}}\n {{{body}}}\n return { ...state, data: undefined };\n});\n";
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 if (state.__metadata?.__stateStack) {\n __stateStack = state.__metadata.__stateStack;\n }\n const __stack = __stateStack.getNewState();\n const __step = __stack.step;\n\n const __self: Record<string, any> = __stack.locals;\n\n const __interruptResponse: InterruptResponseType | undefined = state.__metadata?.interruptResponse;\n const __toolCall: Record<string, any>|undefined = __stateStack.other?.toolCall;\n\n if (state.__metadata?.state?.global) {\n __global = state.__metadata.state.global;\n }\n\n {{#hasParam}}\n \n const __params = {{{paramNames}}};\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;
@@ -7,10 +7,33 @@ graph.node("{{{name}}}", async (state): Promise<any> => {
7
7
  const __messages: Message[] = state.messages || [];
8
8
  const __graph = state.__metadata?.graph || graph;
9
9
  const statelogClient = state.__metadata?.statelogClient || __statelogClient;
10
+ if (state.__metadata?.__stateStack) {
11
+ __stateStack = state.__metadata.__stateStack;
12
+ }
13
+ const __stack = __stateStack.getNewState();
14
+ const __step = __stack.step;
15
+
16
+ const __self: Record<string, any> = __stack.locals;
17
+
18
+ const __interruptResponse: InterruptResponseType | undefined = state.__metadata?.interruptResponse;
19
+ const __toolCall: Record<string, any>|undefined = __stateStack.other?.toolCall;
20
+
21
+ if (state.__metadata?.state?.global) {
22
+ __global = state.__metadata.state.global;
23
+ }
24
+
10
25
  {{#hasParam}}
11
- const {{{paramNames}}} = state.data;
26
+
27
+ const __params = {{{paramNames}}};
28
+ if (state.data !== "<from-stack>") {
29
+ (state.data).forEach((item, index) => {
30
+ __stack.args[__params[index]] = item;
31
+ });
32
+ }
12
33
  {{/hasParam}}
13
34
  {{{body}}}
35
+
36
+ // this is just here to have a default return value from a node if the user doesn't specify one
14
37
  return { ...state, data: undefined };
15
38
  });
16
39
  `;
@@ -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 } 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\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\ntype Interrupt<T> = {\n type: \"interrupt\";\n data: T;\n};\n\nfunction interrupt<T>(data: T): Interrupt<T> {\n return {\n type: \"interrupt\",\n data,\n };\n}\n\nfunction 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}";
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 __state?: PackagedState;\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 | InterruptResponseModify;\nexport type InterruptResponseApprove = {\n type: \"approve\";\n};\nexport type InterruptResponseReject = {\n type: \"reject\";\n};\nexport type InterruptResponseModify = {\n type: \"modify\";\n newArguments: Record<string, any>;\n};\n\n\nexport async function respondToInterrupt(_interrupt: Interrupt, _interruptResponse: InterruptResponseType) {\n const interrupt = structuredClone(_interrupt);\n const interruptResponse = structuredClone(_interruptResponse);\n __stateStack = StateStack.fromJSON(interrupt.__state || {});\n __stateStack.setMode(\"deserialize\");\n const messages = (__stateStack.other.messages || []).map((json: any) => {\n return messageFromJSON(json);\n });\n\n const nodesTraversed = __stateStack.other.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 interruptResponse: interruptResponse,\n state: interrupt.__state,\n __stateStack: __stateStack,\n },\n data: \"<from-stack>\"\n });\n //console.log(`Result of graph.run(\"${nodeName}\"):`, JSON.stringify(result, null, 2));\n return result.data;\n}\n\n\nclass PackagedState {\n public messages?: Message[];\n public nodesTraversed?: string[];\n public toolCall?: Record<string, any>;\n public step?: number;\n public self?: Record<string, any>;\n public global?: Record<string, any>;\n public args?: any;\n constructor(_state: Record<string, any>, args?: any) {\n const state = structuredClone(_state);\n this.messages = state.messages;\n this.nodesTraversed = state.graph?.getNodesTraversed();\n this.toolCall = state.toolCall;\n this.step = state.part;\n this.self = state.self;\n this.global = state.global;\n this.args = state.args;\n }\n\n toJSON() {\n return {\n messages: this.messages,\n nodesTraversed: this.nodesTraversed,\n toolCall: this.toolCall,\n step: this.step,\n self: this.self,\n global: this.global,\n args: this.args,\n };\n }\n\n nextStep() {\n this.step ||= 0;\n this.step += 1;\n }\n}\n\n\nclass StateStack {\n public stack: StateItem[] = [];\n public mode: \"serialize\" | \"deserialize\" = \"serialize\";\n public globals: Record<string, any> = {};\n public other: Record<string, any> = {};\n\n constructor(stack: StateItem[] = [], mode: \"serialize\" | \"deserialize\" = \"serialize\") {\n this.stack = stack;\n this.mode = mode;\n }\n\n getNewState(): StateItem | null {\n if (this.stack.length === 0 && this.mode !== \"serialize\") {\n console.log(\"Forcing mode to serialize, nothing left to deserialize\");\n this.mode = \"serialize\";\n }\n if (this.mode === \"serialize\") {\n const newState: StateItem = {\n args: {},\n locals: {},\n step: 0,\n };\n this.stack.push(newState);\n return newState;\n } else if (this.mode === \"deserialize\") {\n const item = this.stack.shift();\n this.stack.push(item);\n return item;\n }\n return null;\n }\n\n setMode(mode: \"serialize\" | \"deserialize\") {\n this.mode = mode;\n }\n\n pop(): StateItem | 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 mode: this.mode,\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.mode = json.mode || \"serialize\";\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;
@@ -8,7 +8,8 @@ import fs from "fs";
8
8
  import { PieMachine, goToNode } from "piemachine";
9
9
  import { StatelogClient } from "statelog-client";
10
10
  import { nanoid } from "nanoid";
11
- import { assistantMessage, getClient, userMessage, toolMessage } from "smoltalk";
11
+ import { assistantMessage, getClient, userMessage, toolMessage, messageFromJSON } from "smoltalk";
12
+ import type { Message } from "smoltalk";
12
13
 
13
14
  const statelogHost = "https://statelog.adit.io";
14
15
  const traceId = nanoid();
@@ -22,7 +23,6 @@ const statelogConfig = {
22
23
  const __statelogClient = new StatelogClient(statelogConfig);
23
24
  const __model: ModelName = "gpt-4o-mini";
24
25
 
25
-
26
26
  const getClientWithConfig = (config = {}) => {
27
27
  const defaultConfig = {
28
28
  openAiApiKey: process.env.OPENAI_API_KEY || "",
@@ -69,25 +69,165 @@ const empty = <T>(arr: T[]): boolean => arr.length === 0;
69
69
 
70
70
  // interrupts
71
71
 
72
- type Interrupt<T> = {
72
+ export type Interrupt<T> = {
73
73
  type: "interrupt";
74
74
  data: T;
75
+ __state?: PackagedState;
75
76
  };
76
77
 
77
- function interrupt<T>(data: T): Interrupt<T> {
78
+ export function interrupt<T>(data: T): Interrupt<T> {
78
79
  return {
79
80
  type: "interrupt",
80
81
  data,
81
82
  };
82
83
  }
83
84
 
84
- function isInterrupt<T>(obj: any): obj is Interrupt<T> {
85
+ export function isInterrupt<T>(obj: any): obj is Interrupt<T> {
85
86
  return obj && obj.type === "interrupt";
86
87
  }
87
88
 
88
89
  function printJSON(obj: any) {
89
90
  console.log(JSON.stringify(obj, null, 2));
90
- }`;
91
+ }
92
+
93
+ export type InterruptResponseType = InterruptResponseApprove | InterruptResponseReject | InterruptResponseModify;
94
+ export type InterruptResponseApprove = {
95
+ type: "approve";
96
+ };
97
+ export type InterruptResponseReject = {
98
+ type: "reject";
99
+ };
100
+ export type InterruptResponseModify = {
101
+ type: "modify";
102
+ newArguments: Record<string, any>;
103
+ };
104
+
105
+
106
+ export async function respondToInterrupt(_interrupt: Interrupt, _interruptResponse: InterruptResponseType) {
107
+ const interrupt = structuredClone(_interrupt);
108
+ const interruptResponse = structuredClone(_interruptResponse);
109
+ __stateStack = StateStack.fromJSON(interrupt.__state || {});
110
+ __stateStack.setMode("deserialize");
111
+ const messages = (__stateStack.other.messages || []).map((json: any) => {
112
+ return messageFromJSON(json);
113
+ });
114
+
115
+ const nodesTraversed = __stateStack.other.nodesTraversed || [];
116
+ const nodeName = nodesTraversed[nodesTraversed.length - 1];
117
+ const result = await graph.run(nodeName, {
118
+ messages: messages,
119
+ __metadata: {
120
+ graph: graph,
121
+ statelogClient: __statelogClient,
122
+ interruptResponse: interruptResponse,
123
+ state: interrupt.__state,
124
+ __stateStack: __stateStack,
125
+ },
126
+ data: "<from-stack>"
127
+ });
128
+ //console.log(\`Result of graph.run("\${nodeName}"):\`, JSON.stringify(result, null, 2));
129
+ return result.data;
130
+ }
131
+
132
+
133
+ class PackagedState {
134
+ public messages?: Message[];
135
+ public nodesTraversed?: string[];
136
+ public toolCall?: Record<string, any>;
137
+ public step?: number;
138
+ public self?: Record<string, any>;
139
+ public global?: Record<string, any>;
140
+ public args?: any;
141
+ constructor(_state: Record<string, any>, args?: any) {
142
+ const state = structuredClone(_state);
143
+ this.messages = state.messages;
144
+ this.nodesTraversed = state.graph?.getNodesTraversed();
145
+ this.toolCall = state.toolCall;
146
+ this.step = state.part;
147
+ this.self = state.self;
148
+ this.global = state.global;
149
+ this.args = state.args;
150
+ }
151
+
152
+ toJSON() {
153
+ return {
154
+ messages: this.messages,
155
+ nodesTraversed: this.nodesTraversed,
156
+ toolCall: this.toolCall,
157
+ step: this.step,
158
+ self: this.self,
159
+ global: this.global,
160
+ args: this.args,
161
+ };
162
+ }
163
+
164
+ nextStep() {
165
+ this.step ||= 0;
166
+ this.step += 1;
167
+ }
168
+ }
169
+
170
+
171
+ class StateStack {
172
+ public stack: StateItem[] = [];
173
+ public mode: "serialize" | "deserialize" = "serialize";
174
+ public globals: Record<string, any> = {};
175
+ public other: Record<string, any> = {};
176
+
177
+ constructor(stack: StateItem[] = [], mode: "serialize" | "deserialize" = "serialize") {
178
+ this.stack = stack;
179
+ this.mode = mode;
180
+ }
181
+
182
+ getNewState(): StateItem | null {
183
+ if (this.stack.length === 0 && this.mode !== "serialize") {
184
+ console.log("Forcing mode to serialize, nothing left to deserialize");
185
+ this.mode = "serialize";
186
+ }
187
+ if (this.mode === "serialize") {
188
+ const newState: StateItem = {
189
+ args: {},
190
+ locals: {},
191
+ step: 0,
192
+ };
193
+ this.stack.push(newState);
194
+ return newState;
195
+ } else if (this.mode === "deserialize") {
196
+ const item = this.stack.shift();
197
+ this.stack.push(item);
198
+ return item;
199
+ }
200
+ return null;
201
+ }
202
+
203
+ setMode(mode: "serialize" | "deserialize") {
204
+ this.mode = mode;
205
+ }
206
+
207
+ pop(): StateItem | undefined {
208
+ return this.stack.pop();
209
+ }
210
+
211
+ toJSON() {
212
+ return structuredClone({
213
+ stack: this.stack,
214
+ globals: this.globals,
215
+ other: this.other,
216
+ mode: this.mode,
217
+ });
218
+ }
219
+
220
+ static fromJSON(json: any): StateStack {
221
+ const stateStack = new StateStack([], "serialize");
222
+ stateStack.stack = json.stack || [];
223
+ stateStack.globals = json.globals || {};
224
+ stateStack.other = json.other || {};
225
+ stateStack.mode = json.mode || "serialize";
226
+ return stateStack;
227
+ }
228
+ }
229
+
230
+ let __stateStack = new StateStack();`;
91
231
  const render = (args) => {
92
232
  return apply(template, args);
93
233
  };
@@ -1,4 +1,4 @@
1
- export declare const template = "{{#hasArgs}}\nexport async function {{{nodeName:string}}}({{{argsStr:string}}}, { messages = [] }): Promise<{{{returnType:string}}}> {\n{{/hasArgs}}\n{{^hasArgs}}\nexport async function {{{nodeName:string}}}({ messages = [] }): Promise<{{{returnType:string}}}> {\n{{/hasArgs}}\n const data = [ {{{argsStr:string}}} ];\n const result = await graph.run(\"{{{nodeName:string}}}\", { messages, data });\n return result.data;\n}\n";
1
+ export declare const template = "{{#hasArgs}}\nexport async function {{{nodeName:string}}}({{{argsStr:string}}}, { messages } = {}): Promise<{{{returnType:string}}}> {\n{{/hasArgs}}\n{{^hasArgs}}\nexport async function {{{nodeName:string}}}({ messages } = {}): Promise<{{{returnType:string}}}> {\n{{/hasArgs}}\n const data = [ {{{argsStr:string}}} ];\n const result = await graph.run(\"{{{nodeName:string}}}\", { messages: messages || [], data });\n return result.data;\n}\n";
2
2
  export type TemplateType = {
3
3
  hasArgs: boolean;
4
4
  nodeName: string;
@@ -3,13 +3,13 @@
3
3
  // Any manual changes will be lost.
4
4
  import { apply } from "typestache";
5
5
  export const template = `{{#hasArgs}}
6
- export async function {{{nodeName:string}}}({{{argsStr:string}}}, { messages = [] }): Promise<{{{returnType:string}}}> {
6
+ export async function {{{nodeName:string}}}({{{argsStr:string}}}, { messages } = {}): Promise<{{{returnType:string}}}> {
7
7
  {{/hasArgs}}
8
8
  {{^hasArgs}}
9
- export async function {{{nodeName:string}}}({ messages = [] }): Promise<{{{returnType:string}}}> {
9
+ export async function {{{nodeName:string}}}({ messages } = {}): Promise<{{{returnType:string}}}> {
10
10
  {{/hasArgs}}
11
11
  const data = [ {{{argsStr:string}}} ];
12
- const result = await graph.run("{{{nodeName:string}}}", { messages, data });
12
+ const result = await graph.run("{{{nodeName:string}}}", { messages: messages || [], data });
13
13
  return result.data;
14
14
  }
15
15
  `;
@@ -1,8 +1,8 @@
1
- export declare const template = "\nexport async function {{{functionName:string}}}({{{args}}}) : Promise<{{{returnType}}}> {\n const __messages: Message[] = [];\n {{{functionBody}}}\n}";
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; // > 0 ? __stack.step + 1 : 0;\n const __self: Record<string, any> = __stack.locals;\n\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
- args: string | boolean | number;
5
4
  returnType: string | boolean | number;
5
+ argsStr: string | boolean | number;
6
6
  functionBody: string | boolean | number;
7
7
  };
8
8
  declare const render: (args: TemplateType) => string;
@@ -3,8 +3,18 @@
3
3
  // Any manual changes will be lost.
4
4
  import { apply } from "typestache";
5
5
  export const template = `
6
- export async function {{{functionName:string}}}({{{args}}}) : Promise<{{{returnType}}}> {
6
+ export async function {{{functionName:string}}}(args, __metadata={}) : Promise<{{{returnType}}}> {
7
7
  const __messages: Message[] = [];
8
+ const __stack = __stateStack.getNewState();
9
+ const __step = __stack.step; // > 0 ? __stack.step + 1 : 0;
10
+ const __self: Record<string, any> = __stack.locals;
11
+
12
+ const __params = [{{{argsStr}}}];
13
+ (args).forEach((item, index) => {
14
+ __stack.args[__params[index]] = item;
15
+ });
16
+
17
+
8
18
  {{{functionBody}}}
9
19
  }`;
10
20
  const render = (args) => {
@@ -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 __messages.push(userMessage(__prompt));\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\n let __completion = await __client.text({\n messages: __messages,\n tools: __tools,\n responseFormat: __responseFormat,\n });\n\n const endTime = performance.now();\n await 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 let responseMessage = __completion.value;\n\n // Handle function calls\n while (responseMessage.toolCalls.length > 0) {\n // Add assistant's response with tool calls to message history\n __messages.push(assistantMessage(responseMessage.output, { toolCalls: responseMessage.toolCalls }));\n let toolCallStartTime, toolCallEndTime;\n let haltExecution = false;\n\n // Process each tool call\n for (const toolCall of responseMessage.toolCalls) {\n {{{functionCalls:string}}}\n }\n\n if (haltExecution) {\n await statelogClient.debug(`Tool call interrupted execution.`, {\n messages: __messages,\n model: __client.getModel(),\n });\n try {\n const obj = JSON.parse(__messages.at(-1).content);\n obj.__messages = __messages;\n obj.__nodesTraversed = __graph.getNodesTraversed();\n return obj;\n } catch (e) {\n return __messages.at(-1).content;\n }\n //return __messages;\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 await 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";
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 let __toolCalls = __metadata?.toolCall ? [__metadata.toolCall] : [];\n const __interruptResponse:InterruptResponseType|undefined = __metadata?.interruptResponse;\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 await 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 await statelogClient.debug(`Tool call interrupted execution.`, {\n messages: __messages,\n model: __client.getModel(),\n });\n\n __stateStack.other = {\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 await 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\nif (isInterrupt(__self.{{{variableName:string}}})) {\n return { ...state, data: __self.{{{variableName:string}}} };\n}";
2
2
  export type TemplateType = {
3
3
  variableName: string;
4
4
  argsStr: string;
@@ -9,6 +9,7 @@ export type TemplateType = {
9
9
  zodSchema: string;
10
10
  clientConfig: string;
11
11
  functionCalls: string;
12
+ funcCallParams: string;
12
13
  };
13
14
  declare const render: (args: TemplateType) => string;
14
15
  export default render;
@@ -6,7 +6,9 @@ export const template = `
6
6
  async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{typeString:string}}}> {
7
7
  const __prompt = {{{promptCode:string}}};
8
8
  const startTime = performance.now();
9
- __messages.push(userMessage(__prompt));
9
+ const __messages: Message[] = __metadata?.messages || [];
10
+ let __toolCalls = __metadata?.toolCall ? [__metadata.toolCall] : [];
11
+ const __interruptResponse:InterruptResponseType|undefined = __metadata?.interruptResponse;
10
12
  const __tools = {{{tools}}};
11
13
 
12
14
  {{#hasResponseFormat}}
@@ -18,40 +20,52 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
18
20
  {{^hasResponseFormat}}
19
21
  const __responseFormat = undefined;
20
22
  {{/hasResponseFormat}}
21
-
23
+
22
24
  const __client = getClientWithConfig({{{clientConfig:string}}});
25
+ let responseMessage:any;
23
26
 
24
- let __completion = await __client.text({
25
- messages: __messages,
26
- tools: __tools,
27
- responseFormat: __responseFormat,
28
- });
29
-
30
- const endTime = performance.now();
31
- await statelogClient.promptCompletion({
32
- messages: __messages,
33
- completion: __completion,
34
- model: __client.getModel(),
35
- timeTaken: endTime - startTime,
36
- });
27
+ if (__toolCalls.length === 0) {
28
+ __messages.push(userMessage(__prompt));
29
+
30
+
31
+ let __completion = await __client.text({
32
+ messages: __messages,
33
+ tools: __tools,
34
+ responseFormat: __responseFormat,
35
+ });
36
+
37
+ const endTime = performance.now();
38
+ await statelogClient.promptCompletion({
39
+ messages: __messages,
40
+ completion: __completion,
41
+ model: __client.getModel(),
42
+ timeTaken: endTime - startTime,
43
+ });
44
+
45
+ if (!__completion.success) {
46
+ throw new Error(
47
+ \`Error getting response from $\{__model\}: $\{__completion.error\}\`
48
+ );
49
+ }
50
+
51
+ responseMessage = __completion.value;
52
+ __toolCalls = responseMessage.toolCalls || [];
37
53
 
38
- if (!__completion.success) {
39
- throw new Error(
40
- \`Error getting response from $\{__model\}: $\{__completion.error\}\`
41
- );
54
+ if (__toolCalls.length > 0) {
55
+ // Add assistant's response with tool calls to message history
56
+ __messages.push(assistantMessage(responseMessage.output, { toolCalls: __toolCalls }));
57
+ }
42
58
  }
43
59
 
44
- let responseMessage = __completion.value;
45
-
46
60
  // Handle function calls
47
- while (responseMessage.toolCalls.length > 0) {
48
- // Add assistant's response with tool calls to message history
49
- __messages.push(assistantMessage(responseMessage.output, { toolCalls: responseMessage.toolCalls }));
61
+ if (__toolCalls.length > 0) {
50
62
  let toolCallStartTime, toolCallEndTime;
51
63
  let haltExecution = false;
64
+ let haltToolCall = {}
65
+ let haltInterrupt:any = null;
52
66
 
53
67
  // Process each tool call
54
- for (const toolCall of responseMessage.toolCalls) {
68
+ for (const toolCall of __toolCalls) {
55
69
  {{{functionCalls:string}}}
56
70
  }
57
71
 
@@ -60,15 +74,14 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
60
74
  messages: __messages,
61
75
  model: __client.getModel(),
62
76
  });
63
- try {
64
- const obj = JSON.parse(__messages.at(-1).content);
65
- obj.__messages = __messages;
66
- obj.__nodesTraversed = __graph.getNodesTraversed();
67
- return obj;
68
- } catch (e) {
69
- return __messages.at(-1).content;
70
- }
71
- //return __messages;
77
+
78
+ __stateStack.other = {
79
+ messages: __messages.map((msg) => msg.toJSON()),
80
+ nodesTraversed: __graph.getNodesTraversed(),
81
+ toolCall: haltToolCall,
82
+ };
83
+ haltInterrupt.__state = __stateStack.toJSON();
84
+ return haltInterrupt;
72
85
  }
73
86
 
74
87
  const nextStartTime = performance.now();
@@ -114,7 +127,12 @@ async function _{{{variableName:string}}}({{{argsStr:string}}}): Promise<{{{type
114
127
  return responseMessage.output;
115
128
  {{/hasResponseFormat}}
116
129
  }
117
- `;
130
+
131
+ __self.{{{variableName:string}}} = await _{{{variableName:string}}}({{{funcCallParams:string}}});
132
+
133
+ if (isInterrupt(__self.{{{variableName:string}}})) {
134
+ return { ...state, data: __self.{{{variableName:string}}} };
135
+ }`;
118
136
  const render = (args) => {
119
137
  return apply(template, args);
120
138
  };
@@ -1,6 +1,7 @@
1
- export declare const template = "if (\n toolCall.name === \"{{{name:string}}}\"\n) {\n const args = toolCall.arguments;\n\n toolCallStartTime = performance.now();\n const result = await {{{name}}}(args);\n toolCallEndTime = performance.now();\n\n // console.log(\"Tool '{{{name:string}}}' called with arguments:\", args);\n // console.log(\"Tool '{{{name:string}}}' returned result:\", result);\n\nawait statelogClient.toolCall({\n toolName: \"{{{name:string}}}\",\n args,\n output: result,\n model: __client.getModel(),\n timeTaken: toolCallEndTime - toolCallStartTime,\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\n if (isInterrupt(result)) {\n haltExecution = true;\n break;\n }\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\nawait 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
+ paramsStr: string;
4
5
  };
5
6
  declare const render: (args: TemplateType) => string;
6
7
  export default render;
@@ -7,31 +7,45 @@ export const template = `if (
7
7
  ) {
8
8
  const args = toolCall.arguments;
9
9
 
10
+ const params = [ {{{paramsStr:string}}} ];
11
+
10
12
  toolCallStartTime = performance.now();
11
- const result = await {{{name}}}(args);
13
+
14
+ let result: any;
15
+ if (__interruptResponse && __interruptResponse.type === "reject") {
16
+ __messages.push(toolMessage("tool call rejected", {
17
+ tool_call_id: toolCall.id,
18
+ name: toolCall.name,
19
+ }));
20
+ } else {
21
+ result = await {{{name}}}(params);
22
+ }
12
23
  toolCallEndTime = performance.now();
13
24
 
14
- // console.log("Tool '{{{name:string}}}' called with arguments:", args);
15
- // console.log("Tool '{{{name:string}}}' returned result:", result);
16
-
17
25
  await statelogClient.toolCall({
18
26
  toolName: "{{{name:string}}}",
19
- args,
27
+ params,
20
28
  output: result,
21
29
  model: __client.getModel(),
22
30
  timeTaken: toolCallEndTime - toolCallStartTime,
23
31
  });
24
32
 
25
- // Add function result to messages
26
- __messages.push(toolMessage(result, {
27
- tool_call_id: toolCall.id,
28
- name: toolCall.name,
29
- }));
30
-
31
33
  if (isInterrupt(result)) {
34
+ haltInterrupt = result;
35
+ haltToolCall = {
36
+ id: toolCall.id,
37
+ name: toolCall.name,
38
+ arguments: toolCall.arguments,
39
+ }
32
40
  haltExecution = true;
33
41
  break;
34
42
  }
43
+
44
+ // Add function result to messages
45
+ __messages.push(toolMessage(result, {
46
+ tool_call_id: toolCall.id,
47
+ name: toolCall.name,
48
+ }));
35
49
  }`;
36
50
  const render = (args) => {
37
51
  return apply(template, args);
package/dist/lib/utils.js CHANGED
@@ -19,3 +19,32 @@ export function zip(arr1, arr2) {
19
19
  export function uniq(arr) {
20
20
  return Array.from(new Set(arr));
21
21
  }
22
+ class PackagedState {
23
+ messages;
24
+ nodesTraversed;
25
+ toolCall;
26
+ step;
27
+ selfState;
28
+ globalState;
29
+ args;
30
+ constructor(state, args) {
31
+ this.messages = state.messages;
32
+ this.nodesTraversed = state.graph?.getNodesTraversed();
33
+ this.toolCall = state.toolCall;
34
+ this.step = state.part;
35
+ this.selfState = JSON.parse(JSON.stringify(state.self));
36
+ this.globalState = JSON.parse(JSON.stringify(state.global));
37
+ this.args = args;
38
+ }
39
+ toJSON() {
40
+ return {
41
+ messages: this.messages,
42
+ nodesTraversed: this.nodesTraversed,
43
+ toolCall: this.toolCall,
44
+ step: this.step,
45
+ selfState: this.selfState,
46
+ globalState: this.globalState,
47
+ args: this.args,
48
+ };
49
+ }
50
+ }
@@ -77,10 +77,11 @@ function getImports(program) {
77
77
  .filter((node) => node.type === "importNodeStatement" ||
78
78
  node.type === "importToolStatement")
79
79
  .map((node) => node.agencyFile.trim());
80
- const importStatements = program.nodes
81
- .filter((node) => node.type === "importStatement")
82
- .map((node) => node.modulePath.trim());
83
- return [...toolAndNodeImports, ...importStatements];
80
+ /* const importStatements = program.nodes
81
+ .filter((node) => node.type === "importStatement")
82
+ .map((node) => (node as ImportStatement).modulePath.trim());
83
+ */
84
+ return [...toolAndNodeImports]; //, ...importStatements];
84
85
  }
85
86
  const compiledFiles = new Set();
86
87
  const dirSearched = new Set();
@@ -119,6 +120,7 @@ function compile(inputFile, _outputFile, verbose = false) {
119
120
  }
120
121
  compiledFiles.add(absoluteInputFile);
121
122
  const contents = readFile(inputFile);
123
+ console.log(`Compiling ${inputFile}...`);
122
124
  const parsedProgram = parse(contents, verbose);
123
125
  const imports = getImports(parsedProgram);
124
126
  const inputDir = path.dirname(absoluteInputFile);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agency-lang",
3
- "version": "0.0.41",
3
+ "version": "0.0.43",
4
4
  "description": "The Agency language",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -42,7 +42,7 @@
42
42
  "dependencies": {
43
43
  "egonlog": "^0.0.2",
44
44
  "piemachine": "^0.0.6",
45
- "smoltalk": "^0.0.12",
45
+ "smoltalk": "^0.0.15",
46
46
  "statelog-client": "^0.0.34",
47
47
  "tarsec": "^0.1.1",
48
48
  "typestache": "^0.4.4",
@@ -56,4 +56,4 @@
56
56
  "typescript": "^5.9.3",
57
57
  "vitest": "^4.0.16"
58
58
  }
59
- }
59
+ }