agentlang 0.0.40 → 0.0.51

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 (80) hide show
  1. package/out/extension/main.cjs +250 -250
  2. package/out/extension/main.cjs.map +2 -2
  3. package/out/language/generated/ast.d.ts +57 -21
  4. package/out/language/generated/ast.d.ts.map +1 -1
  5. package/out/language/generated/ast.js +80 -26
  6. package/out/language/generated/ast.js.map +1 -1
  7. package/out/language/generated/grammar.d.ts.map +1 -1
  8. package/out/language/generated/grammar.js +337 -115
  9. package/out/language/generated/grammar.js.map +1 -1
  10. package/out/language/main.cjs +900 -637
  11. package/out/language/main.cjs.map +3 -3
  12. package/out/language/syntax.d.ts +7 -0
  13. package/out/language/syntax.d.ts.map +1 -1
  14. package/out/language/syntax.js +16 -0
  15. package/out/language/syntax.js.map +1 -1
  16. package/out/runtime/agents/common.d.ts +9 -1
  17. package/out/runtime/agents/common.d.ts.map +1 -1
  18. package/out/runtime/agents/common.js +83 -1
  19. package/out/runtime/agents/common.js.map +1 -1
  20. package/out/runtime/agents/registry.js +3 -1
  21. package/out/runtime/agents/registry.js.map +1 -1
  22. package/out/runtime/auth/cognito.js +1 -1
  23. package/out/runtime/defs.d.ts +1 -0
  24. package/out/runtime/defs.d.ts.map +1 -1
  25. package/out/runtime/defs.js +1 -0
  26. package/out/runtime/defs.js.map +1 -1
  27. package/out/runtime/interpreter.d.ts +5 -0
  28. package/out/runtime/interpreter.d.ts.map +1 -1
  29. package/out/runtime/interpreter.js +81 -9
  30. package/out/runtime/interpreter.js.map +1 -1
  31. package/out/runtime/loader.d.ts.map +1 -1
  32. package/out/runtime/loader.js +111 -40
  33. package/out/runtime/loader.js.map +1 -1
  34. package/out/runtime/module.d.ts +24 -2
  35. package/out/runtime/module.d.ts.map +1 -1
  36. package/out/runtime/module.js +103 -12
  37. package/out/runtime/module.js.map +1 -1
  38. package/out/runtime/modules/ai.d.ts +9 -0
  39. package/out/runtime/modules/ai.d.ts.map +1 -1
  40. package/out/runtime/modules/ai.js +50 -9
  41. package/out/runtime/modules/ai.js.map +1 -1
  42. package/out/runtime/modules/auth.d.ts.map +1 -1
  43. package/out/runtime/modules/auth.js.map +1 -1
  44. package/out/runtime/modules/core.d.ts +1 -0
  45. package/out/runtime/modules/core.d.ts.map +1 -1
  46. package/out/runtime/modules/core.js +15 -5
  47. package/out/runtime/modules/core.js.map +1 -1
  48. package/out/runtime/resolvers/authinfo.d.ts +8 -0
  49. package/out/runtime/resolvers/authinfo.d.ts.map +1 -0
  50. package/out/runtime/resolvers/authinfo.js +14 -0
  51. package/out/runtime/resolvers/authinfo.js.map +1 -0
  52. package/out/runtime/resolvers/interface.d.ts +1 -7
  53. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  54. package/out/runtime/resolvers/interface.js +3 -16
  55. package/out/runtime/resolvers/interface.js.map +1 -1
  56. package/out/runtime/resolvers/sqldb/database.d.ts +3 -1
  57. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  58. package/out/runtime/resolvers/sqldb/database.js +72 -3
  59. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  60. package/out/syntaxes/agentlang.monarch.js +3 -3
  61. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  62. package/package.json +156 -153
  63. package/src/language/agentlang.langium +12 -4
  64. package/src/language/generated/ast.ts +144 -49
  65. package/src/language/generated/grammar.ts +337 -115
  66. package/src/language/syntax.ts +21 -0
  67. package/src/runtime/agents/common.ts +91 -1
  68. package/src/runtime/agents/registry.ts +3 -3
  69. package/src/runtime/auth/cognito.ts +3 -3
  70. package/src/runtime/defs.ts +1 -0
  71. package/src/runtime/interpreter.ts +117 -8
  72. package/src/runtime/loader.ts +113 -39
  73. package/src/runtime/module.ts +123 -13
  74. package/src/runtime/modules/ai.ts +67 -11
  75. package/src/runtime/modules/auth.ts +6 -1
  76. package/src/runtime/modules/core.ts +16 -4
  77. package/src/runtime/resolvers/authinfo.ts +14 -0
  78. package/src/runtime/resolvers/interface.ts +2 -19
  79. package/src/runtime/resolvers/sqldb/database.ts +76 -3
  80. package/src/syntaxes/agentlang.monarch.ts +3 -3
