agentlang 0.0.50 → 0.0.52

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 (62) hide show
  1. package/out/api/http.d.ts.map +1 -1
  2. package/out/api/http.js +207 -1
  3. package/out/api/http.js.map +1 -1
  4. package/out/extension/main.cjs +250 -250
  5. package/out/extension/main.cjs.map +2 -2
  6. package/out/language/main.cjs +502 -502
  7. package/out/language/main.cjs.map +2 -2
  8. package/out/language/syntax.d.ts +7 -0
  9. package/out/language/syntax.d.ts.map +1 -1
  10. package/out/language/syntax.js +16 -0
  11. package/out/language/syntax.js.map +1 -1
  12. package/out/runtime/agents/common.d.ts +7 -0
  13. package/out/runtime/agents/common.d.ts.map +1 -1
  14. package/out/runtime/agents/common.js +34 -0
  15. package/out/runtime/agents/common.js.map +1 -1
  16. package/out/runtime/agents/impl/anthropic.js +1 -1
  17. package/out/runtime/agents/impl/anthropic.js.map +1 -1
  18. package/out/runtime/agents/impl/openai.js +1 -1
  19. package/out/runtime/agents/impl/openai.js.map +1 -1
  20. package/out/runtime/agents/registry.d.ts.map +1 -1
  21. package/out/runtime/agents/registry.js +9 -6
  22. package/out/runtime/agents/registry.js.map +1 -1
  23. package/out/runtime/interpreter.d.ts.map +1 -1
  24. package/out/runtime/interpreter.js +5 -2
  25. package/out/runtime/interpreter.js.map +1 -1
  26. package/out/runtime/loader.d.ts.map +1 -1
  27. package/out/runtime/loader.js +25 -6
  28. package/out/runtime/loader.js.map +1 -1
  29. package/out/runtime/module.d.ts +23 -1
  30. package/out/runtime/module.d.ts.map +1 -1
  31. package/out/runtime/module.js +95 -11
  32. package/out/runtime/module.js.map +1 -1
  33. package/out/runtime/modules/ai.d.ts +2 -6
  34. package/out/runtime/modules/ai.d.ts.map +1 -1
  35. package/out/runtime/modules/ai.js +1 -31
  36. package/out/runtime/modules/ai.js.map +1 -1
  37. package/out/runtime/resolvers/authinfo.d.ts +8 -0
  38. package/out/runtime/resolvers/authinfo.d.ts.map +1 -0
  39. package/out/runtime/resolvers/authinfo.js +14 -0
  40. package/out/runtime/resolvers/authinfo.js.map +1 -0
  41. package/out/runtime/resolvers/interface.d.ts +1 -7
  42. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  43. package/out/runtime/resolvers/interface.js +3 -16
  44. package/out/runtime/resolvers/interface.js.map +1 -1
  45. package/out/runtime/resolvers/sqldb/database.d.ts +3 -1
  46. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  47. package/out/runtime/resolvers/sqldb/database.js +72 -3
  48. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  49. package/package.json +156 -153
  50. package/src/api/http.ts +234 -0
  51. package/src/language/syntax.ts +21 -0
  52. package/src/runtime/agents/common.ts +41 -0
  53. package/src/runtime/agents/impl/anthropic.ts +1 -1
  54. package/src/runtime/agents/impl/openai.ts +1 -1
  55. package/src/runtime/agents/registry.ts +12 -6
  56. package/src/runtime/interpreter.ts +5 -10
  57. package/src/runtime/loader.ts +24 -11
  58. package/src/runtime/module.ts +112 -12
  59. package/src/runtime/modules/ai.ts +3 -38
  60. package/src/runtime/resolvers/authinfo.ts +14 -0
  61. package/src/runtime/resolvers/interface.ts +2 -19
  62. package/src/runtime/resolvers/sqldb/database.ts +76 -3
