agentlang 0.10.0 → 0.10.2

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.
Files changed (107) hide show
  1. package/out/api/http.d.ts.map +1 -1
  2. package/out/api/http.js +136 -0
  3. package/out/api/http.js.map +1 -1
  4. package/out/language/generated/ast.d.ts +87 -44
  5. package/out/language/generated/ast.d.ts.map +1 -1
  6. package/out/language/generated/ast.js +65 -31
  7. package/out/language/generated/ast.js.map +1 -1
  8. package/out/language/generated/grammar.d.ts.map +1 -1
  9. package/out/language/generated/grammar.js +337 -330
  10. package/out/language/generated/grammar.js.map +1 -1
  11. package/out/language/main.cjs +396 -361
  12. package/out/language/main.cjs.map +2 -2
  13. package/out/language/parser.d.ts +11 -1
  14. package/out/language/parser.d.ts.map +1 -1
  15. package/out/language/parser.js +74 -8
  16. package/out/language/parser.js.map +1 -1
  17. package/out/language/syntax.d.ts +14 -0
  18. package/out/language/syntax.d.ts.map +1 -1
  19. package/out/language/syntax.js +60 -27
  20. package/out/language/syntax.js.map +1 -1
  21. package/out/runtime/agents/common.d.ts +2 -2
  22. package/out/runtime/agents/common.js +1 -1
  23. package/out/runtime/api.d.ts +2 -0
  24. package/out/runtime/api.d.ts.map +1 -1
  25. package/out/runtime/api.js +3 -0
  26. package/out/runtime/api.js.map +1 -1
  27. package/out/runtime/datefns.d.ts +34 -0
  28. package/out/runtime/datefns.d.ts.map +1 -0
  29. package/out/runtime/datefns.js +82 -0
  30. package/out/runtime/datefns.js.map +1 -0
  31. package/out/runtime/exec-graph.d.ts.map +1 -1
  32. package/out/runtime/exec-graph.js +22 -3
  33. package/out/runtime/exec-graph.js.map +1 -1
  34. package/out/runtime/interpreter.d.ts +9 -0
  35. package/out/runtime/interpreter.d.ts.map +1 -1
  36. package/out/runtime/interpreter.js +85 -20
  37. package/out/runtime/interpreter.js.map +1 -1
  38. package/out/runtime/module.d.ts +8 -0
  39. package/out/runtime/module.d.ts.map +1 -1
  40. package/out/runtime/module.js +23 -0
  41. package/out/runtime/module.js.map +1 -1
  42. package/out/runtime/modules/ai.d.ts +9 -8
  43. package/out/runtime/modules/ai.d.ts.map +1 -1
  44. package/out/runtime/modules/ai.js +92 -37
  45. package/out/runtime/modules/ai.js.map +1 -1
  46. package/out/runtime/modules/core.d.ts.map +1 -1
  47. package/out/runtime/modules/core.js +5 -1
  48. package/out/runtime/modules/core.js.map +1 -1
  49. package/out/runtime/monitor.d.ts +6 -0
  50. package/out/runtime/monitor.d.ts.map +1 -1
  51. package/out/runtime/monitor.js +21 -1
  52. package/out/runtime/monitor.js.map +1 -1
  53. package/out/runtime/relgraph.d.ts.map +1 -1
  54. package/out/runtime/relgraph.js +7 -3
  55. package/out/runtime/relgraph.js.map +1 -1
  56. package/out/runtime/resolvers/interface.d.ts +3 -2
  57. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  58. package/out/runtime/resolvers/interface.js +3 -2
  59. package/out/runtime/resolvers/interface.js.map +1 -1
  60. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  61. package/out/runtime/resolvers/sqldb/dbutil.js +17 -4
  62. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  63. package/out/runtime/resolvers/sqldb/impl.d.ts +1 -1
  64. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  65. package/out/runtime/resolvers/sqldb/impl.js +17 -7
  66. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  67. package/out/runtime/services/documentFetcher.js +2 -2
  68. package/out/runtime/services/documentFetcher.js.map +1 -1
  69. package/out/runtime/util.d.ts +12 -1
  70. package/out/runtime/util.d.ts.map +1 -1
  71. package/out/runtime/util.js +38 -2
  72. package/out/runtime/util.js.map +1 -1
  73. package/out/syntaxes/agentlang.monarch.js +1 -1
  74. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  75. package/out/test-harness.d.ts +36 -0
  76. package/out/test-harness.d.ts.map +1 -0
  77. package/out/test-harness.js +341 -0
  78. package/out/test-harness.js.map +1 -0
  79. package/out/utils/fs/index.d.ts +12 -2
  80. package/out/utils/fs/index.d.ts.map +1 -1
  81. package/out/utils/fs/index.js +27 -6
  82. package/out/utils/fs/index.js.map +1 -1
  83. package/package.json +4 -1
  84. package/src/api/http.ts +138 -0
  85. package/src/language/agentlang.langium +9 -10
  86. package/src/language/generated/ast.ts +98 -44
  87. package/src/language/generated/grammar.ts +337 -330
  88. package/src/language/parser.ts +87 -6
  89. package/src/language/syntax.ts +71 -24
  90. package/src/runtime/agents/common.ts +1 -1
  91. package/src/runtime/api.ts +5 -0
  92. package/src/runtime/datefns.ts +112 -0
  93. package/src/runtime/exec-graph.ts +23 -2
  94. package/src/runtime/interpreter.ts +107 -19
  95. package/src/runtime/module.ts +26 -0
  96. package/src/runtime/modules/ai.ts +110 -54
  97. package/src/runtime/modules/core.ts +5 -1
  98. package/src/runtime/monitor.ts +27 -1
  99. package/src/runtime/relgraph.ts +7 -3
  100. package/src/runtime/resolvers/interface.ts +4 -2
  101. package/src/runtime/resolvers/sqldb/dbutil.ts +20 -6
  102. package/src/runtime/resolvers/sqldb/impl.ts +17 -7
  103. package/src/runtime/services/documentFetcher.ts +2 -2
  104. package/src/runtime/util.ts +62 -2
  105. package/src/syntaxes/agentlang.monarch.ts +1 -1
  106. package/src/test-harness.ts +423 -0
  107. package/src/utils/fs/index.ts +30 -6
