agentlang 0.9.11 → 0.10.1

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 (48) hide show
  1. package/out/language/generated/ast.d.ts +60 -42
  2. package/out/language/generated/ast.d.ts.map +1 -1
  3. package/out/language/generated/ast.js +45 -31
  4. package/out/language/generated/ast.js.map +1 -1
  5. package/out/language/generated/grammar.d.ts.map +1 -1
  6. package/out/language/generated/grammar.js +294 -327
  7. package/out/language/generated/grammar.js.map +1 -1
  8. package/out/language/main.cjs +336 -358
  9. package/out/language/main.cjs.map +2 -2
  10. package/out/language/parser.d.ts +11 -1
  11. package/out/language/parser.d.ts.map +1 -1
  12. package/out/language/parser.js +56 -6
  13. package/out/language/parser.js.map +1 -1
  14. package/out/language/syntax.d.ts +8 -0
  15. package/out/language/syntax.d.ts.map +1 -1
  16. package/out/language/syntax.js +11 -2
  17. package/out/language/syntax.js.map +1 -1
  18. package/out/runtime/agents/common.d.ts +2 -2
  19. package/out/runtime/agents/common.js +1 -1
  20. package/out/runtime/interpreter.d.ts.map +1 -1
  21. package/out/runtime/interpreter.js +104 -87
  22. package/out/runtime/interpreter.js.map +1 -1
  23. package/out/runtime/modules/ai.d.ts +2 -5
  24. package/out/runtime/modules/ai.d.ts.map +1 -1
  25. package/out/runtime/modules/ai.js +28 -19
  26. package/out/runtime/modules/ai.js.map +1 -1
  27. package/out/runtime/services/documentFetcher.js +2 -2
  28. package/out/runtime/services/documentFetcher.js.map +1 -1
  29. package/out/runtime/util.d.ts +10 -0
  30. package/out/runtime/util.d.ts.map +1 -1
  31. package/out/runtime/util.js +27 -2
  32. package/out/runtime/util.js.map +1 -1
  33. package/out/utils/fs/index.d.ts +12 -2
  34. package/out/utils/fs/index.d.ts.map +1 -1
  35. package/out/utils/fs/index.js +27 -6
  36. package/out/utils/fs/index.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/language/agentlang.langium +6 -9
  39. package/src/language/generated/ast.ts +66 -43
  40. package/src/language/generated/grammar.ts +294 -327
  41. package/src/language/parser.ts +65 -4
  42. package/src/language/syntax.ts +21 -2
  43. package/src/runtime/agents/common.ts +1 -1
  44. package/src/runtime/interpreter.ts +111 -84
  45. package/src/runtime/modules/ai.ts +35 -25
  46. package/src/runtime/services/documentFetcher.ts +2 -2
  47. package/src/runtime/util.ts +45 -2
  48. package/src/utils/fs/index.ts +30 -6