@@ -105,7 +105,7 @@ export class OpenAIProvider implements AgentServiceProvider {
105
105
  async invoke(messages: BaseMessage[], externalToolSpecs: any[] | undefined): Promise<AIResponse> {
106
106
  if (!this.config.apiKey) {
107
107
  throw new Error(
108
- 'OpenAI API key is required. Set OPENAI_API_KEY environment variable or provide apiKey in config.'
108
+ 'OpenAI API key is required. Set OPENAI_API_KEY environment variable or use setLocalEnv("OPENAI_API_KEY", key) or provide apiKey in config.'
109
109
  );
110
110
  }
111
111
  if (externalToolSpecs) {
@@ -1,5 +1,6 @@
1
1
  import { OpenAIProvider } from './impl/openai.js';
2
2
  import { AnthropicProvider } from './impl/anthropic.js';
3
+ import { getLocalEnv } from '../auth/defs.js';
3
4
 
4
5
  const Providers = new Map().set('openai', OpenAIProvider).set('anthropic', AnthropicProvider);
5
6
 
@@ -18,6 +19,7 @@ export function provider(service: string) {
18
19
  p = Providers.get(availableService);
19
20
  if (p) return p;
20
21
  }
22
+
21
23
  const errorMessage = `${service} provider requested but ${service.toUpperCase()}_API_KEY not found. Available providers: ${getAvailableProviders().join(', ') || 'none'}`;
22
24
  console.error(errorMessage);
23
25
  throw new Error(errorMessage);
@@ -30,19 +32,19 @@ export function provider(service: string) {
30
32
  function isProviderAvailable(service: string): boolean {
31
33
  switch (service) {
32
34
  case 'openai':
33
- return !!process.env.OPENAI_API_KEY;
35
+ return !!(process.env.OPENAI_API_KEY || getLocalEnv('OPENAI_API_KEY'));
34
36
  case 'anthropic':
35
- return !!process.env.ANTHROPIC_API_KEY;
37
+ return !!(process.env.ANTHROPIC_API_KEY || getLocalEnv('ANTHROPIC_API_KEY'));
36
38
  default:
37
39
  return false;
38
40
  }
39
41
  }
40
42
 
41
43
  function getAvailableProvider(): string | null {
42
- if (process.env.ANTHROPIC_API_KEY) {
44
+ if (process.env.ANTHROPIC_API_KEY || getLocalEnv('ANTHROPIC_API_KEY')) {
43
45
  return 'anthropic';
44
46
  }
45
- if (process.env.OPENAI_API_KEY) {
47
+ if (process.env.OPENAI_API_KEY || getLocalEnv('OPENAI_API_KEY')) {
46
48
  return 'openai';
47
49
  }
48
50
  return null;
@@ -50,7 +52,11 @@ function getAvailableProvider(): string | null {
50
52
 
51
53
  function getAvailableProviders(): string[] {
52
54
  const available: string[] = [];
53
- if (process.env.ANTHROPIC_API_KEY) available.push('anthropic');
54
- if (process.env.OPENAI_API_KEY) available.push('openai');
55
+ if (process.env.ANTHROPIC_API_KEY || getLocalEnv('ANTHROPIC_API_KEY')) {
56
+ available.push('anthropic');
57
+ }
58
+ if (process.env.OPENAI_API_KEY || getLocalEnv('OPENAI_API_KEY')) {
59
+ available.push('openai');
60
+ }
55
61
  return available;
56
62
  }
@@ -47,7 +47,8 @@ import {
47
47
  Relationship,
48
48
  Workflow,
49
49
  } from './module.js';
50
- import { JoinInfo, Resolver, ResolverAuthInfo } from './resolvers/interface.js';
50
+ import { JoinInfo, Resolver } from './resolvers/interface.js';
51
+ import { ResolverAuthInfo } from './resolvers/authinfo.js';
51
52
  import { SqlDbResolver } from './resolvers/sqldb/impl.js';
52
53
  import {
53
54
  CrudType,
@@ -70,15 +71,7 @@ import {
70
71
  import { getResolver, getResolverNameForPath } from './resolvers/registry.js';
71
72
  import { parseStatement, parseWorkflow } from '../language/parser.js';
72
73
  import { ActiveSessionInfo, AdminSession, AdminUserId } from './auth/defs.js';
73
- import {
74
- AgentInstance,
75
- AgentEntityName,
76
- AgentFqName,
77
- findAgentByName,
78
- FlowSpec,
79
- getAgentFlow,
80
- FlowStep,
81
- } from './modules/ai.js';
74
+ import { AgentInstance, AgentEntityName, AgentFqName, findAgentByName } from './modules/ai.js';
82
75
  import { logger } from './logger.js';
83
76
  import {
84
77
  FlowSuspensionTag,
@@ -97,6 +90,7 @@ import {
97
90
  import { invokeModuleFn } from './jsmodules.js';
98
91
  import { invokeOpenApiEvent, isOpenApiEventInstance } from './openapi.js';
99
92
  import { fetchDoc } from './docs.js';
93
+ import { FlowSpec, FlowStep, getAgentFlow } from './agents/common.js';
100
94
 
101
95
  export type Result = any;
102
96
 
@@ -169,6 +163,7 @@ export class Environment extends Instance {
169
163
  this.activeResolvers = new Map<string, Resolver>();
170
164
  this.activeTransactions = new Map<string, string>();
171
165
  this.activeCatchHandlers = new Array<CatchHandlers>();
166
+ this.attributes.set('process', process);
172
167
  }
173
168
  }
174
169
 
@@ -48,6 +48,7 @@ import {
48
48
  removeModule,
49
49
  newInstanceAttributes,
50
50
  addAgent,
51
+ fetchModule,
51
52
  } from './module.js';
52
53
  import {
53
54
  escapeSpecialChars,
@@ -67,13 +68,7 @@ import { maybeGetValidationErrors, parse, parseModule, parseWorkflow } from '../
67
68
  import { logger } from './logger.js';
68
69
  import { Environment, evaluateStatements, GlobalEnvironment } from './interpreter.js';
69
70
  import { createPermission, createRole } from './modules/auth.js';
70
- import {
71
- AgentEntityName,
72
- CoreAIModuleName,
73
- LlmEntityName,
74
- registerAgentFlow,
75
- registerFlow,
76
- } from './modules/ai.js';
71
+ import { AgentEntityName, CoreAIModuleName, LlmEntityName } from './modules/ai.js';
77
72
  import { GenericResolver, GenericResolverMethods } from './resolvers/interface.js';
78
73
  import { registerResolver, setResolver } from './resolvers/registry.js';
79
74
  import { Config, ConfigSchema, setAppConfig } from './state.js';
@@ -81,6 +76,7 @@ import { getModuleFn, importModule } from './jsmodules.js';
81
76
  import { SetSubscription } from './defs.js';
82
77
  import { ExtendedFileSystem } from '../utils/fs/interfaces.js';
83
78
  import z from 'zod';
79
+ import { registerAgentFlow, registerFlow } from './agents/common.js';
84
80
 
85
81
  export async function extractDocument(
86
82
  fileName: string,
@@ -431,7 +427,7 @@ export function addWorkflowFromDef(def: WorkflowDefinition, moduleName: string):
431
427
 
432
428
  const StandaloneStatements = new Map<string, Statement[]>();
433
429
 
434
- function addStandaloneStatement(stmt: Statement, moduleName: string) {
430
+ function addStandaloneStatement(stmt: Statement, moduleName: string, userDefined = true) {
435
431
  let stmts: Array<Statement> | undefined = StandaloneStatements.get(moduleName);
436
432
  if (stmts == undefined) {
437
433
  stmts = new Array<Statement>();
@@ -440,6 +436,10 @@ function addStandaloneStatement(stmt: Statement, moduleName: string) {
440
436
  if (!StandaloneStatements.has(moduleName)) {
441
437
  StandaloneStatements.set(moduleName, stmts);
442
438
  }
439
+ if (userDefined) {
440
+ const m = fetchModule(moduleName);
441
+ m.addStandaloneStatement(stmt);
442
+ }
443
443
  }
444
444
 
445
445
  export async function runStandaloneStatements() {
@@ -486,6 +486,8 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
486
486
  });
487
487
  attrsStrs.push(`type "flow-exec"`);
488
488
  attrs.set('type', 'flow-exec');
489
+ attrsStrs.push(`flows "${fnames}"`);
490
+ attrs.set('flows', fnames);
489
491
  } else {
490
492
  throw new Error(`Invalid flows list in agent ${name}`);
491
493
  }
@@ -526,10 +528,11 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
526
528
  }, @upsert}`;
527
529
  let wf = createAgent;
528
530
  if (llmName) {
529
- wf = `{${CoreAIModuleName}/${LlmEntityName} {name "${llmName}"}, @upsert}; ${wf}`;
531
+ const service = process.env.ANTHROPIC_API_KEY ? 'anthropic' : 'openai';
532
+ wf = `{${CoreAIModuleName}/${LlmEntityName} {name "${llmName}", service "${service}"}, @upsert}; ${wf}`;
530
533
  }
531
534
  (await parseWorkflow(`workflow A {${wf}}`)).statements.forEach((stmt: Statement) => {
532
- addStandaloneStatement(stmt, moduleName);
535
+ addStandaloneStatement(stmt, moduleName, false);
533
536
  });
534
537
  addAgent(def.name, attrs, moduleName);
535
538
  }
@@ -569,7 +572,17 @@ function processAgentArrayValue(expr: Expr | undefined, attrName: string): strin
569
572
 
570
573
  function addFlowDefinition(def: FlowDefinition, moduleName: string) {
571
574
  if (def.body && def.$cstNode) {
572
- registerFlow(`${moduleName}/${def.name}`, def.$cstNode.text);
575
+ const m = fetchModule(moduleName);
576
+ const sdef = def.$cstNode.text;
577
+ const idx = sdef.indexOf('{');
578
+ let f = '';
579
+ if (idx > 0) {
580
+ f = sdef.substring(idx + 1, sdef.lastIndexOf('}')).trim();
581
+ } else {
582
+ f = sdef;
583
+ }
584
+ m.addFlow(def.name, f);
585
+ registerFlow(`${moduleName}/${def.name}`, f);
573
586
  }
574
587
  }
575
588
 
@@ -816,42 +816,63 @@ 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
- if (key != 'moduleName') {
854
- const v = isString(value) ? `"${value}"` : value;
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
+ }
855
876
  attrs.push(` ${key} ${v}`);
856
877
  }
857
878
  });
@@ -1295,6 +1316,59 @@ export class Workflow extends ModuleEntry {
1295
1316
  }
1296
1317
  }
1297
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
+
1298
1372
  const EmptyWorkflow: Workflow = new Workflow('', [], DefaultModuleName);
1299
1373
 
1300
1374
  export function isEmptyWorkflow(wf: Workflow): boolean {
@@ -1340,6 +1414,32 @@ export class Module {
1340
1414
  return this.removeEntry(Agent.EscapeName(agentName));
1341
1415
  }
1342
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
+
1343
1443
  private getEntryIndex(entryName: string): number {
1344
1444
  return this.entries.findIndex((v: ModuleEntry) => {
1345
1445
  return v.name == entryName;
@@ -22,7 +22,7 @@ import {
22
22
  systemMessage,
23
23
  } from '../agents/provider.js';
24
24
  import { AIMessage, BaseMessage, HumanMessage } from '@langchain/core/messages';
25
- import { FlowExecInstructions, PlannerInstructions } from '../agents/common.js';
25
+ import { FlowExecInstructions, FlowStep, PlannerInstructions } from '../agents/common.js';
26
26
  import { PathAttributeNameQuery } from '../defs.js';
27
27
  import { logger } from '../logger.js';
28
28
 
@@ -49,6 +49,7 @@ entity ${AgentEntityName} {
49
49
  channels String @optional, // comma-separated list of channel names
50
50
  output String @optional, // fq-name of another agent to which the result will be pushed
51
51
  role String @optional,
52
+ flows String @optional,
52
53
  llm String
53
54
  }
54
55
 
@@ -95,6 +96,7 @@ export class AgentInstance {
95
96
  runWorkflows: boolean = true;
96
97
  output: string | undefined;
97
98
  role: string | undefined;
99
+ flows: string | undefined;
98
100
  private toolsArray: string[] | undefined = undefined;
99
101
  private hasModuleTools = false;
100
102
  private withSession = true;
@@ -416,40 +418,3 @@ export async function saveAgentChatSession(chatId: string, messages: any[], env:
416
418
  export function agentName(agentInstance: Instance): string {
417
419
  return agentInstance.lookup('name');
418
420
  }
419
-
420
- export type FlowSpec = string;
421
- export type FlowStep = string;
422
-
423
- const AgentFlows = new Map<string, string[]>();
424
- const FlowRegistry = new Map<string, FlowSpec>();
425
-
426
- export function registerFlow(name: string, flow: FlowSpec): string {
427
- FlowRegistry.set(name, flow);
428
- return name;
429
- }
430
-
431
- export function getFlow(name: string): FlowSpec | undefined {
432
- return FlowRegistry.get(name);
433
- }
434
-
435
- export function registerAgentFlow(agentName: string, flowSpecName: string): string {
436
- let currentFlows = AgentFlows.get(agentName);
437
- if (currentFlows) {
438
- currentFlows.push(flowSpecName);
439
- } else {
440
- currentFlows = new Array<string>();
441
- currentFlows.push(flowSpecName);
442
- }
443
- AgentFlows.set(agentName, currentFlows);
444
- return agentName;
445
- }
446
-
447
- // Return the first flow registered with the agent.
448
- export function getAgentFlow(agentName: string): FlowSpec | undefined {
449
- const currentFlows = AgentFlows.get(agentName);
450
- if (currentFlows) {
451
- return getFlow(currentFlows[0]);
452
- } else {
453
- return undefined;
454
- }
455
- }
@@ -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;