@@ -816,42 +816,65 @@ export class Agent extends Record {
816
816
  return this.removeAgentAttribute('type');
817
817
  }
818
818
 
819
- setTools(tools: string[]): Agent {
820
- this.attributes.set('tools', tools.join(','));
819
+ private setStrings(attrName: string, v: string[]): Agent {
820
+ this.attributes.set(attrName, v.join(','));
821
821
  return this;
822
822
  }
823
823
 
824
- getTools(): string[] | undefined {
825
- const tools = this.attributes.get('tools');
826
- if (tools) {
827
- return tools.split(',');
824
+ private getStrings(attrName: string): string[] | undefined {
825
+ const v = this.attributes.get(attrName);
826
+ if (v) {
827
+ return v.split(',');
828
828
  } else {
829
829
  return undefined;
830
830
  }
831
831
  }
832
832
 
833
+ setTools(tools: string[]): Agent {
834
+ return this.setStrings('tools', tools);
835
+ }
836
+
837
+ getTools(): string[] | undefined {
838
+ return this.getStrings('tools');
839
+ }
840
+
833
841
  removeTools(): Agent {
834
842
  return this.removeAgentAttribute('tools');
835
843
  }
836
844
 
837
845
  setDocuments(docs: string[]): Agent {
838
- this.attributes.set('documents', docs.join(','));
839
- return this;
846
+ return this.setStrings('documents', docs);
840
847
  }
841
848
 
842
- getDocuments(): string {
843
- return this.attributes.get('documents');
849
+ getDocuments(): string[] | undefined {
850
+ return this.getStrings('documents');
844
851
  }
845
852
 
846
853
  removeDocuments(): Agent {
847
854
  return this.removeAgentAttribute('documents');
848
855
  }
849
856
 
857
+ setFlows(flows: string[]): Agent {
858
+ return this.setStrings('flows', flows);
859
+ }
860
+
861
+ getFlows(): string[] | undefined {
862
+ return this.getStrings('flows');
863
+ }
864
+
850
865
  override toString(): string {
851
866
  const attrs = new Array<string>();
852
867
  this.attributes.forEach((value: any, key: string) => {
853
- const v = isString(value) ? `"${value}"` : value;
854
- attrs.push(` ${key} ${v}`);
868
+ const skip = key == 'moduleName' || (key == 'type' && value == 'flow-exec');
869
+ if (!skip) {
870
+ let v = value;
871
+ if (key == 'flows') {
872
+ v = `[${v}]`;
873
+ } else if (isString(v)) {
874
+ v = `"${v}"`;
875
+ }
876
+ attrs.push(` ${key} ${v}`);
877
+ }
855
878
  });
856
879
  return `agent ${Agent.NormalizeName(this.name)}
857
880
  {
@@ -1293,6 +1316,59 @@ export class Workflow extends ModuleEntry {
1293
1316
  }
1294
1317
  }
1295
1318
 
1319
+ export class Flow extends ModuleEntry {
1320
+ flowSteps: string[];
1321
+
1322
+ constructor(name: string, moduleName: string, flow?: string) {
1323
+ super(name, moduleName);
1324
+ this.flowSteps = new Array<string>();
1325
+ flow?.split('\n').forEach((step: string) => {
1326
+ const s = step.trim();
1327
+ if (s.length > 0) {
1328
+ this.flowSteps.push(s);
1329
+ }
1330
+ });
1331
+ }
1332
+
1333
+ getFlow(): string {
1334
+ return this.flowSteps.join('\n');
1335
+ }
1336
+
1337
+ removeStep(index: number): Flow {
1338
+ this.flowSteps.splice(index, 1);
1339
+ return this;
1340
+ }
1341
+
1342
+ insertStep(index: number, s: string): Flow {
1343
+ this.flowSteps.splice(index, 0, s);
1344
+ return this;
1345
+ }
1346
+
1347
+ stepsCount(): number {
1348
+ return this.flowSteps.length;
1349
+ }
1350
+
1351
+ override toString(): string {
1352
+ return `flow ${this.name} {
1353
+ ${this.getFlow()}
1354
+ }`;
1355
+ }
1356
+ }
1357
+
1358
+ class StandaloneStatement extends ModuleEntry {
1359
+ stmt: Statement;
1360
+
1361
+ constructor(stmt: Statement, moduleName: string) {
1362
+ super(crypto.randomUUID(), moduleName);
1363
+ this.stmt = stmt;
1364
+ }
1365
+
1366
+ override toString(): string {
1367
+ if (this.stmt.$cstNode) return this.stmt.$cstNode?.text;
1368
+ else return '';
1369
+ }
1370
+ }
1371
+
1296
1372
  const EmptyWorkflow: Workflow = new Workflow('', [], DefaultModuleName);
1297
1373
 
1298
1374
  export function isEmptyWorkflow(wf: Workflow): boolean {
@@ -1338,6 +1414,32 @@ export class Module {
1338
1414
  return this.removeEntry(Agent.EscapeName(agentName));
1339
1415
  }
1340
1416
 
1417
+ addFlow(name: string, flowString?: string): Flow {
1418
+ const flow: Flow = new Flow(name, this.name, flowString);
1419
+ this.addEntry(flow);
1420
+ return flow;
1421
+ }
1422
+
1423
+ getFlow(name: string): Flow | undefined {
1424
+ if (this.hasEntry(name)) {
1425
+ return this.getEntry(name) as Flow;
1426
+ }
1427
+ return undefined;
1428
+ }
1429
+
1430
+ addStandaloneStatement(stmt: Statement): StandaloneStatement {
1431
+ const s = new StandaloneStatement(stmt, this.name);
1432
+ this.addEntry(s);
1433
+ return s;
1434
+ }
1435
+
1436
+ getStandaloneStatement(name: string): StandaloneStatement | undefined {
1437
+ if (this.hasEntry(name)) {
1438
+ return this.getEntry(name) as StandaloneStatement;
1439
+ }
1440
+ return undefined;
1441
+ }
1442
+
1341
1443
  private getEntryIndex(entryName: string): number {
1342
1444
  return this.entries.findIndex((v: ModuleEntry) => {
1343
1445
  return v.name == entryName;
@@ -2848,14 +2950,22 @@ export function assertInstance(obj: any) {
2848
2950
 
2849
2951
  const IsAgentEventMeta = 'is-agent-event';
2850
2952
  const EventAgentName = 'event-agent-name';
2953
+ const DocumentationMetaTag = 'documentation';
2851
2954
 
2852
- export function defineAgentEvent(moduleName: string, agentName: string) {
2955
+ export function defineAgentEvent(moduleName: string, agentName: string, instruction?: string) {
2853
2956
  const module = fetchModule(moduleName);
2854
2957
  const event: Record = new Event(agentName, moduleName);
2855
2958
  event.addAttribute('message', { type: 'Any' });
2856
2959
  event.addAttribute('chatId', { type: 'String' });
2857
2960
  event.addMeta(IsAgentEventMeta, 'y');
2858
2961
  event.addMeta(EventAgentName, agentName);
2962
+ if (instruction) {
2963
+ event.addMeta(
2964
+ DocumentationMetaTag,
2965
+ `This event will trigger an agent which has the instruction - "${instruction}".
2966
+ So make sure to pass all relevant information in the 'message' attribute of this event.`
2967
+ );
2968
+ }
2859
2969
  module.addEntry(event);
2860
2970
  }
2861
2971
 
@@ -5,7 +5,14 @@ import {
5
5
  makeEventEvaluator,
6
6
  parseAndEvaluateStatement,
7
7
  } from '../interpreter.js';
8
- import { fetchModule, Instance, instanceToObject, isModule } from '../module.js';
8
+ import {
9
+ fetchModule,
10
+ Instance,
11
+ instanceToObject,
12
+ isModule,
13
+ makeInstance,
14
+ newInstanceAttributes,
15
+ } from '../module.js';
9
16
  import { provider } from '../agents/registry.js';
10
17
  import {
11
18
  AgentServiceProvider,
@@ -15,7 +22,7 @@ import {
15
22
  systemMessage,
16
23
  } from '../agents/provider.js';
17
24
  import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages';
18
- import { PlannerInstructions } from '../agents/common.js';
25
+ import { FlowExecInstructions, FlowStep, PlannerInstructions } from '../agents/common.js';
19
26
  import { PathAttributeNameQuery } from '../defs.js';
20
27
  import { logger } from '../logger.js';
21
28
 
@@ -33,7 +40,8 @@ entity ${LlmEntityName} {
33
40
 
34
41
  entity ${AgentEntityName} {
35
42
  name String @id,
36
- type @enum("chat", "planner") @default("chat"),
43
+ moduleName String @default("${CoreAIModuleName}"),
44
+ type @enum("chat", "planner", "flow-exec") @default("chat"),
37
45
  runWorkflows Boolean @default(true),
38
46
  instruction String @optional,
39
47
  tools String @optional, // comma-separated list of tool names
@@ -41,6 +49,7 @@ entity ${AgentEntityName} {
41
49
  channels String @optional, // comma-separated list of channel names
42
50
  output String @optional, // fq-name of another agent to which the result will be pushed
43
51
  role String @optional,
52
+ flows String @optional,
44
53
  llm String
45
54
  }
46
55
 
@@ -77,6 +86,7 @@ const ProviderDb = new Map<string, AgentServiceProvider>();
77
86
  export class AgentInstance {
78
87
  llm: string = '';
79
88
  name: string = '';
89
+ moduleName: string = CoreAIModuleName;
80
90
  chatId: string | undefined;
81
91
  instruction: string = '';
82
92
  type: string = 'chat';
@@ -86,8 +96,10 @@ export class AgentInstance {
86
96
  runWorkflows: boolean = true;
87
97
  output: string | undefined;
88
98
  role: string | undefined;
99
+ flows: string | undefined;
89
100
  private toolsArray: string[] | undefined = undefined;
90
101
  private hasModuleTools = false;
102
+ private withSession = true;
91
103
 
92
104
  private constructor() {}
93
105
 
@@ -123,29 +135,71 @@ export class AgentInstance {
123
135
  return agent;
124
136
  }
125
137
 
138
+ static FromFlowStep(step: FlowStep, flowAgent: AgentInstance): AgentInstance {
139
+ const fqs = isFqName(step) ? step : `${flowAgent.moduleName}/${step}`;
140
+ const instruction = `Analyse the context and generate the pattern required to invoke ${fqs}.
141
+ Never include references in the pattern. All attribute values must be literals derived from the context.`;
142
+ const inst = makeInstance(
143
+ CoreAIModuleName,
144
+ AgentEntityName,
145
+ newInstanceAttributes()
146
+ .set('llm', flowAgent.llm)
147
+ .set('name', `${step}_agent`)
148
+ .set('moduleName', flowAgent.moduleName)
149
+ .set('instruction', instruction)
150
+ .set('tools', fqs)
151
+ .set('type', 'planner')
152
+ );
153
+ return AgentInstance.FromInstance(inst);
154
+ }
155
+
156
+ disableSession(): AgentInstance {
157
+ this.withSession = false;
158
+ return this;
159
+ }
160
+
161
+ enableSession(): AgentInstance {
162
+ this.withSession = true;
163
+ return this;
164
+ }
165
+
166
+ hasSession(): boolean {
167
+ return this.withSession;
168
+ }
169
+
126
170
  isPlanner(): boolean {
127
171
  return this.hasModuleTools || this.type == 'planner';
128
172
  }
129
173
 
174
+ isFlowExecutor(): boolean {
175
+ return this.type == 'flow-exec';
176
+ }
177
+
130
178
  async invoke(message: string, env: Environment) {
131
179
  const p = await findProviderForLLM(this.llm, env);
132
180
  const agentName = this.name;
133
181
  const chatId = this.chatId || agentName;
134
- const sess: Instance | null = await findAgentChatSession(chatId, env);
135
- let msgs: BaseMessage[] | undefined;
136
182
  const isplnr = this.isPlanner();
183
+ const isflow = !isplnr && this.isFlowExecutor();
184
+ if (isplnr && this.withSession) {
185
+ this.withSession = false;
186
+ }
187
+ if (isflow) {
188
+ this.withSession = false;
189
+ }
190
+ const sess: Instance | null = this.withSession ? await findAgentChatSession(chatId, env) : null;
191
+ let msgs: BaseMessage[] | undefined;
137
192
  if (sess) {
138
193
  msgs = sess.lookup('messages');
139
194
  } else {
140
- msgs = [systemMessage(this.instruction)];
195
+ msgs = [systemMessage(this.instruction || '')];
141
196
  }
142
197
  if (msgs) {
143
198
  try {
144
199
  const sysMsg = msgs[0];
145
- if (isplnr) {
146
- const newSysMsg = systemMessage(
147
- `${PlannerInstructions}\n${this.toolsAsString()}\n${this.instruction}`
148
- );
200
+ if (isplnr || isflow) {
201
+ const s = isplnr ? PlannerInstructions : FlowExecInstructions;
202
+ const newSysMsg = systemMessage(`${s}\n${this.toolsAsString()}\n${this.instruction}`);
149
203
  msgs[0] = newSysMsg;
150
204
  }
151
205
  msgs.push(humanMessage(await this.maybeAddRelevantDocuments(message, env)));
@@ -155,7 +209,9 @@ export class AgentInstance {
155
209
  if (isplnr) {
156
210
  msgs[0] = sysMsg;
157
211
  }
158
- await saveAgentChatSession(chatId, msgs, env);
212
+ if (this.withSession) {
213
+ await saveAgentChatSession(chatId, msgs, env);
214
+ }
159
215
  env.setLastResult(response.content);
160
216
  } catch (err: any) {
161
217
  logger.error(`Error while invoking ${agentName} - ${err}`);
@@ -324,7 +324,12 @@ export async function findUserByEmail(email: string, env: Environment): Promise<
324
324
  );
325
325
  }
326
326
 
327
- export async function updateUser(userId: string, firstName: string, lastName: string, env: Environment): Promise<Result> {
327
+ export async function updateUser(
328
+ userId: string,
329
+ firstName: string,
330
+ lastName: string,
331
+ env: Environment
332
+ ): Promise<Result> {
328
333
  return await evalEvent(
329
334
  'UpdateUser',
330
335
  {
@@ -7,12 +7,13 @@ import {
7
7
  evaluate,
8
8
  evaluateStatements,
9
9
  parseAndEvaluateStatement,
10
+ restartFlow,
10
11
  } from '../interpreter.js';
11
12
  import { logger } from '../logger.js';
12
13
  import { Statement } from '../../language/generated/ast.js';
13
14
  import { parseStatements } from '../../language/parser.js';
14
15
  import { Resolver } from '../resolvers/interface.js';
15
- import { ForceReadPermFlag, PathAttributeName } from '../defs.js';
16
+ import { FlowSuspensionTag, ForceReadPermFlag, PathAttributeName } from '../defs.js';
16
17
 
17
18
  const CoreModuleDefinition = `module ${DefaultModuleName}
18
19
 
@@ -165,9 +166,14 @@ export async function createSuspension(
165
166
 
166
167
  export type Suspension = {
167
168
  continuation: Statement[];
169
+ flowContext?: string[];
168
170
  env: Environment;
169
171
  };
170
172
 
173
+ function isFlowSuspension(cont: string[]): boolean {
174
+ return cont.length > 0 && cont[0] == FlowSuspensionTag;
175
+ }
176
+
171
177
  async function loadSuspension(suspId: string, env?: Environment): Promise<Suspension | undefined> {
172
178
  const newEnv = new Environment('auditlog', env).setInKernelMode(true);
173
179
  const r: any = await parseAndEvaluateStatement(
@@ -178,12 +184,14 @@ async function loadSuspension(suspId: string, env?: Environment): Promise<Suspen
178
184
  if (r instanceof Array && r.length > 0) {
179
185
  const inst: Instance = r[0];
180
186
  const cont = inst.lookup('continuation');
181
- const stmts: Statement[] = await parseStatements(cont);
187
+ const ifs = isFlowSuspension(cont);
188
+ const stmts: Statement[] = ifs ? new Array<Statement>() : await parseStatements(cont);
182
189
  const envStr = inst.lookup('env');
183
190
  const suspEnv: Environment = Environment.FromSerializableObject(JSON.parse(envStr));
184
191
  return {
185
192
  continuation: stmts,
186
193
  env: suspEnv,
194
+ flowContext: ifs ? cont : undefined,
187
195
  };
188
196
  }
189
197
  return undefined;
@@ -210,8 +218,12 @@ export async function restartSuspension(
210
218
  ): Promise<any> {
211
219
  const susp = await loadSuspension(suspId, env);
212
220
  if (susp) {
213
- susp.env.bindSuspensionUserData(userData);
214
- await evaluateStatements(susp.continuation, susp.env);
221
+ if (susp.flowContext) {
222
+ await restartFlow(susp.flowContext, userData, susp.env);
223
+ } else {
224
+ susp.env.bindSuspensionUserData(userData);
225
+ await evaluateStatements(susp.continuation, susp.env);
226
+ }
215
227
  await deleteSuspension(suspId, env);
216
228
  return susp.env.getLastResult();
217
229
  } else {
@@ -0,0 +1,14 @@
1
+ export class ResolverAuthInfo {
2
+ userId: string;
3
+ readForUpdate = false;
4
+ readForDelete = false;
5
+
6
+ constructor(userId: string, readForUpdate?: boolean, readForDelete?: boolean) {
7
+ this.userId = userId;
8
+ if (readForUpdate != undefined) this.readForUpdate = readForUpdate;
9
+ if (readForDelete != undefined) this.readForDelete = readForDelete;
10
+ }
11
+ }
12
+
13
+ // This user-id is only for testing. Override per session from the HTTP layer.
14
+ export const DefaultAuthInfo = new ResolverAuthInfo('9459a305-5ee6-415d-986d-caaf6d6e2828');
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  callPostEventOnSubscription,
3
3
  Environment,
4
- evaluate,
5
4
  runPostCreateEvents,
6
5
  runPostDeleteEvents,
7
6
  runPostUpdateEvents,
@@ -15,24 +14,7 @@ import {
15
14
  Relationship,
16
15
  } from '../module.js';
17
16
  import { CrudType, splitFqName } from '../util.js';
18
-
19
- export class ResolverAuthInfo {
20
- userId: string;
21
- readForUpdate: boolean = false;
22
- readForDelete: boolean = false;
23
-
24
- constructor(userId: string, readForUpdate?: boolean, readForDelete?: boolean) {
25
- this.userId = userId;
26
- if (readForUpdate != undefined) this.readForUpdate = readForUpdate;
27
- if (readForDelete != undefined) this.readForDelete = readForDelete;
28
- }
29
- }
30
-
31
- export const DefaultAuthInfo = new ResolverAuthInfo(
32
- // This user-id is only for testing, per-session user-id needs to be set from
33
- // the HTTP layer.
34
- '9459a305-5ee6-415d-986d-caaf6d6e2828'
35
- );
17
+ import { DefaultAuthInfo, ResolverAuthInfo } from './authinfo.js';
36
18
 
37
19
  export type JoinInfo = {
38
20
  relationship: Relationship;
@@ -242,6 +224,7 @@ export class Resolver {
242
224
  path.getEntryName(),
243
225
  newInstanceAttributes().set('data', result)
244
226
  );
227
+ const { evaluate } = await import('../interpreter.js');
245
228
  return await evaluate(inst);
246
229
  }
247
230
  }
@@ -6,7 +6,7 @@ import {
6
6
  OwnersSuffix,
7
7
  VectorSuffix,
8
8
  } from './dbutil.js';
9
- import { DefaultAuthInfo, ResolverAuthInfo } from '../interface.js';
9
+ import { DefaultAuthInfo, ResolverAuthInfo } from '../authinfo.js';
10
10
  import { canUserCreate, canUserDelete, canUserRead, canUserUpdate } from '../../modules/auth.js';
11
11
  import { Environment, GlobalEnvironment } from '../../interpreter.js';
12
12
  import {
@@ -17,7 +17,6 @@ import {
17
17
  RbacSpecification,
18
18
  Relationship,
19
19
  } from '../../module.js';
20
- import pgvector from 'pgvector';
21
20
  import { isString } from '../../util.js';
22
21
  import {
23
22
  DeletedFlagAttributeName,
@@ -194,10 +193,60 @@ function makeSqliteDataSource(
194
193
  });
195
194
  }
196
195
 
196
+ function isBrowser(): boolean {
197
+ // window for DOM pages, self+importScripts for web workers
198
+ return (
199
+ (typeof window !== 'undefined' && typeof (window as any).document !== 'undefined') ||
200
+ (typeof self !== 'undefined' && typeof (self as any).importScripts === 'function')
201
+ );
202
+ }
203
+
204
+ function defaultLocateFile(file: string): string {
205
+ // Out-of-the-box: use the official CDN in browsers.
206
+ if (isBrowser()) {
207
+ return `https://sql.js.org/dist/${file}`;
208
+ }
209
+ // Node: resolve from node_modules/sql.js/dist
210
+ try {
211
+ /* eslint-disable-next-line @typescript-eslint/no-require-imports */
212
+ const path = require('path');
213
+
214
+ const base = require.resolve('sql.js/dist/sql-wasm.js');
215
+ return path.join(path.dirname(base), file);
216
+ } catch {
217
+ return file;
218
+ }
219
+ }
220
+
221
+ function makeSqljsDataSource(
222
+ entities: EntitySchema[],
223
+ _config: DatabaseConfig | undefined,
224
+ synchronize: boolean = true
225
+ ): DataSource {
226
+ return new DataSource({
227
+ type: 'sqljs',
228
+ autoSave: false,
229
+ sqlJsConfig: {
230
+ locateFile: defaultLocateFile,
231
+ },
232
+ synchronize: synchronize,
233
+ entities: entities,
234
+ });
235
+ }
236
+
197
237
  const DbType = 'sqlite';
198
238
 
199
239
  function getDbType(config?: DatabaseConfig): string {
200
- return process.env.AL_DB_TYPE || config?.type || DbType;
240
+ if (config?.type) return config.type;
241
+ let envType: string | undefined;
242
+ try {
243
+ if (typeof process !== 'undefined' && process.env) {
244
+ envType = process.env.AL_DB_TYPE;
245
+ }
246
+ } catch {}
247
+ if (envType) return envType;
248
+ if (isBrowser()) return 'sqljs';
249
+ return DbType;
201
250
  }
202
251
 
203
252
  function getDsFunction(
@@ -212,6 +261,8 @@ function getDsFunction(
212
261
  return makeSqliteDataSource;
213
262
  case 'postgres':
214
263
  return makePostgresDataSource;
264
+ case 'sqljs':
265
+ return makeSqljsDataSource;
215
266
  default:
216
267
  throw new Error(`Unsupported database type - ${config?.type}`);
217
268
  }
@@ -221,6 +272,15 @@ export function isUsingSqlite(): boolean {
221
272
  return getDbType() == 'sqlite';
222
273
  }
223
274
 
275
+ export function isUsingSqljs(): boolean {
276
+ return getDbType() == 'sqljs';
277
+ }
278
+
279
+ export function isVectorStoreSupported(): boolean {
280
+ // Only Postgres supports pgvector
281
+ return getDbType() === 'postgres';
282
+ }
283
+
224
284
  export async function initDatabase(config: DatabaseConfig | undefined) {
225
285
  if (defaultDataSource == undefined) {
226
286
  const mkds = getDsFunction(config);
@@ -268,9 +328,11 @@ export async function addRowForFullTextSearch(
268
328
  vect: number[],
269
329
  ctx: DbContext
270
330
  ) {
331
+ if (!isVectorStoreSupported()) return;
271
332
  try {
272
333
  const vecTableName = tableName + VectorSuffix;
273
334
  const qb = getDatasourceForTransaction(ctx.txnId).createQueryBuilder();
335
+ const { default: pgvector } = await import('pgvector');
274
336
  await qb
275
337
  .insert()
276
338
  .into(vecTableName)
@@ -282,6 +344,10 @@ export async function addRowForFullTextSearch(
282
344
  }
283
345
 
284
346
  export async function initVectorStore(tableNames: string[], ctx: DbContext) {
347
+ if (!isVectorStoreSupported()) {
348
+ logger.info(`Vector store not supported for ${getDbType()}, skipping init...`);
349
+ return;
350
+ }
285
351
  let notInited = true;
286
352
  tableNames.forEach(async (vecTableName: string) => {
287
353
  const vecRepo = getDatasourceForTransaction(ctx.txnId).getRepository(vecTableName);
@@ -312,9 +378,14 @@ export async function vectorStoreSearch(
312
378
  limit: number,
313
379
  ctx: DbContext
314
380
  ): Promise<any> {
381
+ if (!isVectorStoreSupported()) {
382
+ // Not supported on sqljs/sqlite
383
+ return [];
384
+ }
315
385
  try {
316
386
  const vecTableName = tableName + VectorSuffix;
317
387
  const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
388
+ const { default: pgvector } = await import('pgvector');
318
389
  return await qb.query(
319
390
  `select id from ${vecTableName} order by embedding <-> $1 LIMIT ${limit}`,
320
391
  [pgvector.toSql(searchVec)]
@@ -329,6 +400,7 @@ export async function vectorStoreSearchEntryExists(
329
400
  id: string,
330
401
  ctx: DbContext
331
402
  ): Promise<boolean> {
403
+ if (!isVectorStoreSupported()) return false;
332
404
  try {
333
405
  const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
334
406
  const vecTableName = tableName + VectorSuffix;
@@ -341,6 +413,7 @@ export async function vectorStoreSearchEntryExists(
341
413
  }
342
414
 
343
415
  export async function deleteFullTextSearchEntry(tableName: string, id: string, ctx: DbContext) {
416
+ if (!isVectorStoreSupported()) return;
344
417
  try {
345
418
  const qb = getDatasourceForTransaction(ctx.txnId).getRepository(tableName).manager;
346
419
  const vecTableName = tableName + VectorSuffix;
@@ -1,12 +1,12 @@
1
1
  // Monarch syntax highlighting for the agentlang language.
2
2
  export default {
3
3
  keywords: [
4
- '@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@into','@meta','@oneof','@rbac','@ref','@then','@upsert','@with_unique','agent','allow','and','await','between','contains','create','delete','else','entity','error','event','extends','false','for','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','subscribe','true','update','upsert','where','workflow'
4
+ '@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@into','@meta','@oneof','@rbac','@ref','@then','@upsert','@with_unique','agent','allow','and','await','between','contains','create','delete','else','entity','error','event','extends','false','flow','for','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','subscribe','true','update','upsert','where','workflow'
5
5
  ],
6
6
  operators: [
7
- '!=','*','+',',','-','.','/',':',';','<','<=','<>','=','>','>=','?','@'
7
+ '!=','*','+',',','-','-->','.','/',':',';','<','<=','<>','=','>','>=','?','@'
8
8
  ],
9
- symbols: /!=|\(|\)|\*|\+|,|-|\.|\/|:|;|<|<=|<>|=|>|>=|\?|@|\[|\]|\{|\}/,
9
+ symbols: /!=|\(|\)|\*|\+|,|-|-->|\.|\/|:|;|<|<=|<>|=|>|>=|\?|@|\[|\]|\{|\}/,
10
10
 
11
11
  tokenizer: {
12
12
  initial: [