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
@@ -10,6 +10,9 @@ export class MonitorEntry {
10
10
  private llm: boolean = false;
11
11
  private llmPrompt: string | undefined;
12
12
  private llmResponse: string | undefined;
13
+ private llmInputTokens: number | undefined;
14
+ private llmOutputTokens: number | undefined;
15
+ private llmTotalTokens: number | undefined;
13
16
  private planner: boolean = false;
14
17
  private flowStep: boolean = false;
15
18
  private flow: boolean = false;
@@ -25,6 +28,10 @@ export class MonitorEntry {
25
28
  return this.input;
26
29
  }
27
30
 
31
+ getTimestamp(): number {
32
+ return this.timestamp;
33
+ }
34
+
28
35
  setResult(result: any): MonitorEntry {
29
36
  if (this.result === undefined) {
30
37
  this.result = result;
@@ -68,6 +75,13 @@ export class MonitorEntry {
68
75
  return this;
69
76
  }
70
77
 
78
+ setLlmTokenUsage(input: number, output: number, total: number): MonitorEntry {
79
+ this.llmInputTokens = input;
80
+ this.llmOutputTokens = output;
81
+ this.llmTotalTokens = total;
82
+ return this;
83
+ }
84
+
71
85
  flagAsPlanner(): MonitorEntry {
72
86
  this.llm = true;
73
87
  if (this.flowStep || this.flow || this.decision) {
@@ -137,6 +151,11 @@ export class MonitorEntry {
137
151
  llmObj.isFlowStep = this.flowStep;
138
152
  llmObj.isDecision = this.decision;
139
153
  llmObj.isFlow = this.flow;
154
+ if (this.llmInputTokens !== undefined) {
155
+ llmObj.inputTokens = this.llmInputTokens;
156
+ llmObj.outputTokens = this.llmOutputTokens;
157
+ llmObj.totalTokens = this.llmTotalTokens;
158
+ }
140
159
  obj.llm = llmObj;
141
160
  }
142
161
  obj.label = this.input;
@@ -269,9 +288,16 @@ export class Monitor {
269
288
  return this;
270
289
  }
271
290
 
291
+ setEntryLlmTokenUsage(input: number, output: number, total: number): Monitor {
292
+ if (this.lastEntry !== undefined) {
293
+ this.lastEntry.setLlmTokenUsage(input, output, total);
294
+ }
295
+ return this;
296
+ }
297
+
272
298
  private finalizeLastEntry(): void {
273
299
  if (this.lastEntry) {
274
- const ms = Date.now() - this.lastEntrySetAtMs;
300
+ const ms = Date.now() - this.lastEntry.getTimestamp();
275
301
  this.lastEntry.setLatencyMs(ms);
276
302
  if (MonitoringCallback !== undefined) {
277
303
  MonitoringCallback(this.lastEntry);
@@ -51,8 +51,11 @@ export class RelationshipGraph {
51
51
  node: RelationshipGraphNode,
52
52
  onNode: Function,
53
53
  onContainsRelationship: Function,
54
- onBetweenRelationship: Function
54
+ onBetweenRelationship: Function,
55
+ visited: Set<RelationshipGraphNode>
55
56
  ) {
57
+ if (visited.has(node)) return;
58
+ visited.add(node);
56
59
  const n = node.entity.asFqName();
57
60
  onNode(n, node.edges);
58
61
  node.edges.forEach((edge: RelationshipGraphEdge) => {
@@ -60,13 +63,14 @@ export class RelationshipGraph {
60
63
  ? onContainsRelationship
61
64
  : onBetweenRelationship;
62
65
  rf(n, edge.node.entity.asFqName(), edge.relationship);
63
- this.walkEdges(edge.node, onNode, onContainsRelationship, onBetweenRelationship);
66
+ this.walkEdges(edge.node, onNode, onContainsRelationship, onBetweenRelationship, visited);
64
67
  });
65
68
  }
66
69
 
67
70
  walk(onNode: Function, onContainsRelationship: Function, onBetweenRelationship: Function) {
71
+ const visited = new Set<RelationshipGraphNode>();
68
72
  this.nodes.forEach((node: RelationshipGraphNode) => {
69
- this.walkEdges(node, onNode, onContainsRelationship, onBetweenRelationship);
73
+ this.walkEdges(node, onNode, onContainsRelationship, onBetweenRelationship, visited);
70
74
  });
71
75
  }
72
76
  }
@@ -121,13 +121,15 @@ export class Resolver {
121
121
  /**
122
122
  * Return all instances connected to connectedInstance via the given between-relationship
123
123
  * @param relationship Between relationship
124
- * @param connectedInstance The instance to traveres the relationship from
124
+ * @param connectedInstance The instance to traverse the relationship from
125
125
  * @param inst Target instance with query attributes
126
+ * @param connectedAlias For self-referencing relationships, the alias of the connected instance's role
126
127
  */
127
128
  public async queryConnectedInstances(
128
129
  relationship: Relationship,
129
130
  connectedInstance: Instance,
130
- inst: Instance
131
+ inst: Instance,
132
+ connectedAlias?: string
131
133
  ): Promise<any> {
132
134
  return this.notImpl(`queryConnectedInstances(${relationship}, ${connectedInstance}, ${inst})`);
133
135
  }
@@ -188,13 +188,27 @@ function ormSchemaFromRecordSchema(
188
188
  const fqName = makeFqName(moduleName, entityName);
189
189
  getAllOneToOneRelationshipsForEntity(moduleName, entityName, allBetRels).forEach(
190
190
  (re: Relationship) => {
191
- const colName = re.getInverseAliasForName(fqName);
192
- if (cols.has(colName)) {
193
- throw new Error(
194
- `Cannot establish relationship ${re.name}, ${entityName}.${colName} already exists`
195
- );
191
+ if (re.isSelfReferencing()) {
192
+ // Self-referencing one-to-one: add columns for both aliases
193
+ // so each side of the relationship can be stored on the entity.
194
+ // Both must be nullable since they're set after entity creation.
195
+ [re.node1.alias, re.node2.alias].forEach((colName: string) => {
196
+ if (cols.has(colName)) {
197
+ throw new Error(
198
+ `Cannot establish relationship ${re.name}, ${entityName}.${colName} already exists`
199
+ );
200
+ }
201
+ cols.set(colName, { type: 'varchar', unique: true });
202
+ });
203
+ } else {
204
+ const colName = re.getInverseAliasForName(fqName);
205
+ if (cols.has(colName)) {
206
+ throw new Error(
207
+ `Cannot establish relationship ${re.name}, ${entityName}.${colName} already exists`
208
+ );
209
+ }
210
+ cols.set(colName, { type: 'varchar', unique: true });
196
211
  }
197
- cols.set(colName, { type: 'varchar', unique: true });
198
212
  }
199
213
  );
200
214
  if (relsSpec.size > 0) {
@@ -480,16 +480,17 @@ export class SqlDbResolver extends Resolver {
480
480
  public override async queryConnectedInstances(
481
481
  relationship: Relationship,
482
482
  connectedInstance: Instance,
483
- inst: Instance
483
+ inst: Instance,
484
+ connectedAlias?: string
484
485
  ): Promise<Instance[]> {
485
486
  let result = SqlDbResolver.EmptyResultSet;
486
487
  if (relationship.isOneToOne()) {
487
- const col = relationship.getAliasFor(connectedInstance);
488
+ const col = relationship.getAliasForConnected(connectedInstance, connectedAlias);
488
489
  inst.addQuery(col, '=', connectedInstance.lookup(PathAttributeName));
489
490
  return await this.queryInstances(inst, false);
490
491
  } else {
491
- const from = relationship.getAliasFor(connectedInstance);
492
- const to = relationship.getInverseAliasFor(connectedInstance);
492
+ const from = relationship.getAliasForConnected(connectedInstance, connectedAlias);
493
+ const to = relationship.getInverseAliasForConnected(connectedInstance, connectedAlias);
493
494
  await getAllConnected(
494
495
  asTableReference(inst.moduleName, inst.name),
495
496
  inst.queryAttributesAsObject(),
@@ -797,9 +798,18 @@ function ensureOneToOneAttributes(inst: Instance) {
797
798
  const betRels = getAllBetweenRelationships();
798
799
  getAllOneToOneRelationshipsForEntity(inst.moduleName, inst.name, betRels).forEach(
799
800
  (re: Relationship) => {
800
- const n = re.getInverseAliasFor(inst);
801
- if (!inst.attributes.has(n)) {
802
- inst.attributes.set(n, crypto.randomUUID());
801
+ if (re.isSelfReferencing()) {
802
+ // Self-referencing one-to-one: ensure both alias columns have placeholder values
803
+ [re.node1.alias, re.node2.alias].forEach((n: string) => {
804
+ if (!inst.attributes.has(n)) {
805
+ inst.attributes.set(n, crypto.randomUUID());
806
+ }
807
+ });
808
+ } else {
809
+ const n = re.getInverseAliasFor(inst);
810
+ if (!inst.attributes.has(n)) {
811
+ inst.attributes.set(n, crypto.randomUUID());
812
+ }
803
813
  }
804
814
  }
805
815
  );
@@ -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,7 +1,8 @@
1
- import { isNodeEnv } from '../utils/runtime.js';
1
+ import { isNodeEnv, path } from '../utils/runtime.js';
2
2
  import {
3
3
  AliasSpec,
4
4
  CatchSpec,
5
+ EmptySpec,
5
6
  ExtendsClause,
6
7
  isLiteral,
7
8
  MapEntry,
@@ -15,7 +16,6 @@ import {
15
16
  } from '../language/generated/ast.js';
16
17
  import { readFile } from '../utils/fs-utils.js';
17
18
  import bcrypt from 'bcryptjs';
18
- import path from 'node:path';
19
19
 
20
20
  export const QuerySuffix = '?';
21
21
 
@@ -442,6 +442,18 @@ export function firstCatchSpec(stmt: Statement): CatchSpec | undefined {
442
442
  return undefined;
443
443
  }
444
444
 
445
+ export function firstEmptySpec(stmt: Statement): EmptySpec | undefined {
446
+ if (stmt.hints) {
447
+ for (let i = 0; i < stmt.hints.length; ++i) {
448
+ const rh = stmt.hints[i];
449
+ if (rh.emptySpec) {
450
+ return rh.emptySpec;
451
+ }
452
+ }
453
+ }
454
+ return undefined;
455
+ }
456
+
445
457
  function maybeExtractEntryName(n: string): string {
446
458
  const i = n.indexOf('$');
447
459
  if (i > 0) {
@@ -665,3 +677,51 @@ export function objectAsString(obj: any, keyAsString: boolean = false) {
665
677
  });
666
678
  return `{${entries.join(', ')}}`;
667
679
  }
680
+
681
+ export type ExtractedText = {
682
+ extracted: string[] | undefined;
683
+ updatedText: string;
684
+ };
685
+
686
+ // extract all data between a given xml tag from within an arbitray text.
687
+ export function extractAndRemoveAllXmlTaggedText(
688
+ text: string,
689
+ tagName: string,
690
+ replaceWith: string = ''
691
+ ): ExtractedText {
692
+ const pattern = `<${tagName}\\b[^>]*>([\\s\\S]*?)</${tagName}>`;
693
+ const regex = new RegExp(pattern, 'gi');
694
+
695
+ const extracted = [];
696
+ let updatedText = text;
697
+
698
+ let match;
699
+ while ((match = regex.exec(text)) !== null) {
700
+ extracted.push(match[1]);
701
+ }
702
+
703
+ updatedText = text.replace(regex, replaceWith);
704
+
705
+ return { extracted, updatedText };
706
+ }
707
+
708
+ export type ExtractedCode = {
709
+ language: string | null;
710
+ code: string;
711
+ };
712
+
713
+ // extract tick-quoted code from markdown-formatted text.
714
+ export function extractFencedCodeBlocks(markdown: string): ExtractedCode[] {
715
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
716
+ const blocks: ExtractedCode[] = [];
717
+ let match;
718
+
719
+ while ((match = codeBlockRegex.exec(markdown)) !== null) {
720
+ blocks.push({
721
+ language: match[1] || null,
722
+ code: match[2],
723
+ });
724
+ }
725
+
726
+ return blocks;
727
+ }
@@ -1,7 +1,7 @@
1
1
  // Monarch syntax highlighting for the agentlang language.
2
2
  export default {
3
3
  keywords: [
4
- '@actions','@after','@as','@asc','@async','@before','@catch','@desc','@distinct','@enum','@expr','@from','@full_join','@groupBy','@inner_join','@into','@join','@left_join','@meta','@oneof','@orderBy','@public','@rbac','@ref','@right_join','@then','@upsert','@where','@withRole','@with_unique','agent','agentlang/retry','allow','and','attempts','await','backoff','between','case','commitTransaction','contains','create','decision','delete','directive','else','entity','error','eval','event','extends','false','flow','for','glossaryEntry','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','rollbackTransaction','scenario','startTransaction','subscribe','throw','true','update','upsert','where','workflow'
4
+ '@actions','@after','@as','@asc','@async','@before','@catch','@desc','@distinct','@empty','@enum','@expr','@from','@full_join','@groupBy','@inner_join','@into','@join','@left_join','@meta','@oneof','@orderBy','@public','@rbac','@ref','@right_join','@then','@upsert','@where','@withRole','@with_unique','agent','agentlang/retry','allow','and','attempts','await','backoff','between','case','commitTransaction','contains','create','decision','delete','directive','else','entity','error','eval','event','extends','false','flow','for','glossaryEntry','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','rollbackTransaction','scenario','startTransaction','subscribe','throw','true','update','upsert','where','workflow'
5
5
  ],
6
6
  operators: [
7
7
  '!=','*','+',',','-','-->','.','/',':',';','<','<=','<>','=','==','>','>=','?','@'