agentlang 0.10.0 → 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 (43) 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 +47 -7
  13. package/out/language/parser.js.map +1 -1
  14. package/out/runtime/agents/common.d.ts +2 -2
  15. package/out/runtime/agents/common.js +1 -1
  16. package/out/runtime/interpreter.d.ts.map +1 -1
  17. package/out/runtime/interpreter.js +14 -13
  18. package/out/runtime/interpreter.js.map +1 -1
  19. package/out/runtime/modules/ai.d.ts +2 -5
  20. package/out/runtime/modules/ai.d.ts.map +1 -1
  21. package/out/runtime/modules/ai.js +26 -17
  22. package/out/runtime/modules/ai.js.map +1 -1
  23. package/out/runtime/services/documentFetcher.js +2 -2
  24. package/out/runtime/services/documentFetcher.js.map +1 -1
  25. package/out/runtime/util.d.ts +10 -0
  26. package/out/runtime/util.d.ts.map +1 -1
  27. package/out/runtime/util.js +27 -2
  28. package/out/runtime/util.js.map +1 -1
  29. package/out/utils/fs/index.d.ts +12 -2
  30. package/out/utils/fs/index.d.ts.map +1 -1
  31. package/out/utils/fs/index.js +27 -6
  32. package/out/utils/fs/index.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/language/agentlang.langium +6 -9
  35. package/src/language/generated/ast.ts +66 -43
  36. package/src/language/generated/grammar.ts +294 -327
  37. package/src/language/parser.ts +54 -5
  38. package/src/runtime/agents/common.ts +1 -1
  39. package/src/runtime/interpreter.ts +25 -13
  40. package/src/runtime/modules/ai.ts +33 -24
  41. package/src/runtime/services/documentFetcher.ts +2 -2
  42. package/src/runtime/util.ts +45 -2
  43. 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,
@@ -24,14 +25,17 @@ import {
24
25
  ModuleDefinition,
25
26
  NegExpr,
26
27
  NotExpr,
28
+ OrderByClause,
27
29
  Pattern,
28
30
  PrimExpr,
31
+ QueryOption,
29
32
  RelationshipPattern,
30
33
  Return,
31
34
  SelectIntoEntry,
32
35
  SelectIntoSpec,
33
36
  SetAttribute,
34
37
  Statement,
38
+ WhereSpec,
35
39
  WorkflowDefinition,
36
40
  } from './generated/ast.js';
37
41
  import { firstAliasSpec, firstCatchSpec, isString, QuerySuffix } from '../runtime/util.js';
@@ -269,8 +273,9 @@ function introspectPattern(pat: Pattern): BasePattern {
269
273
  } else {
270
274
  r = introspectCreatePattern(pat.crudMap);
271
275
  }
272
- if (pat.crudMap.into) {
273
- 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);
274
279
  }