@@ -80,7 +80,12 @@ import {
80
80
  splitRefs,
81
81
  } from './util.js';
82
82
  import { getResolver, getResolverNameForPath } from './resolvers/registry.js';
83
- import { parseStatement, parseWorkflow } from '../language/parser.js';
83
+ import {
84
+ ExtractedQueryOptions,
85
+ extractQueryOptions,
86
+ parseStatement,
87
+ parseWorkflow,
88
+ } from '../language/parser.js';
84
89
  import { ActiveSessionInfo, AdminSession, AdminUserId } from './auth/defs.js';
85
90
  import {
86
91
  AgentEntityName,
@@ -114,6 +119,7 @@ import { Monitor, MonitorEntry } from './monitor.js';
114
119
  import { detailedDiff } from 'deep-object-diff';
115
120
  import { callMcpTool, mcpClientNameFromToolEvent } from './mcpclient.js';
116
121
  import { isNodeEnv } from '../utils/runtime.js';
122
+ import Handlebars from 'handlebars';
117
123
 
118
124
  export type Result = any;
119
125
 
@@ -126,6 +132,7 @@ export function isEmptyResult(r: Result): boolean {
126
132
  type BetweenRelInfo = {
127
133
  relationship: Relationship;
128
134
  connectedInstance: Instance;
135
+ connectedAlias?: string;
129
136
  };
130
137
 
131
138
  function mkEnvName(name: string | undefined, parent: Environment | undefined): string {
@@ -150,6 +157,7 @@ export class Environment extends Instance {
150
157
  private activeUserSet: boolean = false;
151
158
  private lastResult: Result;
152
159
  private trashedResult: Result = undefined;
160
+ private lastPattern: string | undefined;
153
161
  private returnFlag: boolean = false;
154
162
  private parentPath: string | undefined;
155
163
  private normalizedParentPath: string | undefined;
@@ -169,6 +177,7 @@ export class Environment extends Instance {
169
177
  private agentChatId: string | undefined = undefined;
170
178
  private monitor: Monitor | undefined = undefined;
171
179
  private escalatedRole: string | undefined;
180
+ private activeChatId: string | undefined;
172
181
 
173
182
  private activeUserData: any = undefined;
174
183
 
@@ -196,6 +205,7 @@ export class Environment extends Instance {
196
205
  this.agentChatId = parent.agentChatId;
197
206
  this.monitor = parent.monitor;
198
207
  this.escalatedRole = parent.escalatedRole;
208
+ this.activeChatId = parent.activeChatId;
199
209
  } else {
200
210
  this.activeModule = DefaultModuleName;
201
211
  this.activeResolvers = new Map<string, Resolver>();
@@ -322,6 +332,15 @@ export class Environment extends Instance {
322
332
  return this.attributes.get(Environment.FlowContextTag);
323
333
  }
324
334
 
335
+ setActiveChatId(chatId: string): Environment {
336
+ this.activeChatId = chatId;
337
+ return this;
338
+ }
339
+
340
+ getActiveChatId(): string | undefined {
341
+ return this.activeChatId;
342
+ }
343
+
325
344
  addToScratchPad(k: string, data: any): Environment {
326
345
  if (this.scratchPad === undefined) {
327
346
  this.scratchPad = {};
@@ -392,6 +411,11 @@ export class Environment extends Instance {
392
411
  return this;
393
412
  }
394
413
 
414
+ maybeRewriteTemplatePatterns(instruction: string, scratchPad?: any): string {
415
+ const templ = Handlebars.compile(this.rewriteTemplateMappings(instruction));
416
+ return templ(scratchPad);
417
+ }
418
+
395
419
  static SuspensionUserData = '^';
396
420
 
397
421
  bindSuspensionUserData(userData: string): Environment {
@@ -549,6 +573,15 @@ export class Environment extends Instance {
549
573
  return this.lastResult;
550
574
  }
551
575
 
576
+ setLastPattern(pattern: string | undefined): Environment {
577
+ this.lastPattern = pattern;
578
+ return this;
579
+ }
580
+
581
+ getLastPattern(): string | undefined {
582
+ return this.lastPattern;
583
+ }
584
+
552
585
  getActiveModuleName(): string {
553
586
  return this.activeModule;
554
587
  }
@@ -886,6 +919,13 @@ export class Environment extends Instance {
886
919
  return this;
887
920
  }
888
921
 
922
+ setMonitorEntryLlmTokenUsage(input: number, output: number, total: number): Environment {
923
+ if (this.monitor !== undefined) {
924
+ this.monitor.setEntryLlmTokenUsage(input, output, total);
925
+ }
926
+ return this;
927
+ }
928
+
889
929
  incrementMonitor(): Environment {
890
930
  if (this.monitor !== undefined) {
891
931
  this.monitor = this.monitor.increment();
@@ -934,7 +974,13 @@ export let evaluate = async function (
934
974
  } else if (isAgentEventInstance(eventInstance)) {
935
975
  env = new Environment(eventInstance.name + '.env', activeEnv);
936
976
  await handleAgentInvocation(eventInstance, env);
937
- if (continuation) continuation(env.getLastResult());
977
+ if (continuation) {
978
+ if (env.getLastPattern()) {
979
+ continuation({ result: env.getLastResult(), pattern: env.getLastPattern() });
980
+ } else {
981
+ continuation(env.getLastResult());
982
+ }
983
+ }
938
984
  } else if (isOpenApiEventInstance(eventInstance)) {
939
985
  env = new Environment(eventInstance.name + '.env', activeEnv);
940
986
  await handleOpenApiEvent(eventInstance, env);
@@ -1087,6 +1133,9 @@ export async function evaluateStatement(stmt: Statement, env: Environment): Prom
1087
1133
  handlersPushed = env.pushHandlers(handlers);
1088
1134
  }
1089
1135
  await evaluatePattern(stmt.pattern, env);
1136
+ if (hasHints) {
1137
+ await maybeHandleEmpty(hints, env);
1138
+ }
1090
1139
  if (hasHints) {
1091
1140
  maybeBindStatementResultToAlias(hints, env);
1092
1141
  }
@@ -1116,6 +1165,24 @@ async function maybeHandleNotFound(handlers: CatchHandlers | undefined, env: Env
1116
1165
  }
1117
1166
  }
1118
1167
 
1168
+ async function maybeHandleEmpty(hints: RuntimeHint[], env: Environment) {
1169
+ const lastResult: Result = env.getLastResult();
1170
+ if (
1171
+ lastResult === null ||
1172
+ lastResult === undefined ||
1173
+ (lastResult instanceof Array && lastResult.length == 0)
1174
+ ) {
1175
+ for (const rh of hints) {
1176
+ if (rh.emptySpec) {
1177
+ const newEnv = new Environment('empty-env', env).unsetEventExecutor();
1178
+ await evaluateStatement(rh.emptySpec.stmt, newEnv);
1179
+ env.setLastResult(newEnv.getLastResult());
1180
+ break;
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+
1119
1186
  async function maybeHandleError(
1120
1187
  handlers: CatchHandlers | undefined,
1121
1188
  reason: any,
@@ -1497,17 +1564,18 @@ async function maybeValidateOneOfRefs(inst: Instance, env: Environment) {
1497
1564
  }
1498
1565
  }
1499
1566
 
1500
- function maybeSetQueryClauses(inst: Instance, crud: CrudMap) {
1501
- if (crud.groupByClause) {
1502
- inst.setGroupBy(crud.groupByClause.colNames);
1567
+ function maybeSetQueryClauses(inst: Instance, qopts: ExtractedQueryOptions) {
1568
+ if (qopts.groupByClause) {
1569
+ inst.setGroupBy(qopts.groupByClause.colNames);
1503
1570
  }
1504
- if (crud.orderByClause) {
1505
- inst.setOrderBy(crud.orderByClause.colNames, crud.orderByClause.order === '@desc');
1571
+ if (qopts.orderByClause) {
1572
+ inst.setOrderBy(qopts.orderByClause.colNames, qopts.orderByClause.order === '@desc');
1506
1573
  }
1507
1574
  }
1508
1575
 
1509
1576
  async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1510
- if (!env.isInUpsertMode() && crud.upsert.length > 0) {
1577
+ const qopts = extractQueryOptions(crud);
1578
+ if (!env.isInUpsertMode() && qopts.upsert !== undefined) {
1511
1579
  return await evaluateUpsert(crud, env);
1512
1580
  }
1513
1581
  const inst: Instance = crud.source
@@ -1519,12 +1587,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1519
1587
  const qattrs = inst.queryAttributes;
1520
1588
  const onlyAggregates = inst.aggregates !== undefined && qattrs === undefined;
1521
1589
  const isQueryAll = onlyAggregates || crud.name.endsWith(QuerySuffix);
1522
- const distinct: boolean = crud.distinct.length > 0;
1523
- maybeSetQueryClauses(inst, crud);
1590
+ const distinct: boolean = qopts.distinct !== undefined;
1591
+ maybeSetQueryClauses(inst, qopts);
1524
1592
  if (attrs.size > 0) {
1525
1593
  await maybeValidateOneOfRefs(inst, env);
1526
1594
  }
1527
- if (crud.into) {
1595
+ if (qopts.into) {
1528
1596
  if (attrs.size > 0) {
1529
1597
  throw new Error(
1530
1598
  `Query pattern for ${entryName} with 'into' clause cannot be used to update attributes`
@@ -1533,10 +1601,16 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1533
1601
  if (qattrs === undefined && !isQueryAll) {
1534
1602
  throw new Error(`Pattern for ${entryName} with 'into' clause must be a query`);
1535
1603
  }
1536
- if (crud.joins.length > 0) {
1537
- await evaluateJoinQuery(crud.joins, crud.into, crud.where, inst, distinct, env);
1604
+ if (qopts.joins && qopts.joins.length > 0) {
1605
+ await evaluateJoinQuery(qopts.joins, qopts.into, qopts.where, inst, distinct, env);
1538
1606
  } else {
1539
- await evaluateJoinQueryWithRelationships(crud.into, inst, crud.relationships, distinct, env);
1607
+ await evaluateJoinQueryWithRelationships(
1608
+ qopts.into,
1609
+ inst,
1610
+ crud.relationships || [],
1611
+ distinct,
1612
+ env
1613
+ );
1540
1614
  }
1541
1615
  return;
1542
1616
  }
@@ -1634,7 +1708,8 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1634
1708
  const insts: Instance[] = await res.queryConnectedInstances(
1635
1709
  betRelInfo.relationship,
1636
1710
  betRelInfo.connectedInstance,
1637
- inst
1711
+ inst,
1712
+ betRelInfo.connectedAlias
1638
1713
  );
1639
1714
  env.setLastResult(insts);
1640
1715
  } else {
@@ -1659,7 +1734,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1659
1734
  false,
1660
1735
  true
1661
1736
  );
1662
- env.setLastResult(inst);
1737
+ env.setLastResult([inst]);
1663
1738
  } else {
1664
1739
  const insts: Instance[] = await res.queryInstances(inst, isQueryAll, distinct);
1665
1740
  env.setLastResult(insts);
@@ -1686,7 +1761,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1686
1761
  await evaluatePattern(rel.pattern, newEnv);
1687
1762
  lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
1688
1763
  } else if (isBetweenRelationship(rel.name, moduleName)) {
1689
- newEnv.setBetweenRelInfo({ relationship: relEntry, connectedInstance: lastRes[j] });
1764
+ const connAlias = relEntry.isSelfReferencing() ? relEntry.node1.alias : undefined;
1765
+ newEnv.setBetweenRelInfo({
1766
+ relationship: relEntry,
1767
+ connectedInstance: lastRes[j],
1768
+ connectedAlias: connAlias,
1769
+ });
1690
1770
  await evaluatePattern(rel.pattern, newEnv);
1691
1771
  lastRes[j].attachRelatedInstances(rel.name, newEnv.getLastResult());
1692
1772
  }
@@ -1722,7 +1802,7 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1722
1802
  await runPreUpdateEvents(lastRes, env);
1723
1803
  const finalInst: Instance = await res.updateInstance(lastRes, attrs);
1724
1804
  await runPostUpdateEvents(finalInst, lastRes, env);
1725
- env.setLastResult(finalInst);
1805
+ env.setLastResult([finalInst]);
1726
1806
  }
1727
1807
  }
1728
1808
  }
@@ -2023,6 +2103,11 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
2023
2103
  while (true) {
2024
2104
  try {
2025
2105
  let rs: string = result ? normalizeGeneratedCode(result) : '';
2106
+ if (agent.tools) {
2107
+ env.setLastPattern(rs);
2108
+ } else {
2109
+ env.setLastPattern(undefined);
2110
+ }
2026
2111
  let isWf = rs.startsWith('workflow');
2027
2112
  if (isWf && !agent.runWorkflows) {
2028
2113
  await parseWorkflow(rs);
@@ -2116,6 +2201,10 @@ export async function handleAgentInvocation(
2116
2201
  env: Environment
2117
2202
  ): Promise<void> {
2118
2203
  const agent: AgentInstance = await findAgentByName(agentEventInst.name, env);
2204
+ const chatId = agentEventInst.lookup('chatId');
2205
+ if (chatId) {
2206
+ env.setActiveChatId(chatId);
2207
+ }
2119
2208
  const origMsg: any =
2120
2209
  agentEventInst.lookup('message') || JSON.stringify(agentEventInst.asObject());
2121
2210
  const msg: string = isString(origMsg) ? origMsg : maybeInstanceAsString(origMsg);
@@ -2125,7 +2214,6 @@ export async function handleAgentInvocation(
2125
2214
  } else {
2126
2215
  const mode = agentEventInst.lookup('mode');
2127
2216
  let activeEnv = env;
2128
- const chatId = agentEventInst.lookup('chatId');
2129
2217
  if (chatId !== undefined) {
2130
2218
  activeEnv.setAgentChatId(chatId);
2131
2219
  }
@@ -1409,6 +1409,10 @@ export class Relationship extends Record {
1409
1409
  return this.relType == RelType.BETWEEN;
1410
1410
  }
1411
1411
 
1412
+ isSelfReferencing(): boolean {
1413
+ return this.node1.path.asFqName() === this.node2.path.asFqName();
1414
+ }
1415
+
1412
1416
  parentNode(): RelationshipNode {
1413
1417
  return this.node1;
1414
1418
  }
@@ -1502,6 +1506,28 @@ export class Relationship extends Record {
1502
1506
  }
1503
1507
  }
1504
1508
 
1509
+ /**
1510
+ * For self-referencing relationships, resolve the alias for the connected instance
1511
+ * based on an explicit connectedAlias. Falls back to fqName-based lookup for
1512
+ * non-self-referencing relationships.
1513
+ */
1514
+ getAliasForConnected(inst: Instance, connectedAlias?: string): string {
1515
+ if (connectedAlias && this.isSelfReferencing()) {
1516
+ return connectedAlias;
1517
+ }
1518
+ return this.getAliasFor(inst);
1519
+ }
1520
+
1521
+ getInverseAliasForConnected(inst: Instance, connectedAlias?: string): string {
1522
+ if (connectedAlias && this.isSelfReferencing()) {
1523
+ if (connectedAlias === this.node1.alias) {
1524
+ return this.node2.alias;
1525
+ }
1526
+ return this.node1.alias;
1527
+ }
1528
+ return this.getInverseAliasFor(inst);
1529
+ }
1530
+
1505
1531
  isParent(inst: Instance): boolean {
1506
1532
  return inst.getFqName() == this.node1.path.asFqName();
1507
1533
  }
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  DefaultModuleName,
3
3
  escapeSpecialChars,
4
+ extractAndRemoveAllXmlTaggedText,
5
+ ExtractedCode,
6
+ ExtractedText,
7
+ extractFencedCodeBlocks,
4
8
  isFqName,
5
9
  isString,
6
10
  makeCoreModuleName,
@@ -66,7 +70,6 @@ import {
66
70
  } from '../agents/common.js';
67
71
  import { logger } from '../logger.js';
68
72
  import { FlowStep } from '../agents/flows.js';
69
- import Handlebars from 'handlebars';
70
73
  import { Statement } from '../../language/generated/ast.js';
71
74
  import { isMonitoringEnabled, TtlCache } from '../state.js';
72
75
  import { isNodeEnv } from '../../utils/runtime.js';
@@ -103,6 +106,7 @@ entity ${AgentEntityName} {
103
106
  flows String @optional,
104
107
  validate String @optional,
105
108
  retry String @optional,
109
+ saveResponseAs String @optional,
106
110
  llm String
107
111
  }
108
112
 
@@ -319,6 +323,7 @@ export class AgentInstance {
319
323
  flows: string | undefined;
320
324
  validate: string | undefined;
321
325
  retry: string | undefined;
326
+ saveResponseAs: string | undefined;
322
327
  stateless: boolean = false;
323
328
  private toolsArray: string[] | undefined = undefined;
324
329
  private hasModuleTools = false;
@@ -580,7 +585,7 @@ export class AgentInstance {
580
585
  }
581
586
  }
582
587
 
583
- private async getFullInstructions(
588
+ private async getFullInstructionsHelper(
584
589
  env: Environment,
585
590
  activator: AgentInstructionActivator
586
591
  ): Promise<string> {
@@ -641,7 +646,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
641
646
  const spad = env.getScratchPad();
642
647
  if (spad !== undefined && Object.keys(spad).length > 0) {
643
648
  if (finalInstruction.indexOf('{{') > 0) {
644
- return AgentInstance.maybeRewriteTemplatePatterns(spad, finalInstruction, env);
649
+ return env.maybeRewriteTemplatePatterns(finalInstruction, spad);
645
650
  } else {
646
651
  const ctx = JSON.stringify(spad);
647
652
  return `${finalInstruction}\nSome additional context:\n${ctx}`;
@@ -652,13 +657,36 @@ Only return a pure JSON object with no extra text, annotations etc.`;
652
657
  }
653
658
  }
654
659
 
655
- private static maybeRewriteTemplatePatterns(
656
- scratchPad: any,
657
- instruction: string,
658
- env: Environment
659
- ): string {
660
- const templ = Handlebars.compile(env.rewriteTemplateMappings(instruction));
661
- return templ(scratchPad);
660
+ private static UserTag = 'user';
661
+ private static FileTag = 'file';
662
+
663
+ private async getFullInstructions(
664
+ env: Environment,
665
+ activator: AgentInstructionActivator
666
+ ): Promise<ExtractedText> {
667
+ const finalInstruction = await this.getFullInstructionsHelper(env, activator);
668
+ if (finalInstruction.indexOf(`<${AgentInstance.UserTag}>`) > 0) {
669
+ return extractAndRemoveAllXmlTaggedText(finalInstruction, AgentInstance.UserTag);
670
+ } else {
671
+ return { extracted: undefined, updatedText: finalInstruction };
672
+ }
673
+ }
674
+
675
+ private async maybeFillInFileContents(instructions: string, env: Environment): Promise<string> {
676
+ const chatId = env.getActiveChatId();
677
+ const FC = '$FC';
678
+ if (chatId && instructions.indexOf(`<${AgentInstance.FileTag}>`) > 0) {
679
+ const ext = extractAndRemoveAllXmlTaggedText(instructions, AgentInstance.FileTag, FC);
680
+ if (ext.extracted) {
681
+ instructions = ext.updatedText;
682
+ for (let i = 0; i < ext.extracted.length; ++i) {
683
+ const fileName = ext.extracted[i];
684
+ const contents = await loadLocalAgentResult(chatId, fileName);
685
+ if (contents) instructions = instructions.replace(FC, contents);
686
+ }
687
+ }
688
+ }
689
+ return instructions;
662
690
  }
663
691
 
664
692
  maybeValidateJsonResponse(response: string | undefined): object | undefined {
@@ -820,6 +848,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
820
848
  const sess: Instance | null = this.withSession ? await findAgentChatSession(chatId, env) : null;
821
849
  let msgs: BaseMessage[] | undefined;
822
850
  let cachedMsg: string | undefined = undefined;
851
+ let extractedText: ExtractedText | undefined;
823
852
  const activator: AgentInstructionActivator = {
824
853
  provider: p,
825
854
  userMessage: message,
@@ -829,7 +858,8 @@ Only return a pure JSON object with no extra text, annotations etc.`;
829
858
  if (sess) {
830
859
  msgs = sess.lookup('messages');
831
860
  } else {
832
- cachedMsg = await this.getFullInstructions(env, activator);
861
+ extractedText = await this.getFullInstructions(env, activator);
862
+ cachedMsg = extractedText.updatedText;
833
863
  msgs = [systemMessage(cachedMsg || '')];
834
864
  }
835
865
  if (msgs) {
@@ -844,12 +874,22 @@ Only return a pure JSON object with no extra text, annotations etc.`;
844
874
  ? EvalInstructions
845
875
  : LearnerAgentInstructions;
846
876
  const ts = this.toolsAsString();
847
- const msg = `${s}\n${ts}\n${cachedMsg || (await this.getFullInstructions(env, activator))}`;
877
+ let tmpMsg = cachedMsg;
878
+ if (!tmpMsg) {
879
+ extractedText = await this.getFullInstructions(env, activator);
880
+ tmpMsg = extractedText.updatedText;
881
+ }
882
+ const msg = `${s}\n${ts}\n${tmpMsg}`;
848
883
  const newSysMsg = systemMessage(msg);
849
884
  msgs[0] = newSysMsg;
850
885
  }
886
+ let tmpMsg = message;
887
+ if (extractedText?.extracted) {
888
+ const s = await this.maybeFillInFileContents(extractedText.extracted.join('\n'), env);
889
+ tmpMsg = `${tmpMsg}\n${s}`;
890
+ }
851
891
  const hmsg = await this.maybeAddRelevantDocuments(
852
- this.maybeAddFlowContext(message, env),
892
+ this.maybeAddFlowContext(tmpMsg, env),
853
893
  env
854
894
  );
855
895
  if (hmsg.length > 0) {
@@ -898,6 +938,13 @@ Only return a pure JSON object with no extra text, annotations etc.`;
898
938
  await saveAgentChatSession(chatId, msgs, env);
899
939
  }
900
940
  if (monitoringEnabled) env.setMonitorEntryLlmResponse(response.content);
941
+ if (monitoringEnabled && response.sysMsg.usage_metadata) {
942
+ const u = response.sysMsg.usage_metadata;
943
+ env.setMonitorEntryLlmTokenUsage(u.input_tokens, u.output_tokens, u.total_tokens);
944
+ }
945
+ if (this.saveResponseAs) {
946
+ await saveAgentResponse(this.saveResponseAs, response.content, env);
947
+ }
901
948
  env.setLastResult(response.content);
902
949
  } catch (err: any) {
903
950
  logger.error(`Error while invoking ${agentName} - ${err}`);
@@ -1299,26 +1346,6 @@ function processScenarioResponse(resp: string): string {
1299
1346
  return resp;
1300
1347
  }
1301
1348
 
1302
- type ExtractedCode = {
1303
- language: string | null;
1304
- code: string;
1305
- };
1306
-
1307
- export function extractFencedCodeBlocks(markdown: string): ExtractedCode[] {
1308
- const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
1309
- const blocks: ExtractedCode[] = [];
1310
- let match;
1311
-
1312
- while ((match = codeBlockRegex.exec(markdown)) !== null) {
1313
- blocks.push({
1314
- language: match[1] || null,
1315
- code: match[2],
1316
- });
1317
- }
1318
-
1319
- return blocks;
1320
- }
1321
-
1322
1349
  export function normalizeGeneratedCode(code: string | undefined): string {
1323
1350
  if (code !== undefined) {
1324
1351
  const blocks = extractFencedCodeBlocks(code);
@@ -1439,15 +1466,9 @@ export async function saveFlowStepResultLocally(
1439
1466
  chatId: string,
1440
1467
  step: string,
1441
1468
  result: string,
1442
- suspensionId: string
1443
- ): Promise<Instance | undefined> {
1469
+ suspensionId?: string | undefined
1470
+ ): Promise<string> {
1444
1471
  const fs = await getFileSystem();
1445
- const attrs = newInstanceAttributes()
1446
- .set('chatId', chatId)
1447
- .set('step', step)
1448
- .set('result', result)
1449
- .set('suspensionId', suspensionId);
1450
- const inst = makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
1451
1472
  const rootDirName = LocalFlowStepsRootDirName;
1452
1473
  if (!(await fs.exists(rootDirName))) {
1453
1474
  await fs.mkdir(rootDirName);
@@ -1460,8 +1481,18 @@ export async function saveFlowStepResultLocally(
1460
1481
  if (await fs.exists(fileName)) {
1461
1482
  await fs.unlink(fileName);
1462
1483
  }
1463
- await fs.writeFile(fileName, JSON.stringify(inst.attributesAsObject(true)));
1464
- return inst;
1484
+ let s = result;
1485
+ if (suspensionId) {
1486
+ const attrs = newInstanceAttributes()
1487
+ .set('chatId', chatId)
1488
+ .set('step', step)
1489
+ .set('result', result)
1490
+ .set('suspensionId', suspensionId);
1491
+ const inst = makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
1492
+ s = JSON.stringify(inst.attributesAsObject(true));
1493
+ }
1494
+ await fs.writeFile(fileName, s);
1495
+ return s;
1465
1496
  }
1466
1497
 
1467
1498
  export async function saveFlowStepResult(
@@ -1470,9 +1501,10 @@ export async function saveFlowStepResult(
1470
1501
  result: string,
1471
1502
  suspensionId: string,
1472
1503
  env: Environment
1473
- ): Promise<Instance | undefined> {
1504
+ ): Promise<boolean> {
1474
1505
  if (LocalAgentgFlow) {
1475
- return await saveFlowStepResultLocally(chatId, step, result, suspensionId);
1506
+ await saveFlowStepResultLocally(chatId, step, result, suspensionId);
1507
+ return true;
1476
1508
  } else {
1477
1509
  const t = `${CoreAIModuleName}/${AgentFlowStep}`;
1478
1510
  try {
@@ -1486,11 +1518,11 @@ export async function saveFlowStepResult(
1486
1518
  undefined,
1487
1519
  env
1488
1520
  );
1489
- if (isInstanceOfType(inst, t)) return inst;
1490
- else return undefined;
1521
+ if (isInstanceOfType(inst, t)) return true;
1522
+ else return false;
1491
1523
  } catch (reason: any) {
1492
1524
  logger.error(`failed to save flow result for step ${step} - ${reason}`);
1493
- return undefined;
1525
+ return false;
1494
1526
  }
1495
1527
  }
1496
1528
  }
@@ -1528,10 +1560,10 @@ export async function loadFlowStepResults(chatId: string): Promise<Instance[]> {
1528
1560
  }
1529
1561
  }
1530
1562
 
1531
- export async function loadLocalFlowStep(
1563
+ export async function loadLocalAgentResult(
1532
1564
  chatId: string,
1533
1565
  step: string
1534
- ): Promise<Instance | undefined> {
1566
+ ): Promise<string | undefined> {
1535
1567
  const fs = await getFileSystem();
1536
1568
  const dirName = `${LocalFlowStepsRootDirName}/${chatId}`;
1537
1569
  if (await fs.exists(dirName)) {
@@ -1539,10 +1571,7 @@ export async function loadLocalFlowStep(
1539
1571
  for (let i = 0; i < fileNames.length; ++i) {
1540
1572
  const fileName = fileNames[i];
1541
1573
  if (fileName === step) {
1542
- const attrs = objectToInstanceAttributes(
1543
- JSON.parse(await fs.readFile(`${dirName}/${fileName}`))
1544
- );
1545
- return makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
1574
+ return await fs.readFile(`${dirName}/${fileName}`);
1546
1575
  }
1547
1576
  }
1548
1577
  return undefined;
@@ -1551,6 +1580,19 @@ export async function loadLocalFlowStep(
1551
1580
  }
1552
1581
  }
1553
1582
 
1583
+ export async function loadLocalFlowStep(
1584
+ chatId: string,
1585
+ step: string
1586
+ ): Promise<Instance | undefined> {
1587
+ const contents = await loadLocalAgentResult(chatId, step);
1588
+ if (contents) {
1589
+ const attrs = objectToInstanceAttributes(JSON.parse(contents));
1590
+ return makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
1591
+ } else {
1592
+ return undefined;
1593
+ }
1594
+ }
1595
+
1554
1596
  export async function loadFlowStep(chatId: string, step: string): Promise<Instance | undefined> {
1555
1597
  if (LocalAgentgFlow) {
1556
1598
  return await loadLocalFlowStep(chatId, step);
@@ -1630,3 +1672,17 @@ export async function fetchAndCreateDocument(
1630
1672
  throw new Error(`Failed to fetch document: ${title} from ${url}`);
1631
1673
  }
1632
1674
  }
1675
+
1676
+ export async function saveAgentResponse(
1677
+ fileName: string,
1678
+ response: string,
1679
+ env: Environment
1680
+ ): Promise<string> {
1681
+ const chatId = env.getActiveChatId();
1682
+ if (chatId) {
1683
+ await saveFlowStepResultLocally(chatId, fileName, response);
1684
+ } else {
1685
+ logger.warn(`No chatId set, ${fileName} was not saved.`);
1686
+ }
1687
+ return response;
1688
+ }
@@ -749,5 +749,9 @@ export function migrationDowns(inst: Instance): string[] | undefined {
749
749
 
750
750
  export async function doRawQuery(q: any): Promise<any> {
751
751
  const qs = objectToQueryPattern(q);
752
- return await parseAndEvaluateStatement(qs);
752
+ const result = await parseAndEvaluateStatement(qs);
753
+ return {
754
+ query: qs,
755
+ result: result.map((res: any) => (res.attributes ? Object.fromEntries(res.attributes) : res)),
756
+ };
753
757
  }