agentlang 0.10.1 → 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 (95) 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 +27 -2
  5. package/out/language/generated/ast.d.ts.map +1 -1
  6. package/out/language/generated/ast.js +20 -0
  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 +246 -206
  10. package/out/language/generated/grammar.js.map +1 -1
  11. package/out/language/main.cjs +263 -206
  12. package/out/language/main.cjs.map +2 -2
  13. package/out/language/parser.d.ts.map +1 -1
  14. package/out/language/parser.js +28 -2
  15. package/out/language/parser.js.map +1 -1
  16. package/out/language/syntax.d.ts +14 -0
  17. package/out/language/syntax.d.ts.map +1 -1
  18. package/out/language/syntax.js +60 -27
  19. package/out/language/syntax.js.map +1 -1
  20. package/out/runtime/api.d.ts +2 -0
  21. package/out/runtime/api.d.ts.map +1 -1
  22. package/out/runtime/api.js +3 -0
  23. package/out/runtime/api.js.map +1 -1
  24. package/out/runtime/datefns.d.ts +34 -0
  25. package/out/runtime/datefns.d.ts.map +1 -0
  26. package/out/runtime/datefns.js +82 -0
  27. package/out/runtime/datefns.js.map +1 -0
  28. package/out/runtime/exec-graph.d.ts.map +1 -1
  29. package/out/runtime/exec-graph.js +22 -3
  30. package/out/runtime/exec-graph.js.map +1 -1
  31. package/out/runtime/interpreter.d.ts +9 -0
  32. package/out/runtime/interpreter.d.ts.map +1 -1
  33. package/out/runtime/interpreter.js +71 -7
  34. package/out/runtime/interpreter.js.map +1 -1
  35. package/out/runtime/module.d.ts +8 -0
  36. package/out/runtime/module.d.ts.map +1 -1
  37. package/out/runtime/module.js +23 -0
  38. package/out/runtime/module.js.map +1 -1
  39. package/out/runtime/modules/ai.d.ts +7 -3
  40. package/out/runtime/modules/ai.d.ts.map +1 -1
  41. package/out/runtime/modules/ai.js +67 -21
  42. package/out/runtime/modules/ai.js.map +1 -1
  43. package/out/runtime/modules/core.d.ts.map +1 -1
  44. package/out/runtime/modules/core.js +5 -1
  45. package/out/runtime/modules/core.js.map +1 -1
  46. package/out/runtime/monitor.d.ts +6 -0
  47. package/out/runtime/monitor.d.ts.map +1 -1
  48. package/out/runtime/monitor.js +21 -1
  49. package/out/runtime/monitor.js.map +1 -1
  50. package/out/runtime/relgraph.d.ts.map +1 -1
  51. package/out/runtime/relgraph.js +7 -3
  52. package/out/runtime/relgraph.js.map +1 -1
  53. package/out/runtime/resolvers/interface.d.ts +3 -2
  54. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  55. package/out/runtime/resolvers/interface.js +3 -2
  56. package/out/runtime/resolvers/interface.js.map +1 -1
  57. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  58. package/out/runtime/resolvers/sqldb/dbutil.js +17 -4
  59. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  60. package/out/runtime/resolvers/sqldb/impl.d.ts +1 -1
  61. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  62. package/out/runtime/resolvers/sqldb/impl.js +17 -7
  63. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  64. package/out/runtime/util.d.ts +3 -2
  65. package/out/runtime/util.d.ts.map +1 -1
  66. package/out/runtime/util.js +13 -2
  67. package/out/runtime/util.js.map +1 -1
  68. package/out/syntaxes/agentlang.monarch.js +1 -1
  69. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  70. package/out/test-harness.d.ts +36 -0
  71. package/out/test-harness.d.ts.map +1 -0
  72. package/out/test-harness.js +341 -0
  73. package/out/test-harness.js.map +1 -0
  74. package/package.json +4 -1
  75. package/src/api/http.ts +138 -0
  76. package/src/language/agentlang.langium +3 -1
  77. package/src/language/generated/ast.ts +32 -1
  78. package/src/language/generated/grammar.ts +246 -206
  79. package/src/language/parser.ts +33 -1
  80. package/src/language/syntax.ts +71 -24
  81. package/src/runtime/api.ts +5 -0
  82. package/src/runtime/datefns.ts +112 -0
  83. package/src/runtime/exec-graph.ts +23 -2
  84. package/src/runtime/interpreter.ts +82 -6
  85. package/src/runtime/module.ts +26 -0
  86. package/src/runtime/modules/ai.ts +78 -31
  87. package/src/runtime/modules/core.ts +5 -1
  88. package/src/runtime/monitor.ts +27 -1
  89. package/src/runtime/relgraph.ts +7 -3
  90. package/src/runtime/resolvers/interface.ts +4 -2
  91. package/src/runtime/resolvers/sqldb/dbutil.ts +20 -6
  92. package/src/runtime/resolvers/sqldb/impl.ts +17 -7
  93. package/src/runtime/util.ts +19 -2
  94. package/src/syntaxes/agentlang.monarch.ts +1 -1
  95. package/src/test-harness.ts +423 -0
