agentlang 0.9.6 → 0.9.7

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 (78) hide show
  1. package/out/cli/main.d.ts.map +1 -1
  2. package/out/cli/main.js +8 -3
  3. package/out/cli/main.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/generated/ast.js +1 -0
  7. package/out/language/generated/ast.js.map +1 -1
  8. package/out/language/main.cjs +2420 -776
  9. package/out/language/main.cjs.map +4 -4
  10. package/out/runtime/docs.d.ts.map +1 -1
  11. package/out/runtime/docs.js +109 -7
  12. package/out/runtime/docs.js.map +1 -1
  13. package/out/runtime/embeddings/chunker.d.ts +9 -0
  14. package/out/runtime/embeddings/chunker.d.ts.map +1 -0
  15. package/out/runtime/embeddings/chunker.js +41 -0
  16. package/out/runtime/embeddings/chunker.js.map +1 -0
  17. package/out/runtime/embeddings/index.d.ts +6 -0
  18. package/out/runtime/embeddings/index.d.ts.map +1 -0
  19. package/out/runtime/embeddings/index.js +6 -0
  20. package/out/runtime/embeddings/index.js.map +1 -0
  21. package/out/runtime/embeddings/openai.d.ts +15 -0
  22. package/out/runtime/embeddings/openai.d.ts.map +1 -0
  23. package/out/runtime/embeddings/openai.js +34 -0
  24. package/out/runtime/embeddings/openai.js.map +1 -0
  25. package/out/runtime/embeddings/provider.d.ts +20 -0
  26. package/out/runtime/embeddings/provider.d.ts.map +1 -0
  27. package/out/runtime/embeddings/provider.js +17 -0
  28. package/out/runtime/embeddings/provider.js.map +1 -0
  29. package/out/runtime/embeddings/registry.d.ts +3 -0
  30. package/out/runtime/embeddings/registry.d.ts.map +1 -0
  31. package/out/runtime/embeddings/registry.js +16 -0
  32. package/out/runtime/embeddings/registry.js.map +1 -0
  33. package/out/runtime/interpreter.d.ts.map +1 -1
  34. package/out/runtime/interpreter.js +8 -4
  35. package/out/runtime/interpreter.js.map +1 -1
  36. package/out/runtime/module.d.ts +5 -2
  37. package/out/runtime/module.d.ts.map +1 -1
  38. package/out/runtime/module.js +14 -6
  39. package/out/runtime/module.js.map +1 -1
  40. package/out/runtime/modules/ai.d.ts +2 -0
  41. package/out/runtime/modules/ai.d.ts.map +1 -1
  42. package/out/runtime/modules/ai.js +62 -16
  43. package/out/runtime/modules/ai.js.map +1 -1
  44. package/out/runtime/resolvers/sqldb/database.d.ts +1 -1
  45. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  46. package/out/runtime/resolvers/sqldb/database.js +127 -46
  47. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  48. package/out/runtime/resolvers/sqldb/impl.d.ts +21 -1
  49. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  50. package/out/runtime/resolvers/sqldb/impl.js +176 -45
  51. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  52. package/package.json +188 -185
  53. package/public/pdf.worker.mjs +65152 -0
  54. package/src/cli/main.ts +7 -2
  55. package/src/language/generated/ast.ts +1 -1
  56. package/src/runtime/docs.ts +120 -9
  57. package/src/runtime/embeddings/chunker.ts +50 -0
  58. package/src/runtime/embeddings/index.ts +5 -0
  59. package/src/runtime/embeddings/openai.ts +49 -0
  60. package/src/runtime/embeddings/provider.ts +37 -0
  61. package/src/runtime/embeddings/registry.ts +17 -0
  62. package/src/runtime/interpreter.ts +16 -12
  63. package/src/runtime/module.ts +48 -39
  64. package/src/runtime/modules/ai.ts +76 -19
  65. package/src/runtime/resolvers/sqldb/database.ts +133 -51
  66. package/src/runtime/resolvers/sqldb/impl.ts +235 -58
  67. package/out/setupClassic.d.ts +0 -98
  68. package/out/setupClassic.d.ts.map +0 -1
  69. package/out/setupClassic.js +0 -38
  70. package/out/setupClassic.js.map +0 -1
  71. package/out/setupCommon.d.ts +0 -2
  72. package/out/setupCommon.d.ts.map +0 -1
  73. package/out/setupCommon.js +0 -33
  74. package/out/setupCommon.js.map +0 -1
  75. package/out/setupExtended.d.ts +0 -40
  76. package/out/setupExtended.d.ts.map +0 -1
  77. package/out/setupExtended.js +0 -67
  78. package/out/setupExtended.js.map +0 -1