@@ -8,6 +8,7 @@ import {
8
8
  ForEach,
9
9
  FullTextSearch,
10
10
  Group,
11
+ GroupByClause,
11
12
  Handler,
12
13
  If,
13
14
  isExpr,
@@ -17,20 +18,24 @@ import {
17
18
  isNotExpr,
18
19
  isPrimExpr,
19
20
  isWorkflowDefinition,
21
+ JoinSpec,
20
22
  Literal,
21
23
  MapEntry,
22
24
  MapLiteral,
23
25
  ModuleDefinition,
24
26
  NegExpr,
25
27
  NotExpr,
28
+ OrderByClause,
26
29
  Pattern,
27
30
  PrimExpr,
31
+ QueryOption,
28
32
  RelationshipPattern,
29
33
  Return,
30
34
  SelectIntoEntry,
31
35
  SelectIntoSpec,
32
36
  SetAttribute,
33
37
  Statement,
38
+ WhereSpec,
34
39
  WorkflowDefinition,
35
40
  } from './generated/ast.js';
36
41
  import { firstAliasSpec, firstCatchSpec, isString, QuerySuffix } from '../runtime/util.js';
@@ -44,6 +49,7 @@ import {
44
49
  FunctionCallPattern,
45
50
  GroupExpressionPattern,
46
51
  IfPattern,
52
+ JoinPattern,
47
53
  LiteralPattern,
48
54
  NegExpressionPattern,
49
55
  NotExpressionPattern,
@@ -267,8 +273,9 @@ function introspectPattern(pat: Pattern): BasePattern {
267
273
  } else {
268
274
  r = introspectCreatePattern(pat.crudMap);
269
275
  }
270
- if (pat.crudMap.into) {
271
- r = introspectInto(pat.crudMap.into, r as CrudPattern);
276
+ const opts = extractQueryOptions(pat.crudMap);
277
+ if (opts.into) {
278
+ r = introspectInto(opts.into, r as CrudPattern);
272
279
  }
273
280
  } else if (pat.expr) {
274
281
  r = introspectExpression(pat.expr);
@@ -358,9 +365,20 @@ function introspectQueryPattern(crudMap: CrudMap): CrudPattern {
358
365
  crudMap.body?.attributes.forEach((sa: SetAttribute) => {
359
366
  cp.addAttribute(sa.name, introspectExpression(sa.value), sa.op);
360
367
  });
361
- crudMap.relationships.forEach((rp: RelationshipPattern) => {
368
+ const opts = extractQueryOptions(crudMap);
369
+ crudMap.relationships?.forEach((rp: RelationshipPattern) => {
362
370
  cp.addRelationship(rp.name, introspectPattern(rp.pattern) as CrudPattern | CrudPattern[]);
363
371
  });
372
+ opts.joins?.forEach((js: JoinSpec) => {
373
+ const jp: JoinPattern = {
374
+ type: js.type,
375
+ targetEntity: js.name,
376
+ conditionLhs: js.lhs,
377
+ conditionOperator: js.op ? js.op : '=',
378
+ conditionRhs: js.rhs,
379
+ };
380
+ cp.joins.push(jp);
381
+ });
364
382
  cp.isCreate = false;
365
383
  cp.isQueryUpdate = false;
366
384
  cp.isQuery = true;
@@ -369,6 +387,49 @@ function introspectQueryPattern(crudMap: CrudMap): CrudPattern {
369
387
  throw new Error(`Failed to introspect query-pattern: ${crudMap}`);
370
388
  }
371
389
 
390
+ export type ExtractedQueryOptions = {
391
+ joins: JoinSpec[] | undefined;
392
+ into: SelectIntoSpec | undefined;
393
+ where: WhereSpec | undefined;
394
+ groupByClause: GroupByClause | undefined;
395
+ orderByClause: OrderByClause | undefined;
396
+ upsert: '@upsert' | undefined;
397
+ distinct: '@distinct' | undefined;
398
+ };
399
+
400
+ export function extractQueryOptions(crudMap: CrudMap): ExtractedQueryOptions {
401
+ const r: ExtractedQueryOptions = {
402
+ joins: undefined,
403
+ into: undefined,
404
+ where: undefined,
405
+ groupByClause: undefined,
406
+ orderByClause: undefined,
407
+ upsert: undefined,
408
+ distinct: undefined,
409
+ };
410
+ crudMap.queryOptions.forEach((qo: QueryOption) => {
411
+ if (qo.join) {
412
+ if (r.joins === undefined) {
413
+ r.joins = new Array<JoinSpec>();
414
+ }
415
+ r.joins.push(qo.join);
416
+ } else if (qo.into) {
417
+ r.into = qo.into;
418
+ } else if (qo.where) {
419
+ r.where = qo.where;
420
+ } else if (qo.groupByClause) {
421
+ r.groupByClause = qo.groupByClause;
422
+ } else if (qo.orderByClause) {
423
+ r.orderByClause = qo.orderByClause;
424
+ } else if (qo.upsert) {
425
+ r.upsert = qo.upsert;
426
+ } else if (qo.distinct) {
427
+ r.distinct = qo.distinct;
428
+ }
429
+ });
430
+ return r;
431
+ }
432
+
372
433
  function introspectCreatePattern(crudMap: CrudMap): CrudPattern {
373
434
  if (crudMap) {
374
435
  const cp: CrudPattern = new CrudPattern(crudMap.name);
@@ -381,7 +442,7 @@ function introspectCreatePattern(crudMap: CrudMap): CrudPattern {
381
442
  }
382
443
  cp.addAttribute(sa.name, introspectExpression(sa.value), sa.op);
383
444
  });
384
- crudMap.relationships.forEach((rp: RelationshipPattern) => {
445
+ crudMap.relationships?.forEach((rp: RelationshipPattern) => {
385
446
  cp.addRelationship(rp.name, introspectPattern(rp.pattern) as CrudPattern | CrudPattern[]);
386
447
  });
387
448
  cp.isQueryUpdate = qup;
@@ -373,10 +373,24 @@ export type AttributePattern = {
373
373
  value: BasePattern;
374
374
  };
375
375
 
376
+ export type JoinPattern = {
377
+ type: '@join' | '@inner_join' | '@left_join' | '@right_join' | '@full_join';
378
+ targetEntity: string;
379
+ conditionLhs: string;
380
+ conditionOperator: string;
381
+ conditionRhs: string;
382
+ };
383
+
384
+ function joinPatternToString(jp: JoinPattern): string {
385
+ const opr = jp.conditionOperator === '=' ? '' : jp.conditionOperator;
386
+ return `${jp.type} ${jp.targetEntity} {${jp.conditionLhs}${opr} ${jp.conditionRhs}}`;
387
+ }
388
+
376
389
  export class CrudPattern extends BasePattern {
377
390
  recordName: string;
378
391
  attributes: Array<AttributePattern>;
379
392
  relationships: Map<string, CrudPattern[] | CrudPattern> | undefined;
393
+ joins: JoinPattern[];
380
394
  into: Map<string, string> | undefined;
381
395
  isQuery: boolean = false;
382
396
  isQueryUpdate: boolean = false;
@@ -391,6 +405,7 @@ export class CrudPattern extends BasePattern {
391
405
  } else {
392
406
  this.isCreate = true;
393
407
  }
408
+ this.joins = new Array<JoinPattern>();
394
409
  }
395
410
 
396
411
  addAttribute(n: string, p: BasePattern, op?: string): CrudPattern {
@@ -521,11 +536,15 @@ export class CrudPattern extends BasePattern {
521
536
  let s = `{${this.recordName} ${this.attributesAsString()}`;
522
537
  const rs = this.relationshipsAsString();
523
538
  if (rs) {
524
- s = s.concat(`,${rs}`);
539
+ s = s.concat(`,\n${rs}`);
540
+ }
541
+ if (this.joins.length > 0) {
542
+ const js = this.joins.map(joinPatternToString);
543
+ s = s.concat(`,\n${js.join(',\n')}`);
525
544
  }
526
545
  const ins = this.intoAsString();
527
546
  if (ins) {
528
- s = s.concat(`,${ins}`);
547
+ s = s.concat(`,\n${ins}`);
529
548
  }
530
549
  return s.concat('}', this.hintsAsString());
531
550
  }
@@ -209,7 +209,7 @@ A pattern may execute asynchronously and its eventual result can be handled by p
209
209
  If you are instructed that a particular event will be called asynchronously, always provide the patterns that follows in its '@then' clause. You must add the
210
210
  '@then' clause only if an event's documentation or instruction explicitly requires to do so.
211
211
 
212
- Earlier we discussed teh concept of 'between' relationship. Here's the 'CreateEmployee' workflow updated to create the Employee with the his/her Profile attached:
212
+ Earlier we discussed the concept of 'between' relationship. Here's the 'CreateEmployee' workflow updated to create the Employee with the his/her Profile attached:
213
213
 
214
214
  workflow CreateEmployee {
215
215
  {Erp/Employee {firstName CreateEmployee.firstName,
@@ -32,6 +32,7 @@ import {
32
32
  WhereSpec,
33
33
  } from '../language/generated/ast.js';
34
34
  import {
35
+ Agent,
35
36
  defineAgentEvent,
36
37
  Event,
37
38
  getOneOfRef,
@@ -79,7 +80,12 @@ import {
79
80
  splitRefs,
80
81
  } from './util.js';
81
82
  import { getResolver, getResolverNameForPath } from './resolvers/registry.js';
82
- import { parseStatement, parseWorkflow } from '../language/parser.js';
83
+ import {
84
+ ExtractedQueryOptions,
85
+ extractQueryOptions,
86
+ parseStatement,
87
+ parseWorkflow,
88
+ } from '../language/parser.js';
83
89
  import { ActiveSessionInfo, AdminSession, AdminUserId } from './auth/defs.js';
84
90
  import {
85
91
  AgentEntityName,
@@ -1496,17 +1502,18 @@ async function maybeValidateOneOfRefs(inst: Instance, env: Environment) {
1496
1502
  }
1497
1503
  }
1498
1504
 
1499
- function maybeSetQueryClauses(inst: Instance, crud: CrudMap) {
1500
- if (crud.groupByClause) {
1501
- inst.setGroupBy(crud.groupByClause.colNames);
1505
+ function maybeSetQueryClauses(inst: Instance, qopts: ExtractedQueryOptions) {
1506
+ if (qopts.groupByClause) {
1507
+ inst.setGroupBy(qopts.groupByClause.colNames);
1502
1508
  }
1503
- if (crud.orderByClause) {
1504
- inst.setOrderBy(crud.orderByClause.colNames, crud.orderByClause.order === '@desc');
1509
+ if (qopts.orderByClause) {
1510
+ inst.setOrderBy(qopts.orderByClause.colNames, qopts.orderByClause.order === '@desc');
1505
1511
  }
1506
1512
  }
1507
1513
 
1508
1514
  async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1509
- if (!env.isInUpsertMode() && crud.upsert.length > 0) {
1515
+ const qopts = extractQueryOptions(crud);
1516
+ if (!env.isInUpsertMode() && qopts.upsert !== undefined) {
1510
1517
  return await evaluateUpsert(crud, env);
1511
1518
  }
1512
1519
  const inst: Instance = crud.source
@@ -1518,12 +1525,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1518
1525
  const qattrs = inst.queryAttributes;
1519
1526
  const onlyAggregates = inst.aggregates !== undefined && qattrs === undefined;
1520
1527
  const isQueryAll = onlyAggregates || crud.name.endsWith(QuerySuffix);
1521
- const distinct: boolean = crud.distinct.length > 0;
1522
- maybeSetQueryClauses(inst, crud);
1528
+ const distinct: boolean = qopts.distinct !== undefined;
1529
+ maybeSetQueryClauses(inst, qopts);
1523
1530
  if (attrs.size > 0) {
1524
1531
  await maybeValidateOneOfRefs(inst, env);
1525
1532
  }
1526
- if (crud.into) {
1533
+ if (qopts.into) {
1527
1534
  if (attrs.size > 0) {
1528
1535
  throw new Error(
1529
1536
  `Query pattern for ${entryName} with 'into' clause cannot be used to update attributes`
@@ -1532,10 +1539,16 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1532
1539
  if (qattrs === undefined && !isQueryAll) {
1533
1540
  throw new Error(`Pattern for ${entryName} with 'into' clause must be a query`);
1534
1541
  }
1535
- if (crud.joins.length > 0) {
1536
- await evaluateJoinQuery(crud.joins, crud.into, crud.where, inst, distinct, env);
1542
+ if (qopts.joins && qopts.joins.length > 0) {
1543
+ await evaluateJoinQuery(qopts.joins, qopts.into, qopts.where, inst, distinct, env);
1537
1544
  } else {
1538
- await evaluateJoinQueryWithRelationships(crud.into, inst, crud.relationships, distinct, env);
1545
+ await evaluateJoinQueryWithRelationships(
1546
+ qopts.into,
1547
+ inst,
1548
+ crud.relationships || [],
1549
+ distinct,
1550
+ env
1551
+ );
1539
1552
  }
1540
1553
  return;
1541
1554
  }
@@ -2081,7 +2094,7 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
2081
2094
  const obj = agent.maybeValidateJsonResponse(result);
2082
2095
  if (obj !== undefined) {
2083
2096
  env.setLastResult(obj);
2084
- env.addToScratchPad(agent.getFqName(), obj);
2097
+ env.addToScratchPad(Agent.NormalizeName(agent.getFqName()), obj);
2085
2098
  }
2086
2099
  break;
2087
2100
  } catch (err: any) {
@@ -2187,6 +2200,7 @@ export async function restartFlow(
2187
2200
  }
2188
2201
 
2189
2202
  const MaxFlowSteps = 25;
2203
+ const MaxFlowRetries = 10;
2190
2204
 
2191
2205
  async function iterateOnFlow(
2192
2206
  flow: FlowSpec,
@@ -2195,88 +2209,101 @@ async function iterateOnFlow(
2195
2209
  env: Environment
2196
2210
  ): Promise<void> {
2197
2211
  rootAgent.disableSession();
2198
- const initContext = msg;
2199
- const s = `Now consider the following flowchart and return the next step:\n${flow}\n
2212
+ const chatId = env.getActiveEventInstance()?.lookup('chatId');
2213
+ const iterId = chatId || crypto.randomUUID();
2214
+ let step = '';
2215
+ let fullFlowRetries = 0;
2216
+ while (true) {
2217
+ try {
2218
+ const initContext = msg;
2219
+ const s = `Now consider the following flowchart and return the next step:\n${flow}\n
2200
2220
  If you understand from the context that a step with no further possible steps has been evaluated,
2201
2221
  terminate the flowchart by returning DONE. Never return to the top or root step of the flowchart, instead return DONE.
2202
2222
  Important: Return only the next flow-step or DONE. Do not return any additional description, like your thinking process.\n`;
2203
- env.setFlowContext(initContext);
2204
- await agentInvoke(rootAgent, s, env);
2205
- const rootModuleName = rootAgent.moduleName;
2206
- let preprocResult = await preprocessStep(env.getLastResult(), rootModuleName, env);
2207
- let step = preprocResult.step;
2208
- let needAgentProcessing = preprocResult.needAgentProcessing;
2209
- let context = initContext;
2210
- let stepc = 0;
2211
- const chatId = env.getActiveEventInstance()?.lookup('chatId');
2212
- const iterId = chatId || crypto.randomUUID();
2213
- console.debug(`Starting iteration ${iterId} on flow: ${flow}`);
2214
- const executedSteps = new Set<string>();
2215
- const monitoringEnabled = isMonitoringEnabled();
2216
- if (monitoringEnabled) {
2217
- env.flagMonitorEntryAsFlow().incrementMonitor();
2218
- }
2219
- let isfxc = false;
2220
- try {
2221
- while (step != 'DONE' && !executedSteps.has(step)) {
2222
- if (stepc > MaxFlowSteps) {
2223
- throw new Error(`Flow execution exceeded maximum steps limit`);
2223
+ env.setFlowContext(initContext);
2224
+ await agentInvoke(rootAgent, s, env);
2225
+ const rootModuleName = rootAgent.moduleName;
2226
+ let preprocResult = await preprocessStep(env.getLastResult(), rootModuleName, env);
2227
+ step = preprocResult.step;
2228
+ let needAgentProcessing = preprocResult.needAgentProcessing;
2229
+ let context = initContext;
2230
+ let stepc = 0;
2231
+ console.debug(`Starting iteration ${iterId} on flow: ${flow}`);
2232
+ const executedSteps = new Set<string>();
2233
+ const monitoringEnabled = isMonitoringEnabled();
2234
+ let isfxc = false;
2235
+ if (monitoringEnabled) {
2236
+ env.flagMonitorEntryAsFlow().incrementMonitor();
2224
2237
  }
2225
- executedSteps.add(step);
2226
- ++stepc;
2227
- const agent = needAgentProcessing
2228
- ? AgentInstance.FromFlowStep(step, rootAgent, context)
2229
- : undefined;
2230
- if (agent) {
2231
- console.debug(`\n---------------------------------------------------\n`);
2238
+ while (step != 'DONE' && !executedSteps.has(step)) {
2239
+ if (stepc > MaxFlowSteps) {
2240
+ throw new Error(`Flow execution exceeded maximum steps limit`);
2241
+ }
2242
+ executedSteps.add(step);
2243
+ ++stepc;
2244
+ const agent = needAgentProcessing
2245
+ ? AgentInstance.FromFlowStep(step, rootAgent, context)
2246
+ : undefined;
2247
+ if (agent) {
2248
+ console.debug(
2249
+ `Starting to execute flow step ${step} with agent ${agent.name} with iteration ID ${iterId} and context: \n${context}`
2250
+ );
2251
+ isfxc = agent.isFlowExecutor();
2252
+ const isdec = agent.isDecisionExecutor();
2253
+ if (isfxc || isdec) env.setFlowContext(context);
2254
+ else env.setFlowContext(initContext);
2255
+ if (monitoringEnabled) {
2256
+ env.appendEntryToMonitor(step);
2257
+ }
2258
+ const inst = agent.swapInstruction('');
2259
+ await agentInvoke(agent, inst, env);
2260
+ } else {
2261
+ rootAgent.maybeAddScratchData(env);
2262
+ }
2263
+ if (monitoringEnabled) env.setMonitorEntryResult(env.getLastResult());
2264
+ if (env.isSuspended()) {
2265
+ console.debug(`${iterId} suspending iteration on step ${step}`);
2266
+ await saveFlowSuspension(rootAgent, context, step, env);
2267
+ env.releaseSuspension();
2268
+ return;
2269
+ }
2270
+ const r = env.getLastResult();
2271
+ const rs = maybeInstanceAsString(r);
2232
2272
  console.debug(
2233
- `Starting to execute flow step ${step} with agent ${agent.name} with iteration ID ${iterId} and context: \n${context}`
2273
+ `\n----> Completed execution of step ${step}, iteration id ${iterId} with result:\n${rs}`
2234
2274
  );
2235
- isfxc = agent.isFlowExecutor();
2236
- const isdec = agent.isDecisionExecutor();
2237
- if (isfxc || isdec) env.setFlowContext(context);
2238
- else env.setFlowContext(initContext);
2239
- if (monitoringEnabled) {
2240
- env.appendEntryToMonitor(step);
2275
+ context = `${context}\n${step} --> ${rs}\n`;
2276
+ if (chatId) {
2277
+ const suspEnv = new Environment(env.name, env);
2278
+ suspEnv.softSuspend();
2279
+ await saveFlowSuspension(rootAgent, context, step, suspEnv);
2280
+ await saveFlowStepResult(chatId, step, rs, suspEnv.getSuspensionId(), env);
2241
2281
  }
2242
- const inst = agent.swapInstruction('');
2243
- await agentInvoke(agent, inst, env);
2244
- } else {
2245
- rootAgent.maybeAddScratchData(env);
2246
- }
2247
- if (monitoringEnabled) env.setMonitorEntryResult(env.getLastResult());
2248
- if (env.isSuspended()) {
2249
- console.debug(`${iterId} suspending iteration on step ${step}`);
2250
- await saveFlowSuspension(rootAgent, context, step, env);
2251
- env.releaseSuspension();
2252
- return;
2253
- }
2254
- const r = env.getLastResult();
2255
- const rs = maybeInstanceAsString(r);
2256
- console.debug(
2257
- `\n----> Completed execution of step ${step}, iteration id ${iterId} with result:\n${rs}`
2258
- );
2259
- context = `${context}\n${step} --> ${rs}\n`;
2260
- if (chatId) {
2261
- const suspEnv = new Environment(env.name, env);
2262
- suspEnv.softSuspend();
2263
- await saveFlowSuspension(rootAgent, context, step, suspEnv);
2264
- await saveFlowStepResult(chatId, step, rs, suspEnv.getSuspensionId(), env);
2282
+ if (isfxc) {
2283
+ preprocResult = await preprocessStep(rs, rootModuleName, env);
2284
+ } else {
2285
+ env.setFlowContext(context);
2286
+ await agentInvoke(rootAgent, `${s}\n${context}`, env);
2287
+ preprocResult = await preprocessStep(env.getLastResult(), rootModuleName, env);
2288
+ }
2289
+ step = preprocResult.step;
2290
+ needAgentProcessing = preprocResult.needAgentProcessing;
2265
2291
  }
2266
- if (isfxc) {
2267
- preprocResult = await preprocessStep(rs, rootModuleName, env);
2292
+ } catch (reason: any) {
2293
+ if (fullFlowRetries < MaxFlowRetries) {
2294
+ msg = `The previous attempt failed at step ${step} with the error ${reason}. Restart the flow the appropriate step
2295
+ (maybe even from the first step) and try to fix the issue.`;
2296
+ ++fullFlowRetries;
2297
+ continue;
2268
2298
  } else {
2269
- env.setFlowContext(context);
2270
- await agentInvoke(rootAgent, `${s}\n${context}`, env);
2271
- preprocResult = await preprocessStep(env.getLastResult(), rootModuleName, env);
2299
+ throw new Error(reason);
2272
2300
  }
2273
- step = preprocResult.step;
2274
- needAgentProcessing = preprocResult.needAgentProcessing;
2301
+ } finally {
2302
+ env.decrementMonitor().revokeLastResult().setMonitorFlowResult();
2275
2303
  }
2276
- } finally {
2277
- env.decrementMonitor().revokeLastResult().setMonitorFlowResult();
2304
+ console.debug(`No more flow steps, completed iteration ${iterId} on flow:\n${flow}`);
2305
+ break;
2278
2306
  }
2279
- console.debug(`No more flow steps, completed iteration ${iterId} on flow:\n${flow}`);
2280
2307
  }
2281
2308
 
2282
2309
  type PreprocStepResult = {
@@ -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,
@@ -17,6 +21,7 @@ import {
17
21
  parseAndEvaluateStatement,
18
22
  } from '../interpreter.js';
19
23
  import {
24
+ Agent,
20
25
  AgentEvaluator,
21
26
  asJSONSchema,
22
27
  Decision,
@@ -579,7 +584,7 @@ export class AgentInstance {
579
584
  }
580
585
  }
581
586
 
582
- private async getFullInstructions(
587
+ private async getFullInstructionsHelper(
583
588
  env: Environment,
584
589
  activator: AgentInstructionActivator
585
590
  ): Promise<string> {
@@ -651,6 +656,20 @@ Only return a pure JSON object with no extra text, annotations etc.`;
651
656
  }
652
657
  }
653
658
 
659
+ private static UserTag = 'user';
660
+
661
+ private async getFullInstructions(
662
+ env: Environment,
663
+ activator: AgentInstructionActivator
664
+ ): Promise<ExtractedText> {
665
+ const finalInstruction = await this.getFullInstructionsHelper(env, activator);
666
+ if (finalInstruction.indexOf(`<${AgentInstance.UserTag}>`) > 0) {
667
+ return extractAndRemoveAllXmlTaggedText(finalInstruction, AgentInstance.UserTag);
668
+ } else {
669
+ return { extracted: undefined, updatedText: finalInstruction };
670
+ }
671
+ }
672
+
654
673
  private static maybeRewriteTemplatePatterns(
655
674
  scratchPad: any,
656
675
  instruction: string,
@@ -709,7 +728,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
709
728
  ) {
710
729
  r = obj;
711
730
  } else {
712
- env.addToScratchPad(this.name, obj);
731
+ env.addToScratchPad(Agent.NormalizeName(this.name), obj);
713
732
  return this;
714
733
  }
715
734
  const scratchNames = this.getScratchNames();
@@ -819,6 +838,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
819
838
  const sess: Instance | null = this.withSession ? await findAgentChatSession(chatId, env) : null;
820
839
  let msgs: BaseMessage[] | undefined;
821
840
  let cachedMsg: string | undefined = undefined;
841
+ let extractedText: ExtractedText | undefined;
822
842
  const activator: AgentInstructionActivator = {
823
843
  provider: p,
824
844
  userMessage: message,
@@ -828,7 +848,8 @@ Only return a pure JSON object with no extra text, annotations etc.`;
828
848
  if (sess) {
829
849
  msgs = sess.lookup('messages');
830
850
  } else {
831
- cachedMsg = await this.getFullInstructions(env, activator);
851
+ extractedText = await this.getFullInstructions(env, activator);
852
+ cachedMsg = extractedText.updatedText;
832
853
  msgs = [systemMessage(cachedMsg || '')];
833
854
  }
834
855
  if (msgs) {
@@ -843,12 +864,21 @@ Only return a pure JSON object with no extra text, annotations etc.`;
843
864
  ? EvalInstructions
844
865
  : LearnerAgentInstructions;
845
866
  const ts = this.toolsAsString();
846
- const msg = `${s}\n${ts}\n${cachedMsg || (await this.getFullInstructions(env, activator))}`;
867
+ let tmpMsg = cachedMsg;
868
+ if (!tmpMsg) {
869
+ extractedText = await this.getFullInstructions(env, activator);
870
+ tmpMsg = extractedText.updatedText;
871
+ }
872
+ const msg = `${s}\n${ts}\n${tmpMsg}`;
847
873
  const newSysMsg = systemMessage(msg);
848
874
  msgs[0] = newSysMsg;
849
875
  }
876
+ let tmpMsg = message;
877
+ if (extractedText?.extracted) {
878
+ tmpMsg = `${tmpMsg}\n${extractedText.extracted.join('\n')}`;
879
+ }
850
880
  const hmsg = await this.maybeAddRelevantDocuments(
851
- this.maybeAddFlowContext(message, env),
881
+ this.maybeAddFlowContext(tmpMsg, env),
852
882
  env
853
883
  );
854
884
  if (hmsg.length > 0) {
@@ -1298,26 +1328,6 @@ function processScenarioResponse(resp: string): string {
1298
1328
  return resp;
1299
1329
  }
1300
1330
 
1301
- type ExtractedCode = {
1302
- language: string | null;
1303
- code: string;
1304
- };
1305
-
1306
- export function extractFencedCodeBlocks(markdown: string): ExtractedCode[] {
1307
- const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
1308
- const blocks: ExtractedCode[] = [];
1309
- let match;
1310
-
1311
- while ((match = codeBlockRegex.exec(markdown)) !== null) {
1312
- blocks.push({
1313
- language: match[1] || null,
1314
- code: match[2],
1315
- });
1316
- }
1317
-
1318
- return blocks;
1319
- }
1320
-
1321
1331
  export function normalizeGeneratedCode(code: string | undefined): string {
1322
1332
  if (code !== undefined) {
1323
1333
  const blocks = extractFencedCodeBlocks(code);
@@ -1,5 +1,5 @@
1
1
  import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
2
- import { readFile } from 'node:fs/promises';
2
+ import { readFile } from '../../utils/fs-utils.js';
3
3
  import { logger } from '../logger.js';
4
4
  import { parseAndEvaluateStatement } from '../interpreter.js';
5
5
  import { CoreAIModuleName } from '../modules/ai.js';
@@ -443,7 +443,7 @@ class DocumentFetcherService {
443
443
 
444
444
  private async fetchFromLocal(filePath: string): Promise<string> {
445
445
  try {
446
- const content = await readFile(filePath, 'utf-8');
446
+ const content = await readFile(filePath);
447
447
  const lowerPath = filePath.toLowerCase();
448
448
  const isMarkdown = lowerPath.endsWith('.md') || lowerPath.endsWith('.markdown');
449
449
 
@@ -1,4 +1,4 @@
1
- import { isNodeEnv } from '../utils/runtime.js';
1
+ import { isNodeEnv, path } from '../utils/runtime.js';
2
2
  import {
3
3
  AliasSpec,
4
4
  CatchSpec,
@@ -15,7 +15,6 @@ import {
15
15
  } from '../language/generated/ast.js';
16
16
  import { readFile } from '../utils/fs-utils.js';
17
17
  import bcrypt from 'bcryptjs';
18
- import path from 'node:path';
19
18
 
20
19
  export const QuerySuffix = '?';
21
20
 
@@ -665,3 +664,47 @@ export function objectAsString(obj: any, keyAsString: boolean = false) {
665
664
  });
666
665
  return `{${entries.join(', ')}}`;
667
666
  }
667
+
668
+ export type ExtractedText = {
669
+ extracted: string[] | undefined;
670
+ updatedText: string;
671
+ };
672
+
673
+ // extract all data between a given xml tag from within an arbitray text.
674
+ export function extractAndRemoveAllXmlTaggedText(text: string, tagName: string): ExtractedText {
675
+ const pattern = `<${tagName}\\b[^>]*>([\\s\\S]*?)</${tagName}>`;
676
+ const regex = new RegExp(pattern, 'gi');
677
+
678
+ const extracted = [];
679
+ let updatedText = text;
680
+
681
+ let match;
682
+ while ((match = regex.exec(text)) !== null) {
683
+ extracted.push(match[1]);
684
+ }
685
+
686
+ updatedText = text.replace(regex, '');
687
+
688
+ return { extracted, updatedText };
689
+ }
690
+
691
+ export type ExtractedCode = {
692
+ language: string | null;
693
+ code: string;
694
+ };
695
+
696
+ // extract tick-quoted code from markdown-formatted text.
697
+ export function extractFencedCodeBlocks(markdown: string): ExtractedCode[] {
698
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
699
+ const blocks: ExtractedCode[] = [];
700
+ let match;
701
+
702
+ while ((match = codeBlockRegex.exec(markdown)) !== null) {
703
+ blocks.push({
704
+ language: match[1] || null,
705
+ code: match[2],
706
+ });
707
+ }
708
+
709
+ return blocks;
710
+ }