package/src/api/http.ts CHANGED
@@ -58,6 +58,8 @@ import {
58
58
  createFileRecord,
59
59
  deleteFileRecord,
60
60
  } from '../runtime/modules/files.js';
61
+ import * as XLSX from 'xlsx';
62
+ import { objectToQueryPattern } from '../language/parser.js';
61
63
 
62
64
  export async function startServer(
63
65
  appSpec: ApplicationSpec,
@@ -197,6 +199,14 @@ export async function startServer(
197
199
  handleMetaGet(req, res);
198
200
  });
199
201
 
202
+ app.post('/agentlang/QueryAsCsv', (req: Request, res: Response) => {
203
+ handleQueryAsCsvPost(req, res);
204
+ });
205
+
206
+ app.post('/agentlang/QueryAsXlsx', (req: Request, res: Response) => {
207
+ handleQueryAsXlsxPost(req, res);
208
+ });
209
+
200
210
  if (isNodeEnv && upload && uploadDir) {
201
211
  app.post('/uploadFile', upload.single('file'), (req: Request, res: Response) => {
202
212
  handleFileUpload(req, res, config);
@@ -730,6 +740,69 @@ function normalizedResult(r: Result): Result {
730
740
  }
731
741
  }
732
742
 
743
+ /**
744
+ * Converts eval result to an array of flat row objects for CSV/XLSX.
745
+ * Handles:
746
+ * - [{ "EntityName": { "attr": "attrval" } }, ...] -> one row per item, columns = Entity + attrs
747
+ * - [{ "attr": "attrval" }, ...] -> one row per item, columns = attrs
748
+ * - [[ { "EntityName": { ... } }, ... ]] -> unwrap single outer array, then one row per inner item
749
+ */
750
+ function evalResultToRows(data: Result): { [key: string]: unknown }[] {
751
+ let items: Result[];
752
+ if (Array.isArray(data) && data.length === 1 && Array.isArray(data[0])) {
753
+ items = data[0] as Result[];
754
+ } else {
755
+ items = Array.isArray(data) ? (data as Result[]) : [data];
756
+ }
757
+ return items.map((item: Result) => {
758
+ if (item === null || typeof item !== 'object') {
759
+ return { value: item };
760
+ }
761
+ if (Array.isArray(item)) {
762
+ return { value: item };
763
+ }
764
+ const obj = item as { [key: string]: unknown };
765
+ const keys = Object.keys(obj);
766
+ if (keys.length === 1) {
767
+ const [k] = keys;
768
+ const v = obj[k];
769
+ if (
770
+ v !== null &&
771
+ typeof v === 'object' &&
772
+ !Array.isArray(v) &&
773
+ Object.prototype.toString.call(v) === '[object Object]'
774
+ ) {
775
+ return { ...(v as { [key: string]: unknown }) };
776
+ }
777
+ }
778
+ return { ...obj };
779
+ });
780
+ }
781
+
782
+ /**
783
+ * Ensures cell value is stringifiable for CSV/XLSX (nested objects/arrays become JSON string).
784
+ */
785
+ function cellValue(val: unknown): string | number | boolean | null {
786
+ if (val === null || val === undefined) return '';
787
+ if (typeof val === 'object') return JSON.stringify(val);
788
+ return val as string | number | boolean;
789
+ }
790
+
791
+ /**
792
+ * Parses column names from the first instance and returns an array-of-arrays
793
+ * for the sheet: first row = column names, following rows = data. This ensures
794
+ * CSV/XLSX output has proper headers (col1, col2, ...) not 0, 1, 2, ...
795
+ */
796
+ function rowsToSheetAoa(
797
+ rows: { [key: string]: unknown }[]
798
+ ): (string | number | boolean | null)[][] {
799
+ if (rows.length === 0) return [];
800
+ const columns = Object.keys(rows[0] as object);
801
+ const headerRow = columns;
802
+ const dataRows = rows.map(row => columns.map(col => cellValue(row[col])));
803
+ return [headerRow, ...dataRows];
804
+ }
805
+
733
806
  async function handleMetaGet(req: Request, res: Response): Promise<void> {
734
807
  try {
735
808
  const sessionInfo = await verifyAuth('', '', req.headers.authorization);
@@ -957,6 +1030,71 @@ async function handleMetaGet(req: Request, res: Response): Promise<void> {
957
1030
  }
958
1031
  }
959
1032
 
1033
+ async function handleQueryAsCsvPost(req: Request, res: Response): Promise<void> {
1034
+ try {
1035
+ const sessionInfo = await verifyAuth('', '', req.headers.authorization);
1036
+ if (isNoSession(sessionInfo)) {
1037
+ res.status(401).send('Authorization required');
1038
+ return;
1039
+ }
1040
+ const q = req.body?.q;
1041
+ if (q === undefined) {
1042
+ res.status(400).send('Missing or invalid pattern');
1043
+ return;
1044
+ }
1045
+ const qs = objectToQueryPattern(q);
1046
+ const result = await parseAndEvaluateStatement(qs, sessionInfo.userId);
1047
+ const normalized = normalizedResult(result);
1048
+ const rows = evalResultToRows(normalized);
1049
+ const aoa = rowsToSheetAoa(rows);
1050
+ const worksheet = XLSX.utils.aoa_to_sheet(aoa);
1051
+ const workbook = XLSX.utils.book_new();
1052
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
1053
+ const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'csv' });
1054
+ const filename = `eval-${Date.now()}.csv`;
1055
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
1056
+ res.contentType('text/csv');
1057
+ res.send(buffer);
1058
+ } catch (err: any) {
1059
+ logger.error(err);
1060
+ if (!res.headersSent) {
1061
+ res.status(statusFromErrorType(err)).send(err?.message ?? err?.toString());
1062
+ }
1063
+ }
1064
+ }
1065
+
1066
+ async function handleQueryAsXlsxPost(req: Request, res: Response): Promise<void> {
1067
+ try {
1068
+ const sessionInfo = await verifyAuth('', '', req.headers.authorization);
1069
+ if (isNoSession(sessionInfo)) {
1070
+ res.status(401).send('Authorization required');
1071
+ return;
1072
+ }
1073
+ const q = req.body?.q;
1074
+ if (q === undefined) {
1075
+ res.status(400).send('Missing or invalid pattern');
1076
+ return;
1077
+ }
1078
+ const qs = objectToQueryPattern(q);
1079
+ const result = await parseAndEvaluateStatement(qs, sessionInfo.userId);
1080
+ const normalized = normalizedResult(result);
1081
+ const rows = evalResultToRows(normalized);
1082
+ const aoa = rowsToSheetAoa(rows);
1083
+ const worksheet = XLSX.utils.aoa_to_sheet(aoa);
1084
+ const workbook = XLSX.utils.book_new();
1085
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
1086
+ const buffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
1087
+ const filename = `eval-${Date.now()}.xlsx`;
1088
+ res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
1089
+ res.contentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
1090
+ res.send(buffer);
1091
+ } catch (err: any) {
1092
+ logger.error(err);
1093
+ if (!res.headersSent) {
1094
+ res.status(statusFromErrorType(err)).send(err?.message ?? err?.toString());
1095
+ }
1096
+ }
1097
+ }
960
1098
  async function handleFileUpload(
961
1099
  req: Request & { file?: Express.Multer.File },
962
1100
  res: Response,
@@ -115,7 +115,9 @@ Statement: pattern=Pattern hints+=RuntimeHint*;
115
115
 
116
116
  IfWithAlias: '(' if=If ')';
117
117
 
118
- RuntimeHint: aliasSpec=AliasSpec | catchSpec=CatchSpec | thenSpec=ThenSpec;
118
+ RuntimeHint: aliasSpec=AliasSpec | catchSpec=CatchSpec | emptySpec=EmptySpec | thenSpec=ThenSpec;
119
+
120
+ EmptySpec: '@empty' stmt=Statement;
119
121
 
120
122
  AliasSpec: ('@as' (alias=ID | '[' ( aliases+=ID (',' aliases+=ID)*)+ ']'));
121
123
 
@@ -49,6 +49,7 @@ export type AgentlangKeywordNames =
49
49
  | "@catch"
50
50
  | "@desc"
51
51
  | "@distinct"
52
+ | "@empty"
52
53
  | "@enum"
53
54
  | "@expr"
54
55
  | "@from"
@@ -586,6 +587,21 @@ export function isElse(item: unknown): item is Else {
586
587
  return reflection.isInstance(item, Else.$type);
587
588
  }
588
589
 
590
+ export interface EmptySpec extends langium.AstNode {
591
+ readonly $container: RuntimeHint;
592
+ readonly $type: 'EmptySpec';
593
+ stmt: Statement;
594
+ }
595
+
596
+ export const EmptySpec = {
597
+ $type: 'EmptySpec',
598
+ stmt: 'stmt'
599
+ } as const;
600
+
601
+ export function isEmptySpec(item: unknown): item is EmptySpec {
602
+ return reflection.isInstance(item, EmptySpec.$type);
603
+ }
604
+
589
605
  export interface EntityActionsDefinitions extends langium.AstNode {
590
606
  readonly $container: RecordExtraDefinition;
591
607
  readonly $type: 'EntityActionsDefinitions';
@@ -1744,6 +1760,7 @@ export interface RuntimeHint extends langium.AstNode {
1744
1760
  readonly $type: 'RuntimeHint';
1745
1761
  aliasSpec?: AliasSpec;
1746
1762
  catchSpec?: CatchSpec;
1763
+ emptySpec?: EmptySpec;
1747
1764
  thenSpec?: ThenSpec;
1748
1765
  }
1749
1766
 
@@ -1751,6 +1768,7 @@ export const RuntimeHint = {
1751
1768
  $type: 'RuntimeHint',
1752
1769
  aliasSpec: 'aliasSpec',
1753
1770
  catchSpec: 'catchSpec',
1771
+ emptySpec: 'emptySpec',
1754
1772
  thenSpec: 'thenSpec'
1755
1773
  } as const;
1756
1774
 
@@ -1864,7 +1882,7 @@ export function isStandaloneStatement(item: unknown): item is StandaloneStatemen
1864
1882
  }
1865
1883
 
1866
1884
  export interface Statement extends langium.AstNode {
1867
- readonly $container: ArrayLiteral | CaseEntry | ConditionalFlowStep | Else | FlowEntry | ForEach | Handler | If | StandaloneStatement | ThenSpec | WorkflowDefinition;
1885
+ readonly $container: ArrayLiteral | CaseEntry | ConditionalFlowStep | Else | EmptySpec | FlowEntry | ForEach | Handler | If | StandaloneStatement | ThenSpec | WorkflowDefinition;
1868
1886
  readonly $type: 'Statement';
1869
1887
  hints: Array<RuntimeHint>;
1870
1888
  pattern: Pattern;
@@ -2102,6 +2120,7 @@ export type AgentlangAstType = {
2102
2120
  Delete: Delete
2103
2121
  DirectiveDefinition: DirectiveDefinition
2104
2122
  Else: Else
2123
+ EmptySpec: EmptySpec
2105
2124
  EntityActionsDefinitions: EntityActionsDefinitions
2106
2125
  EntityDefinition: EntityDefinition
2107
2126
  EnumSpec: EnumSpec
@@ -2517,6 +2536,15 @@ export class AgentlangAstReflection extends langium.AbstractAstReflection {
2517
2536
  },
2518
2537
  superTypes: []
2519
2538
  },
2539
+ EmptySpec: {
2540
+ name: EmptySpec.$type,
2541
+ properties: {
2542
+ stmt: {
2543
+ name: EmptySpec.stmt
2544
+ }
2545
+ },
2546
+ superTypes: []
2547
+ },
2520
2548
  EntityActionsDefinitions: {
2521
2549
  name: EntityActionsDefinitions.$type,
2522
2550
  properties: {
@@ -3361,6 +3389,9 @@ export class AgentlangAstReflection extends langium.AbstractAstReflection {
3361
3389
  catchSpec: {
3362
3390
  name: RuntimeHint.catchSpec
3363
3391
  },
3392
+ emptySpec: {
3393
+ name: RuntimeHint.emptySpec
3394
+ },
3364
3395
  thenSpec: {
3365
3396
  name: RuntimeHint.thenSpec
3366
3397
  }