package/src/cli/main.ts CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  setRuntimeMode_init_schema,
39
39
  setRuntimeMode_migration,
40
40
  setRuntimeMode_prod,
41
+ setRuntimeMode_test,
41
42
  setRuntimeMode_undo_migration,
42
43
  updateEndpoints,
43
44
  } from '../runtime/defs.js';
@@ -216,8 +217,12 @@ async function internDynamicModule(name: string, definition: string): Promise<st
216
217
  setInternDynamicModuleFn(internDynamicModule);
217
218
 
218
219
  export const runModule = async (fileName: string, releaseDb: boolean = false): Promise<void> => {
219
- if (isRuntimeMode_dev() && process.env.NODE_ENV === 'production') {
220
- setRuntimeMode_prod();
220
+ if (isRuntimeMode_dev()) {
221
+ if (process.env.NODE_ENV === 'production') {
222
+ setRuntimeMode_prod();
223
+ } else if (process.env.NODE_ENV === 'test') {
224
+ setRuntimeMode_test();
225
+ }
221
226
  }
222
227
  const r: boolean = await runPreInitTasks();
223
228
  if (!r) {
@@ -3,7 +3,7 @@
3
3
  * DO NOT EDIT MANUALLY!
4
4
  ******************************************************************************/
5
5
 
6
-
6
+ /* eslint-disable */
7
7
  import * as langium from 'langium';
8
8
 
9
9
  export const AgentlangTerminals = {
@@ -1,5 +1,6 @@
1
- import { getFileSystem } from '../utils/fs-utils.js';
2
1
  import { logger } from './logger.js';
2
+ import { isNodeEnv } from '../utils/runtime.js';
3
+ import { getFileSystem } from '../utils/fs-utils.js';
3
4
 
4
5
  const DocFetchers = new Map<string, Function>();
5
6
 
@@ -25,6 +26,39 @@ export async function fetchDoc(url: string): Promise<string | undefined> {
25
26
  else return undefined;
26
27
  }
27
28
 
29
+ let PDFParse: any = null;
30
+ let pdfWorkerSet = false;
31
+
32
+ async function getPDFParse() {
33
+ if (!PDFParse) {
34
+ const pdfModule = await import('pdf-parse');
35
+ PDFParse = pdfModule.PDFParse;
36
+
37
+ // Set up web worker for browser
38
+ if (!isNodeEnv && !pdfWorkerSet && PDFParse.setWorker) {
39
+ // Worker is served from public/ directory
40
+ PDFParse.setWorker('/pdf.worker.mjs');
41
+ pdfWorkerSet = true;
42
+ }
43
+ }
44
+ return PDFParse;
45
+ }
46
+
47
+ async function parsePdfBuffer(buffer: Uint8Array): Promise<string> {
48
+ try {
49
+ const PDFParseClass = await getPDFParse();
50
+ const parser = new PDFParseClass({
51
+ data: buffer,
52
+ verbosity: 0,
53
+ });
54
+ const data = await parser.getText();
55
+ return data.text;
56
+ } catch (error: any) {
57
+ logger.error(`Failed to parse PDF: ${error.message}`);
58
+ throw new Error(`PDF parsing failed: ${error.message}`);
59
+ }
60
+ }
61
+
28
62
  async function httpFetcher(url: string): Promise<string | undefined> {
29
63
  try {
30
64
  const response = await fetch(url, {
@@ -32,12 +66,24 @@ async function httpFetcher(url: string): Promise<string | undefined> {
32
66
  });
33
67
 
34
68
  if (!response.ok) {
35
- logger.error(
36
- `Failed to fetch document ${url}, HTTP error! status: ${response.status} ${response.text} ${response.statusText}`
37
- );
69
+ logger.error(`Failed to fetch document ${url}, HTTP error! status: ${response.status}`);
38
70
  return undefined;
39
71
  }
40
- return await response.text();
72
+
73
+ const contentType = response.headers.get('content-type') || '';
74
+ const content = await response.arrayBuffer();
75
+
76
+ const lowerUrl = url.toLowerCase();
77
+
78
+ // Process based on content type or file extension
79
+ if (contentType.includes('application/pdf') || lowerUrl.endsWith('.pdf')) {
80
+ return await parsePdfBuffer(new Uint8Array(content));
81
+ } else if (contentType.includes('text/markdown') || lowerUrl.endsWith('.md')) {
82
+ return new TextDecoder().decode(content);
83
+ } else {
84
+ // Default to text
85
+ return new TextDecoder().decode(content);
86
+ }
41
87
  } catch (reason: any) {
42
88
  logger.error(`Failed to fetch document ${url}: ${reason}`);
43
89
  }
@@ -45,11 +91,76 @@ async function httpFetcher(url: string): Promise<string | undefined> {
45
91
  }
46
92
 
47
93
  async function fetchFile(path: string): Promise<string> {
48
- const fs = await getFileSystem();
49
- if (path.startsWith('.')) {
50
- path = `${process.cwd()}${path.substring(1)}`;
94
+ const lowerPath = path.toLowerCase();
95
+
96
+ if (lowerPath.endsWith('.pdf')) {
97
+ return await fetchPdfFile(path);
98
+ } else if (
99
+ lowerPath.endsWith('.md') ||
100
+ lowerPath.endsWith('.markdown') ||
101
+ lowerPath.endsWith('.mdown')
102
+ ) {
103
+ return await fetchMarkdownFile(path);
104
+ } else {
105
+ // Default: plain text
106
+ return await fetchTextFile(path);
107
+ }
108
+ }
109
+
110
+ async function fetchPdfFile(path: string): Promise<string> {
111
+ try {
112
+ const fs = await getFileSystem();
113
+
114
+ if (isNodeEnv && path.startsWith('.')) {
115
+ path = `${process.cwd()}${path.substring(1)}`;
116
+ }
117
+
118
+ const content = await fs.readFile(path);
119
+
120
+ let buffer: Uint8Array;
121
+ if (typeof content === 'string') {
122
+ buffer = new TextEncoder().encode(content);
123
+ } else if (Buffer.isBuffer(content)) {
124
+ buffer = new Uint8Array(content);
125
+ } else {
126
+ buffer = new Uint8Array(Buffer.from(content));
127
+ }
128
+
129
+ return await parsePdfBuffer(buffer);
130
+ } catch (error: any) {
131
+ logger.error(`Failed to read PDF file ${path}: ${error.message}`);
132
+ throw error;
133
+ }
134
+ }
135
+
136
+ async function fetchMarkdownFile(path: string): Promise<string> {
137
+ try {
138
+ const fs = await getFileSystem();
139
+
140
+ if (isNodeEnv && path.startsWith('.')) {
141
+ path = `${process.cwd()}${path.substring(1)}`;
142
+ }
143
+
144
+ return await fs.readFile(path);
145
+ } catch (error: any) {
146
+ logger.error(`Failed to read Markdown file ${path}: ${error.message}`);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ async function fetchTextFile(path: string): Promise<string> {
152
+ try {
153
+ const fs = await getFileSystem();
154
+
155
+ if (isNodeEnv && path.startsWith('.')) {
156
+ path = `${process.cwd()}${path.substring(1)}`;
157
+ }
158
+
159
+ return await fs.readFile(path);
160
+ } catch (error: any) {
161
+ logger.error(`Failed to read text file ${path}: ${error.message}`);
162
+ throw error;
51
163
  }
52
- return fs.readFile(path);
53
164
  }
54
165
 
55
166
  registerDocFetcher('http', httpFetcher);
@@ -0,0 +1,50 @@
1
+ export class TextChunker {
2
+ private chunkSize: number;
3
+ private chunkOverlap: number;
4
+ private separators: string[] = ['\n\n', '\n', '. ', ' ', ''];
5
+
6
+ constructor(chunkSize: number = 1000, chunkOverlap: number = 200) {
7
+ this.chunkSize = chunkSize;
8
+ this.chunkOverlap = chunkOverlap;
9
+ }
10
+
11
+ splitText(text: string): string[] {
12
+ if (text.length <= this.chunkSize) {
13
+ return [text];
14
+ }
15
+
16
+ const chunks: string[] = [];
17
+ let start = 0;
18
+
19
+ while (start < text.length) {
20
+ let end = Math.min(start + this.chunkSize, text.length);
21
+
22
+ if (end < text.length) {
23
+ end = this.findBestSplitPoint(text, start, end);
24
+ }
25
+
26
+ chunks.push(text.substring(start, end));
27
+ start = end - this.chunkOverlap;
28
+
29
+ if (start < 0) start = 0;
30
+ if (start >= text.length - this.chunkOverlap) {
31
+ if (start < text.length) {
32
+ chunks.push(text.substring(start));
33
+ }
34
+ break;
35
+ }
36
+ }
37
+
38
+ return chunks;
39
+ }
40
+
41
+ private findBestSplitPoint(text: string, start: number, end: number): number {
42
+ for (const sep of this.separators) {
43
+ const lastSep = text.lastIndexOf(sep, end);
44
+ if (lastSep > start) {
45
+ return Math.min(lastSep + sep.length, end);
46
+ }
47
+ }
48
+ return end;
49
+ }
50
+ }
@@ -0,0 +1,5 @@
1
+ export { EmbeddingProvider, EmbeddingProviderConfig } from './provider.js';
2
+ export { EmbeddingService } from '../resolvers/sqldb/impl.js';
3
+ export { TextChunker } from './chunker.js';
4
+ export { embeddingProvider, getDefaultEmbeddingProvider } from './registry.js';
5
+ export { OpenAIEmbeddingProvider, OpenAIEmbeddingConfig } from './openai.js';
@@ -0,0 +1,49 @@
1
+ import { OpenAIEmbeddings } from '@langchain/openai';
2
+ import { EmbeddingProvider, EmbeddingProviderConfig } from './provider.js';
3
+ import { getLocalEnv } from '../auth/defs.js';
4
+
5
+ export interface OpenAIEmbeddingConfig extends EmbeddingProviderConfig {
6
+ model?: string;
7
+ dimensions?: number;
8
+ maxRetries?: number;
9
+ }
10
+
11
+ export class OpenAIEmbeddingProvider extends EmbeddingProvider {
12
+ private openaiConfig: OpenAIEmbeddingConfig;
13
+
14
+ constructor(config?: EmbeddingProviderConfig) {
15
+ super(config || {});
16
+ this.openaiConfig = (this.config as OpenAIEmbeddingConfig) || {};
17
+ }
18
+
19
+ protected createEmbeddings(): OpenAIEmbeddings {
20
+ const config: any = {
21
+ apiKey: this.resolveApiKey(),
22
+ };
23
+
24
+ if (this.openaiConfig.model) {
25
+ config.model = this.openaiConfig.model;
26
+ }
27
+
28
+ if (this.openaiConfig.dimensions) {
29
+ config.dimensions = this.openaiConfig.dimensions;
30
+ }
31
+
32
+ if (this.openaiConfig.maxRetries !== undefined) {
33
+ config.maxRetries = this.openaiConfig.maxRetries;
34
+ }
35
+
36
+ return new OpenAIEmbeddings(config);
37
+ }
38
+
39
+ protected resolveApiKey(): string {
40
+ if (this.openaiConfig.apiKey) {
41
+ return this.openaiConfig.apiKey;
42
+ }
43
+ return process.env.AGENTLANG_OPENAI_KEY || getLocalEnv('AGENTLANG_OPENAI_KEY') || '';
44
+ }
45
+
46
+ getProviderName(): string {
47
+ return 'openai';
48
+ }
49
+ }
@@ -0,0 +1,37 @@
1
+ import { Embeddings } from '@langchain/core/embeddings';
2
+
3
+ export interface EmbeddingProviderConfig {
4
+ chunkSize?: number;
5
+ chunkOverlap?: number;
6
+ apiKey?: string;
7
+ model?: string;
8
+ [key: string]: any;
9
+ }
10
+
11
+ export abstract class EmbeddingProvider {
12
+ protected config: EmbeddingProviderConfig;
13
+ protected embeddings: Embeddings;
14
+
15
+ constructor(config: EmbeddingProviderConfig = {}) {
16
+ this.config = config;
17
+ this.embeddings = this.createEmbeddings();
18
+ }
19
+
20
+ protected abstract createEmbeddings(): Embeddings;
21
+ protected abstract resolveApiKey(): string;
22
+
23
+ abstract getProviderName(): string;
24
+
25
+ async embedText(text: string): Promise<number[]> {
26
+ return await this.embeddings.embedQuery(text);
27
+ }
28
+
29
+ getConfig(): EmbeddingProviderConfig {
30
+ return { ...this.config };
31
+ }
32
+
33
+ updateConfig(newConfig: Partial<EmbeddingProviderConfig>): void {
34
+ this.config = { ...this.config, ...newConfig };
35
+ this.embeddings = this.createEmbeddings();
36
+ }
37
+ }
@@ -0,0 +1,17 @@
1
+ import { OpenAIEmbeddingProvider } from './openai.js';
2
+
3
+ const EmbeddingProviders = new Map().set('openai', OpenAIEmbeddingProvider);
4
+
5
+ export function embeddingProvider(service: string): any {
6
+ const requestedService = service.toLowerCase();
7
+ const provider = EmbeddingProviders.get(requestedService);
8
+ if (provider) {
9
+ return provider;
10
+ } else {
11
+ throw new Error(`No embedding provider found for ${service}`);
12
+ }
13
+ }
14
+
15
+ export function getDefaultEmbeddingProvider(): string {
16
+ return 'openai';
17
+ }
@@ -32,8 +32,8 @@ import {
32
32
  WhereSpec,
33
33
  } from '../language/generated/ast.js';
34
34
  import {
35
- maybeInstanceAsString,
36
35
  defineAgentEvent,
36
+ Event,
37
37
  getOneOfRef,
38
38
  getRelationship,
39
39
  getWorkflow,
@@ -46,15 +46,15 @@ import {
46
46
  isEntityInstance,
47
47
  isEventInstance,
48
48
  isInstanceOfType,
49
+ isOneToOneBetweenRelationship,
49
50
  isTimer,
50
51
  makeInstance,
52
+ maybeInstanceAsString,
51
53
  newInstanceAttributes,
52
54
  PlaceholderRecordEntry,
53
55
  Relationship,
54
- Workflow,
55
56
  setMetaAttributes,
56
- Event,
57
- isOneToOneBetweenRelationship,
57
+ Workflow,
58
58
  } from './module.js';
59
59
  import { JoinInfo, Resolver, WhereClause } from './resolvers/interface.js';
60
60
  import { ResolverAuthInfo } from './resolvers/authinfo.js';
@@ -65,26 +65,26 @@ import {
65
65
  escapeFqName,
66
66
  escapeQueryName,
67
67
  fqNameFromPath,
68
+ isCoreModule,
68
69
  isFqName,
69
70
  isPath,
70
71
  isString,
71
72
  makeCoreModuleName,
72
73
  makeFqName,
74
+ nameToPath,
73
75
  Path,
76
+ preprocessRawConfig,
74
77
  QuerySuffix,
75
78
  restoreSpecialChars,
76
- nameToPath,
77
79
  splitRefs,
78
- isCoreModule,
79
- preprocessRawConfig,
80
80
  } from './util.js';
81
81
  import { getResolver, getResolverNameForPath } from './resolvers/registry.js';
82
82
  import { parseStatement, parseWorkflow } from '../language/parser.js';
83
83
  import { ActiveSessionInfo, AdminSession, AdminUserId } from './auth/defs.js';
84
84
  import {
85
- AgentInstance,
86
85
  AgentEntityName,
87
86
  AgentFqName,
87
+ AgentInstance,
88
88
  findAgentByName,
89
89
  normalizeGeneratedCode,
90
90
  } from './modules/ai.js';
@@ -1755,11 +1755,15 @@ async function handleDocEvent(inst: Instance, env: Environment): Promise<void> {
1755
1755
  const s = await fetchDoc(inst.lookup('url'));
1756
1756
  if (s) {
1757
1757
  const title = inst.lookup('title');
1758
- await parseAndEvaluateStatement(
1759
- `{${CoreAIModuleName}/Document {title "${title}", content "${s}"}}`,
1760
- undefined,
1761
- env
1758
+ const doc = makeInstance(
1759
+ CoreAIModuleName,
1760
+ 'Document',
1761
+ newInstanceAttributes().set('title', title).set('content', s)
1762
1762
  );
1763
+ await computeExprAttributes(doc, undefined, undefined, env);
1764
+ await setMetaAttributes(doc.attributes, env);
1765
+ const res: Resolver = await getResolverForPath('Document', CoreAIModuleName, env);
1766
+ await res.createInstance(doc);
1763
1767
  }
1764
1768
  }
1765
1769
 
@@ -1,57 +1,57 @@
1
1
  import chalk from 'chalk';
2
2
  import {
3
- Statement,
4
- KvPair,
5
- Literal,
3
+ AttributeDefinition,
4
+ Expr,
5
+ FlowDefinition,
6
+ FlowEntry,
6
7
  FnCall,
7
- RelNodes,
8
+ isLiteral,
8
9
  isRelNodes,
9
- AttributeDefinition,
10
- PropertyDefinition,
11
- NodeDefinition,
12
- RecordSchemaDefinition,
10
+ KvPair,
11
+ Literal,
13
12
  MapEntry,
14
- isLiteral,
15
13
  MetaDefinition,
14
+ NodeDefinition,
16
15
  PrePostTriggerDefinition,
17
- TriggerEntry,
18
- Expr,
19
- RbacSpecEntry,
20
- RbacSpecEntries,
16
+ PropertyDefinition,
21
17
  RbacOpr,
22
- WorkflowHeader,
23
- FlowDefinition,
24
- FlowEntry,
18
+ RbacSpecEntries,
19
+ RbacSpecEntry,
20
+ RecordSchemaDefinition,
21
+ RelNodes,
22
+ Statement,
23
+ TriggerEntry,
25
24
  WorkflowDirectives,
25
+ WorkflowHeader,
26
26
  } from '../language/generated/ast.js';
27
27
  import {
28
- Path,
29
- nameToPath,
30
- isString,
31
- isNumber,
32
- isBoolean,
33
- isFqName,
34
- makeFqName,
28
+ asCrudType,
29
+ CrudType,
35
30
  DefaultModuleName,
36
31
  DefaultModules,
37
- joinStatements,
38
- isMinusZero,
39
- now,
40
- findMetaSchema,
32
+ encryptPassword,
33
+ escapeFqName,
41
34
  findAllPrePostTriggerSchema,
42
- CrudType,
43
- asCrudType,
44
- isPath,
35
+ findMetaSchema,
45
36
  findUqCompositeAttributes,
46
- escapeFqName,
47
- encryptPassword,
48
- splitFqName,
49
- splitRefs,
50
37
  forceAsFqName,
51
- validateIdFormat,
38
+ isBoolean,
39
+ isFqName,
40
+ isMinusZero,
41
+ isNumber,
42
+ isPath,
43
+ isString,
44
+ joinStatements,
45
+ makeFqName,
52
46
  nameContainsSepEscape,
47
+ nameToPath,
48
+ now,
49
+ Path,
53
50
  registerInitFunction,
54
51
  ScratchModuleName,
52
+ splitFqName,
53
+ splitRefs,
54
+ validateIdFormat,
55
55
  } from './util.js';
56
56
  import { parseStatement } from '../language/parser.js';
57
57
  import { ActiveSessionInfo, AdminSession } from './auth/defs.js';
@@ -513,7 +513,10 @@ export class Record extends ModuleEntry {
513
513
  }
514
514
 
515
515
  getFullTextSearchAttributes(): string[] | undefined {
516
- const fts: string[] | string | undefined = this.getMeta('fullTextSearch');
516
+ let fts: string[] | string | undefined = this.getMeta('fullTextSearchAttributes');
517
+ if (!fts) {
518
+ fts = this.getMeta('fullTextSearch');
519
+ }
517
520
  if (fts) {
518
521
  if (fts instanceof Array) {
519
522
  return fts as string[];
@@ -527,6 +530,14 @@ export class Record extends ModuleEntry {
527
530
  }
528
531
  }
529
532
 
533
+ getEmbeddingConfig(): { [key: string]: any } | undefined {
534
+ const config = this.getMeta('embeddingConfig');
535
+ if (config && typeof config === 'object') {
536
+ return config as { [key: string]: any };
537
+ }
538
+ return undefined;
539
+ }
540
+
530
541
  private resetUserAttrs() {
531
542
  this.userAttrNames = undefined;
532
543
  this.userAttrsSchema = undefined;
@@ -2496,7 +2507,7 @@ export class Module {
2496
2507
 
2497
2508
  private getEntryIndex(entryName: string): number {
2498
2509
  return this.entries.findIndex((v: ModuleEntry) => {
2499
- return v.name == entryName;
2510
+ return v.name === entryName;
2500
2511
  });
2501
2512
  }
2502
2513
 
@@ -2782,8 +2793,6 @@ export function removeModule(name: string): boolean {
2782
2793
  return false;
2783
2794
  }
2784
2795
 
2785
- addModule(DefaultModuleName);
2786
- addRecord('env', DefaultModuleName);
2787
2796
  addModule(ScratchModuleName);
2788
2797
 
2789
2798
  export function getModuleNames(): string[] {
@@ -60,7 +60,6 @@ import {
60
60
  newAgentScenario,
61
61
  PlannerInstructions,
62
62
  } from '../agents/common.js';
63
- import { PathAttributeNameQuery } from '../defs.js';
64
63
  import { logger } from '../logger.js';
65
64
  import { FlowStep } from '../agents/flows.js';
66
65
  import Handlebars from 'handlebars';
@@ -980,34 +979,92 @@ Only return a pure JSON object with no extra text, annotations etc.`;
980
979
 
981
980
  private async maybeAddRelevantDocuments(message: string, env: Environment): Promise<string> {
982
981
  if (this.documents && this.documents.length > 0) {
983
- const s = `${message}. Relevant documents are: ${this.documents}`;
984
- const result: any[] = await parseHelper(`{${CoreAIModuleName}/Document? "${s}"}`, env);
985
- if (result && result.length > 0) {
986
- const docs: Instance[] = [];
987
- for (let i = 0; i < result.length; ++i) {
988
- const v: any = result[i];
989
- const r: Instance[] = await parseHelper(
990
- `{${CoreAIModuleName}/Document {${PathAttributeNameQuery} "${v.id}"}}`,
982
+ try {
983
+ const docNames = this.documents.split(',').map(d => d.trim());
984
+
985
+ const searchQuery = message;
986
+
987
+ try {
988
+ const semanticResult: any[] = await parseHelper(
989
+ `{${CoreAIModuleName}/Document {content? "${searchQuery.replace(/"/g, '\\"')}"}}`,
991
990
  env
992
991
  );
993
- if (r && r.length > 0) {
994
- docs.push(r[0]);
992
+
993
+ if (semanticResult && semanticResult.length > 0) {
994
+ const docs: Instance[] = [];
995
+ for (const doc of semanticResult) {
996
+ const docTitle = doc.lookup ? doc.lookup('title') : doc.title;
997
+ if (AgentInstance.docTitlesMatch(docTitle, docNames)) {
998
+ docs.push(
999
+ doc instanceof Instance
1000
+ ? doc
1001
+ : Instance.newWithAttributes(doc, new Map(Object.entries(doc)))
1002
+ );
1003
+ }
1004
+ }
1005
+
1006
+ if (docs.length > 0) {
1007
+ return message.concat('\n\nRelevant context from documents:\n').concat(
1008
+ docs
1009
+ .map((v: Instance) => {
1010
+ return `Document: ${v.lookup('title')}\n${v.lookup('content') as string}`;
1011
+ })
1012
+ .join('\n\n---\n\n')
1013
+ );
1014
+ }
995
1015
  }
996
- }
997
- if (docs.length > 0) {
998
- message = message.concat('\nUse the additional information given below:\n').concat(
999
- docs
1000
- .map((v: Instance) => {
1001
- return v.lookup('content');
1002
- })
1003
- .join('\n')
1016
+ } catch (semanticErr) {
1017
+ logger.debug(
1018
+ `Semantic search is not available, falling back to title-based filtering: ${semanticErr}`
1004
1019
  );
1005
1020
  }
1021
+
1022
+ const result: any[] = await parseHelper(`{${CoreAIModuleName}/Document? {}}`, env);
1023
+ if (result && result.length > 0) {
1024
+ const docs: Instance[] = [];
1025
+ for (let i = 0; i < result.length; ++i) {
1026
+ const v: any = result[i];
1027
+ const docTitle: string | undefined = AgentInstance.getDocumentTitle(v);
1028
+
1029
+ if (docTitle && docNames.includes(docTitle)) {
1030
+ if (v instanceof Instance) {
1031
+ docs.push(v);
1032
+ }
1033
+ }
1034
+ }
1035
+
1036
+ if (docs.length > 0) {
1037
+ return message.concat('\n\nRelevant context from documents:\n').concat(
1038
+ docs
1039
+ .map((v: Instance) => {
1040
+ return v.lookup('content') as string;
1041
+ })
1042
+ .join('\n\n')
1043
+ );
1044
+ }
1045
+ }
1046
+ } catch (err) {
1047
+ logger.debug(`Error retrieving documents: ${err}`);
1006
1048
  }
1007
1049
  }
1008
1050
  return message;
1009
1051
  }
1010
1052
 
1053
+ private static docTitlesMatch(title: string | undefined, docNames: string[]): boolean {
1054
+ return title !== undefined && docNames.includes(title);
1055
+ }
1056
+
1057
+ private static getDocumentTitle(doc: any): string | undefined {
1058
+ if (typeof doc.lookup === 'function') {
1059
+ return doc.lookup('title') as string | undefined;
1060
+ } else if (doc.attributes) {
1061
+ return doc.attributes.get('title') as string | undefined;
1062
+ } else if (doc.title) {
1063
+ return doc.title;
1064
+ }
1065
+ return undefined;
1066
+ }
1067
+
1011
1068
  private static ToolsCache = new Map<string, string>();
1012
1069
 
1013
1070
  private toolsAsString(): string {