275
280
  } else if (pat.expr) {
276
281
  r = introspectExpression(pat.expr);
@@ -360,10 +365,11 @@ function introspectQueryPattern(crudMap: CrudMap): CrudPattern {
360
365
  crudMap.body?.attributes.forEach((sa: SetAttribute) => {
361
366
  cp.addAttribute(sa.name, introspectExpression(sa.value), sa.op);
362
367
  });
363
- crudMap.relationships.forEach((rp: RelationshipPattern) => {
368
+ const opts = extractQueryOptions(crudMap);
369
+ crudMap.relationships?.forEach((rp: RelationshipPattern) => {
364
370
  cp.addRelationship(rp.name, introspectPattern(rp.pattern) as CrudPattern | CrudPattern[]);
365
371
  });
366
- crudMap.joins.forEach((js: JoinSpec) => {
372
+ opts.joins?.forEach((js: JoinSpec) => {
367
373
  const jp: JoinPattern = {
368
374
  type: js.type,
369
375
  targetEntity: js.name,
@@ -381,6 +387,49 @@ function introspectQueryPattern(crudMap: CrudMap): CrudPattern {
381
387
  throw new Error(`Failed to introspect query-pattern: ${crudMap}`);
382
388
  }
383
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
+
384
433
  function introspectCreatePattern(crudMap: CrudMap): CrudPattern {
385
434
  if (crudMap) {
386
435
  const cp: CrudPattern = new CrudPattern(crudMap.name);
@@ -393,7 +442,7 @@ function introspectCreatePattern(crudMap: CrudMap): CrudPattern {
393
442
  }
394
443
  cp.addAttribute(sa.name, introspectExpression(sa.value), sa.op);
395
444
  });
396
- crudMap.relationships.forEach((rp: RelationshipPattern) => {
445
+ crudMap.relationships?.forEach((rp: RelationshipPattern) => {
397
446
  cp.addRelationship(rp.name, introspectPattern(rp.pattern) as CrudPattern | CrudPattern[]);
398
447
  });
399
448
  cp.isQueryUpdate = qup;
@@ -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,
@@ -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,
@@ -1497,17 +1502,18 @@ async function maybeValidateOneOfRefs(inst: Instance, env: Environment) {
1497
1502
  }
1498
1503
  }
1499
1504
 
1500
- function maybeSetQueryClauses(inst: Instance, crud: CrudMap) {
1501
- if (crud.groupByClause) {
1502
- inst.setGroupBy(crud.groupByClause.colNames);
1505
+ function maybeSetQueryClauses(inst: Instance, qopts: ExtractedQueryOptions) {
1506
+ if (qopts.groupByClause) {
1507
+ inst.setGroupBy(qopts.groupByClause.colNames);
1503
1508
  }
1504
- if (crud.orderByClause) {
1505
- inst.setOrderBy(crud.orderByClause.colNames, crud.orderByClause.order === '@desc');
1509
+ if (qopts.orderByClause) {
1510
+ inst.setOrderBy(qopts.orderByClause.colNames, qopts.orderByClause.order === '@desc');
1506
1511
  }
1507
1512
  }
1508
1513
 
1509
1514
  async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1510
- if (!env.isInUpsertMode() && crud.upsert.length > 0) {
1515
+ const qopts = extractQueryOptions(crud);
1516
+ if (!env.isInUpsertMode() && qopts.upsert !== undefined) {
1511
1517
  return await evaluateUpsert(crud, env);
1512
1518
  }
1513
1519
  const inst: Instance = crud.source
@@ -1519,12 +1525,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1519
1525
  const qattrs = inst.queryAttributes;
1520
1526
  const onlyAggregates = inst.aggregates !== undefined && qattrs === undefined;
1521
1527
  const isQueryAll = onlyAggregates || crud.name.endsWith(QuerySuffix);
1522
- const distinct: boolean = crud.distinct.length > 0;
1523
- maybeSetQueryClauses(inst, crud);
1528
+ const distinct: boolean = qopts.distinct !== undefined;
1529
+ maybeSetQueryClauses(inst, qopts);
1524
1530
  if (attrs.size > 0) {
1525
1531
  await maybeValidateOneOfRefs(inst, env);
1526
1532
  }
1527
- if (crud.into) {
1533
+ if (qopts.into) {
1528
1534
  if (attrs.size > 0) {
1529
1535
  throw new Error(
1530
1536
  `Query pattern for ${entryName} with 'into' clause cannot be used to update attributes`
@@ -1533,10 +1539,16 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1533
1539
  if (qattrs === undefined && !isQueryAll) {
1534
1540
  throw new Error(`Pattern for ${entryName} with 'into' clause must be a query`);
1535
1541
  }
1536
- if (crud.joins.length > 0) {
1537
- 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);
1538
1544
  } else {
1539
- 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
+ );
1540
1552
  }
1541
1553
  return;
1542
1554
  }
@@ -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,
@@ -580,7 +584,7 @@ export class AgentInstance {
580
584
  }
581
585
  }
582
586
 
583
- private async getFullInstructions(
587
+ private async getFullInstructionsHelper(
584
588
  env: Environment,
585
589
  activator: AgentInstructionActivator
586
590
  ): Promise<string> {
@@ -652,6 +656,20 @@ Only return a pure JSON object with no extra text, annotations etc.`;
652
656
  }
653
657
  }
654
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
+
655
673
  private static maybeRewriteTemplatePatterns(
656
674
  scratchPad: any,
657
675
  instruction: string,
@@ -820,6 +838,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
820
838
  const sess: Instance | null = this.withSession ? await findAgentChatSession(chatId, env) : null;
821
839
  let msgs: BaseMessage[] | undefined;
822
840
  let cachedMsg: string | undefined = undefined;
841
+ let extractedText: ExtractedText | undefined;
823
842
  const activator: AgentInstructionActivator = {
824
843
  provider: p,
825
844
  userMessage: message,
@@ -829,7 +848,8 @@ Only return a pure JSON object with no extra text, annotations etc.`;
829
848
  if (sess) {
830
849
  msgs = sess.lookup('messages');
831
850
  } else {
832
- cachedMsg = await this.getFullInstructions(env, activator);
851
+ extractedText = await this.getFullInstructions(env, activator);
852
+ cachedMsg = extractedText.updatedText;
833
853
  msgs = [systemMessage(cachedMsg || '')];
834
854
  }
835
855
  if (msgs) {
@@ -844,12 +864,21 @@ Only return a pure JSON object with no extra text, annotations etc.`;
844
864
  ? EvalInstructions
845
865
  : LearnerAgentInstructions;
846
866
  const ts = this.toolsAsString();
847
- 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}`;
848
873
  const newSysMsg = systemMessage(msg);
849
874
  msgs[0] = newSysMsg;
850
875
  }
876
+ let tmpMsg = message;
877
+ if (extractedText?.extracted) {
878
+ tmpMsg = `${tmpMsg}\n${extractedText.extracted.join('\n')}`;
879
+ }
851
880
  const hmsg = await this.maybeAddRelevantDocuments(
852
- this.maybeAddFlowContext(message, env),
881
+ this.maybeAddFlowContext(tmpMsg, env),
853
882
  env
854
883
  );
855
884
  if (hmsg.length > 0) {
@@ -1299,26 +1328,6 @@ function processScenarioResponse(resp: string): string {
1299
1328
  return resp;
1300
1329
  }
1301
1330
 
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
1331
  export function normalizeGeneratedCode(code: string | undefined): string {
1323
1332
  if (code !== undefined) {
1324
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
+ }
@@ -4,25 +4,49 @@
4
4
 
5
5
  export * from './interfaces.js';
6
6
  import { ExtendedFileSystem } from './interfaces.js';
7
- import { createNodeFS } from './node-fs.js';
8
- import { createLightningFS } from './lightning-fs.js';
9
7
 
10
8
  /**
11
9
  * Create the appropriate filesystem implementation based on environment
10
+ * Uses dynamic imports to avoid bundling Node.js-specific code in browser builds
12
11
  * @returns Promise resolving to appropriate filesystem implementation
13
12
  */
14
13
  export async function createFS(options?: any): Promise<ExtendedFileSystem> {
15
14
  // Check if we're in a browser or Node environment
16
15
  if (typeof window === 'undefined') {
17
- // Node.js environment
16
+ // Node.js environment - use dynamic import to avoid bundling in browser
17
+ const { createNodeFS } = await import('./node-fs.js');
18
18
  return createNodeFS();
19
19
  } else {
20
20
  // Browser environment - use Lightning FS
21
+ const { createLightningFS } = await import('./lightning-fs.js');
21
22
  return createLightningFS(options);
22
23
  }
23
24
  }
24
25
 
25
- // Export the specific filesystem implementations
26
- export { createNodeFS } from './node-fs.js';
27
- export { createLightningFS } from './lightning-fs.js';
26
+ // Re-export interface types (these are safe for browser)
28
27
  export * from './interfaces.js';
28
+
29
+ // Export factory functions that use dynamic imports internally
30
+ // These are async to support dynamic loading based on environment
31
+
32
+ /**
33
+ * Create Node.js filesystem - only works in Node.js environment
34
+ * @returns Promise resolving to NodeFileSystem instance
35
+ */
36
+ export async function createNodeFS(): Promise<ExtendedFileSystem> {
37
+ if (typeof window !== 'undefined') {
38
+ throw new Error('createNodeFS is only available in Node.js environment');
39
+ }
40
+ const module = await import('./node-fs.js');
41
+ return module.createNodeFS();
42
+ }
43
+
44
+ /**
45
+ * Create Lightning FS - works in browser environment
46
+ * @param options Optional configuration for Lightning FS
47
+ * @returns Promise resolving to LightningFileSystem instance
48
+ */
49
+ export async function createLightningFS(options?: any): Promise<ExtendedFileSystem> {
50
+ const module = await import('./lightning-fs.js');
51
+ return module.createLightningFS(options);
52
+ }