agentlang 0.10.1 → 0.10.3

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 (178) hide show
  1. package/README.md +7 -14
  2. package/out/api/http.d.ts +4 -0
  3. package/out/api/http.d.ts.map +1 -1
  4. package/out/api/http.js +307 -26
  5. package/out/api/http.js.map +1 -1
  6. package/out/cli/main.d.ts.map +1 -1
  7. package/out/cli/main.js +3 -0
  8. package/out/cli/main.js.map +1 -1
  9. package/out/extension/main.cjs +250 -250
  10. package/out/extension/main.cjs.map +2 -2
  11. package/out/language/agentlang-validator.d.ts.map +1 -1
  12. package/out/language/agentlang-validator.js +4 -0
  13. package/out/language/agentlang-validator.js.map +1 -1
  14. package/out/language/error-reporter.d.ts +53 -0
  15. package/out/language/error-reporter.d.ts.map +1 -0
  16. package/out/language/error-reporter.js +879 -0
  17. package/out/language/error-reporter.js.map +1 -0
  18. package/out/language/generated/ast.d.ts +77 -2
  19. package/out/language/generated/ast.d.ts.map +1 -1
  20. package/out/language/generated/ast.js +60 -0
  21. package/out/language/generated/ast.js.map +1 -1
  22. package/out/language/generated/grammar.d.ts.map +1 -1
  23. package/out/language/generated/grammar.js +342 -206
  24. package/out/language/generated/grammar.js.map +1 -1
  25. package/out/language/main.cjs +901 -710
  26. package/out/language/main.cjs.map +3 -3
  27. package/out/language/parser.d.ts +4 -2
  28. package/out/language/parser.d.ts.map +1 -1
  29. package/out/language/parser.js +58 -99
  30. package/out/language/parser.js.map +1 -1
  31. package/out/language/syntax.d.ts +16 -0
  32. package/out/language/syntax.d.ts.map +1 -1
  33. package/out/language/syntax.js +66 -27
  34. package/out/language/syntax.js.map +1 -1
  35. package/out/runtime/api.d.ts +2 -0
  36. package/out/runtime/api.d.ts.map +1 -1
  37. package/out/runtime/api.js +25 -0
  38. package/out/runtime/api.js.map +1 -1
  39. package/out/runtime/datefns.d.ts +34 -0
  40. package/out/runtime/datefns.d.ts.map +1 -0
  41. package/out/runtime/datefns.js +82 -0
  42. package/out/runtime/datefns.js.map +1 -0
  43. package/out/runtime/defs.d.ts +1 -0
  44. package/out/runtime/defs.d.ts.map +1 -1
  45. package/out/runtime/defs.js +2 -1
  46. package/out/runtime/defs.js.map +1 -1
  47. package/out/runtime/document-retriever.d.ts +24 -0
  48. package/out/runtime/document-retriever.d.ts.map +1 -0
  49. package/out/runtime/document-retriever.js +258 -0
  50. package/out/runtime/document-retriever.js.map +1 -0
  51. package/out/runtime/embeddings/chunker.d.ts +18 -0
  52. package/out/runtime/embeddings/chunker.d.ts.map +1 -1
  53. package/out/runtime/embeddings/chunker.js +47 -15
  54. package/out/runtime/embeddings/chunker.js.map +1 -1
  55. package/out/runtime/embeddings/openai.d.ts.map +1 -1
  56. package/out/runtime/embeddings/openai.js +22 -9
  57. package/out/runtime/embeddings/openai.js.map +1 -1
  58. package/out/runtime/embeddings/provider.d.ts +1 -0
  59. package/out/runtime/embeddings/provider.d.ts.map +1 -1
  60. package/out/runtime/embeddings/provider.js +20 -1
  61. package/out/runtime/embeddings/provider.js.map +1 -1
  62. package/out/runtime/exec-graph.d.ts.map +1 -1
  63. package/out/runtime/exec-graph.js +22 -3
  64. package/out/runtime/exec-graph.js.map +1 -1
  65. package/out/runtime/integration-client.d.ts +21 -0
  66. package/out/runtime/integration-client.d.ts.map +1 -0
  67. package/out/runtime/integration-client.js +112 -0
  68. package/out/runtime/integration-client.js.map +1 -0
  69. package/out/runtime/integrations.d.ts.map +1 -1
  70. package/out/runtime/integrations.js +20 -9
  71. package/out/runtime/integrations.js.map +1 -1
  72. package/out/runtime/interpreter.d.ts +10 -0
  73. package/out/runtime/interpreter.d.ts.map +1 -1
  74. package/out/runtime/interpreter.js +221 -22
  75. package/out/runtime/interpreter.js.map +1 -1
  76. package/out/runtime/loader.d.ts.map +1 -1
  77. package/out/runtime/loader.js +70 -7
  78. package/out/runtime/loader.js.map +1 -1
  79. package/out/runtime/logger.d.ts.map +1 -1
  80. package/out/runtime/logger.js +8 -1
  81. package/out/runtime/logger.js.map +1 -1
  82. package/out/runtime/module.d.ts +18 -0
  83. package/out/runtime/module.d.ts.map +1 -1
  84. package/out/runtime/module.js +91 -3
  85. package/out/runtime/module.js.map +1 -1
  86. package/out/runtime/modules/ai.d.ts +16 -5
  87. package/out/runtime/modules/ai.d.ts.map +1 -1
  88. package/out/runtime/modules/ai.js +286 -88
  89. package/out/runtime/modules/ai.js.map +1 -1
  90. package/out/runtime/modules/core.d.ts.map +1 -1
  91. package/out/runtime/modules/core.js +5 -1
  92. package/out/runtime/modules/core.js.map +1 -1
  93. package/out/runtime/monitor.d.ts +6 -0
  94. package/out/runtime/monitor.d.ts.map +1 -1
  95. package/out/runtime/monitor.js +21 -1
  96. package/out/runtime/monitor.js.map +1 -1
  97. package/out/runtime/relgraph.d.ts.map +1 -1
  98. package/out/runtime/relgraph.js +7 -3
  99. package/out/runtime/relgraph.js.map +1 -1
  100. package/out/runtime/resolvers/interface.d.ts +7 -2
  101. package/out/runtime/resolvers/interface.d.ts.map +1 -1
  102. package/out/runtime/resolvers/interface.js +17 -3
  103. package/out/runtime/resolvers/interface.js.map +1 -1
  104. package/out/runtime/resolvers/sqldb/database.d.ts +2 -0
  105. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  106. package/out/runtime/resolvers/sqldb/database.js +142 -126
  107. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  108. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  109. package/out/runtime/resolvers/sqldb/dbutil.js +25 -4
  110. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  111. package/out/runtime/resolvers/sqldb/impl.d.ts +2 -1
  112. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  113. package/out/runtime/resolvers/sqldb/impl.js +24 -7
  114. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  115. package/out/runtime/resolvers/vector/lancedb-store.d.ts +16 -0
  116. package/out/runtime/resolvers/vector/lancedb-store.d.ts.map +1 -0
  117. package/out/runtime/resolvers/vector/lancedb-store.js +159 -0
  118. package/out/runtime/resolvers/vector/lancedb-store.js.map +1 -0
  119. package/out/runtime/resolvers/vector/types.d.ts +32 -0
  120. package/out/runtime/resolvers/vector/types.d.ts.map +1 -0
  121. package/out/runtime/resolvers/vector/types.js +2 -0
  122. package/out/runtime/resolvers/vector/types.js.map +1 -0
  123. package/out/runtime/services/documentFetcher.d.ts.map +1 -1
  124. package/out/runtime/services/documentFetcher.js +21 -6
  125. package/out/runtime/services/documentFetcher.js.map +1 -1
  126. package/out/runtime/state.d.ts +19 -1
  127. package/out/runtime/state.d.ts.map +1 -1
  128. package/out/runtime/state.js +36 -1
  129. package/out/runtime/state.js.map +1 -1
  130. package/out/runtime/util.d.ts +3 -2
  131. package/out/runtime/util.d.ts.map +1 -1
  132. package/out/runtime/util.js +13 -2
  133. package/out/runtime/util.js.map +1 -1
  134. package/out/syntaxes/agentlang.monarch.js +1 -1
  135. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  136. package/out/test-harness.d.ts +36 -0
  137. package/out/test-harness.d.ts.map +1 -0
  138. package/out/test-harness.js +341 -0
  139. package/out/test-harness.js.map +1 -0
  140. package/package.json +22 -19
  141. package/src/api/http.ts +336 -38
  142. package/src/cli/main.ts +3 -0
  143. package/src/language/agentlang-validator.ts +3 -0
  144. package/src/language/agentlang.langium +6 -2
  145. package/src/language/error-reporter.ts +1028 -0
  146. package/src/language/generated/ast.ts +94 -1
  147. package/src/language/generated/grammar.ts +342 -206
  148. package/src/language/parser.ts +64 -101
  149. package/src/language/syntax.ts +79 -24
  150. package/src/runtime/api.ts +36 -0
  151. package/src/runtime/datefns.ts +112 -0
  152. package/src/runtime/defs.ts +2 -1
  153. package/src/runtime/document-retriever.ts +311 -0
  154. package/src/runtime/embeddings/chunker.ts +52 -14
  155. package/src/runtime/embeddings/openai.ts +27 -9
  156. package/src/runtime/embeddings/provider.ts +22 -1
  157. package/src/runtime/exec-graph.ts +23 -2
  158. package/src/runtime/integration-client.ts +158 -0
  159. package/src/runtime/integrations.ts +20 -11
  160. package/src/runtime/interpreter.ts +221 -15
  161. package/src/runtime/loader.ts +83 -5
  162. package/src/runtime/logger.ts +12 -1
  163. package/src/runtime/module.ts +104 -3
  164. package/src/runtime/modules/ai.ts +341 -107
  165. package/src/runtime/modules/core.ts +5 -1
  166. package/src/runtime/monitor.ts +27 -1
  167. package/src/runtime/relgraph.ts +7 -3
  168. package/src/runtime/resolvers/interface.ts +23 -3
  169. package/src/runtime/resolvers/sqldb/database.ts +158 -130
  170. package/src/runtime/resolvers/sqldb/dbutil.ts +28 -6
  171. package/src/runtime/resolvers/sqldb/impl.ts +25 -7
  172. package/src/runtime/resolvers/vector/lancedb-store.ts +187 -0
  173. package/src/runtime/resolvers/vector/types.ts +39 -0
  174. package/src/runtime/services/documentFetcher.ts +21 -6
  175. package/src/runtime/state.ts +40 -1
  176. package/src/runtime/util.ts +19 -2
  177. package/src/syntaxes/agentlang.monarch.ts +1 -1
  178. package/src/test-harness.ts +423 -0
@@ -0,0 +1,311 @@
1
+ import { logger } from './logger.js';
2
+ import { AppConfig } from './state.js';
3
+ import { TextChunker } from './embeddings/chunker.js';
4
+ import { OpenAIEmbeddingProvider } from './embeddings/openai.js';
5
+ import { LanceDBVectorStore } from './resolvers/vector/lancedb-store.js';
6
+ import type { VectorStore } from './resolvers/vector/types.js';
7
+ import crypto from 'crypto';
8
+ import { readFileSync } from 'fs';
9
+ import { resolve as pathResolve } from 'path';
10
+
11
+ const VECTOR_DIMENSION = 1536;
12
+
13
+ interface LocalChunk {
14
+ id: string;
15
+ content: string;
16
+ documentTitle: string;
17
+ chunkIndex: number;
18
+ }
19
+
20
+ function usePgvector(): boolean {
21
+ if (AppConfig?.vectorStore?.type === 'pgvector') return true;
22
+ if (AppConfig?.vectorStore?.type === 'lancedb') return false;
23
+ return AppConfig?.store?.type === 'postgres';
24
+ }
25
+
26
+ /**
27
+ * Local document retriever — embeds documents into pgvector or LanceDB
28
+ * and retrieves relevant chunks via vector similarity search.
29
+ */
30
+ class DocumentRetriever {
31
+ private vectorStore: VectorStore | null = null;
32
+ private embeddingProvider: OpenAIEmbeddingProvider | null = null;
33
+ private chunker: TextChunker | null = null;
34
+ private localChunks: Map<string, LocalChunk> = new Map();
35
+ private processedDocuments: Set<string> = new Set();
36
+ private initialized = false;
37
+
38
+ private async ensureInit(): Promise<void> {
39
+ if (this.initialized) return;
40
+
41
+ this.chunker = new TextChunker(1000, 200);
42
+ this.embeddingProvider = new OpenAIEmbeddingProvider({
43
+ model: 'text-embedding-3-small',
44
+ });
45
+
46
+ if (!usePgvector()) {
47
+ const dbPath =
48
+ AppConfig?.vectorStore?.type === 'lancedb'
49
+ ? (AppConfig.vectorStore as any).dbname || './data/document-vectors.lance'
50
+ : './data/document-vectors.lance';
51
+
52
+ this.vectorStore = new LanceDBVectorStore({
53
+ moduleName: 'documents',
54
+ vectorDimension: VECTOR_DIMENSION,
55
+ dbname: dbPath,
56
+ });
57
+ await this.vectorStore.init();
58
+ logger.info(`[DOCUMENT-RETRIEVER] LanceDB vector store initialized at ${dbPath}`);
59
+ } else {
60
+ try {
61
+ const ag = (globalThis as any).agentlang;
62
+ if (ag?.rawQuery) {
63
+ await ag.rawQuery(`
64
+ CREATE TABLE IF NOT EXISTS document_local_chunks (
65
+ id TEXT PRIMARY KEY,
66
+ content TEXT NOT NULL,
67
+ document_title TEXT NOT NULL,
68
+ chunk_index INTEGER NOT NULL,
69
+ embedding vector(${VECTOR_DIMENSION})
70
+ )
71
+ `);
72
+ try {
73
+ await ag.rawQuery(`
74
+ CREATE INDEX IF NOT EXISTS idx_document_local_chunks_embedding
75
+ ON document_local_chunks USING hnsw (embedding vector_cosine_ops)
76
+ `);
77
+ } catch {
78
+ // Index may already exist or pgvector extension not loaded
79
+ }
80
+ logger.info('[DOCUMENT-RETRIEVER] pgvector local chunks table initialized');
81
+ }
82
+ } catch (err) {
83
+ logger.warn(`[DOCUMENT-RETRIEVER] Failed to initialize pgvector table: ${err}`);
84
+ }
85
+ }
86
+
87
+ this.initialized = true;
88
+ }
89
+
90
+ async processDocument(title: string, url: string): Promise<void> {
91
+ if (this.processedDocuments.has(title)) return;
92
+
93
+ await this.ensureInit();
94
+
95
+ try {
96
+ let content: string;
97
+ if (url.startsWith('http://') || url.startsWith('https://')) {
98
+ const resp = await fetch(url);
99
+ if (!resp.ok) {
100
+ logger.warn(
101
+ `[DOCUMENT-RETRIEVER] Failed to fetch "${title}" from ${url}: ${resp.status}`
102
+ );
103
+ return;
104
+ }
105
+ content = await resp.text();
106
+ } else {
107
+ const filePath = pathResolve(url);
108
+ try {
109
+ content = readFileSync(filePath, 'utf-8');
110
+ } catch (err) {
111
+ logger.warn(`[DOCUMENT-RETRIEVER] Failed to read "${title}" from ${filePath}: ${err}`);
112
+ return;
113
+ }
114
+ }
115
+
116
+ if (!content || content.trim().length === 0) {
117
+ logger.debug(`[DOCUMENT-RETRIEVER] Document "${title}" is empty, skipping`);
118
+ this.processedDocuments.add(title);
119
+ return;
120
+ }
121
+
122
+ const chunks = this.chunker!.splitText(content);
123
+ logger.debug(`[DOCUMENT-RETRIEVER] Document "${title}": ${chunks.length} chunks`);
124
+
125
+ if (chunks.length === 0) {
126
+ this.processedDocuments.add(title);
127
+ return;
128
+ }
129
+
130
+ const embeddings = await this.embeddingProvider!.embedTexts(chunks);
131
+
132
+ if (usePgvector()) {
133
+ await this.storePgvectorChunks(title, chunks, embeddings);
134
+ } else {
135
+ await this.storeLanceDBChunks(title, chunks, embeddings);
136
+ }
137
+
138
+ this.processedDocuments.add(title);
139
+ logger.info(
140
+ `[DOCUMENT-RETRIEVER] Processed "${title}": ${chunks.length} chunks embedded and stored`
141
+ );
142
+ } catch (err) {
143
+ logger.warn(`[DOCUMENT-RETRIEVER] Error processing "${title}": ${err}`);
144
+ }
145
+ }
146
+
147
+ private async storePgvectorChunks(
148
+ title: string,
149
+ chunks: string[],
150
+ embeddings: number[][]
151
+ ): Promise<void> {
152
+ const ag = (globalThis as any).agentlang;
153
+ if (!ag?.rawQuery) return;
154
+
155
+ for (let i = 0; i < chunks.length; i++) {
156
+ const id = crypto.randomUUID();
157
+ const embeddingStr = `[${embeddings[i].join(',')}]`;
158
+ await ag.rawQuery(
159
+ `INSERT INTO document_local_chunks (id, content, document_title, chunk_index, embedding)
160
+ VALUES ($1, $2, $3, $4, $5::vector)
161
+ ON CONFLICT (id) DO NOTHING`,
162
+ [id, chunks[i], title, i, embeddingStr]
163
+ );
164
+ this.localChunks.set(id, {
165
+ id,
166
+ content: chunks[i],
167
+ documentTitle: title,
168
+ chunkIndex: i,
169
+ });
170
+ }
171
+ }
172
+
173
+ private async storeLanceDBChunks(
174
+ title: string,
175
+ chunks: string[],
176
+ embeddings: number[][]
177
+ ): Promise<void> {
178
+ if (!this.vectorStore) return;
179
+
180
+ for (let i = 0; i < chunks.length; i++) {
181
+ const id = crypto.randomUUID();
182
+ await this.vectorStore.addEmbedding({
183
+ id,
184
+ embedding: embeddings[i],
185
+ documentId: title,
186
+ });
187
+ this.localChunks.set(id, {
188
+ id,
189
+ content: chunks[i],
190
+ documentTitle: title,
191
+ chunkIndex: i,
192
+ });
193
+ }
194
+ }
195
+
196
+ async query(queryText: string, documentTitles?: string[], limit: number = 10): Promise<string> {
197
+ await this.ensureInit();
198
+
199
+ try {
200
+ const results = usePgvector()
201
+ ? await this.queryPgvector(queryText, documentTitles, limit)
202
+ : await this.queryLanceDB(queryText, documentTitles, limit);
203
+
204
+ if (results.length === 0) return '';
205
+
206
+ return results.map(r => r.content).join('\n\n---\n\n');
207
+ } catch (err) {
208
+ logger.debug(`[DOCUMENT-RETRIEVER] Query failed: ${err}`);
209
+ return '';
210
+ }
211
+ }
212
+
213
+ private async queryPgvector(
214
+ queryText: string,
215
+ documentTitles?: string[],
216
+ limit: number = 10
217
+ ): Promise<Array<{ id: string; content: string; similarity: number }>> {
218
+ const ag = (globalThis as any).agentlang;
219
+ if (!ag?.rawQuery) return [];
220
+
221
+ const queryEmbedding = await this.embeddingProvider!.embedText(queryText);
222
+ const embeddingStr = `[${queryEmbedding.join(',')}]`;
223
+
224
+ let sql: string;
225
+ let params: any[];
226
+
227
+ if (documentTitles && documentTitles.length > 0) {
228
+ const placeholders = documentTitles.map((_, i) => `$${i + 2}`).join(', ');
229
+ sql = `SELECT id, content, document_title, 1 - (embedding <=> $1::vector) AS similarity
230
+ FROM document_local_chunks
231
+ WHERE document_title IN (${placeholders})
232
+ ORDER BY embedding <=> $1::vector
233
+ LIMIT ${limit}`;
234
+ params = [embeddingStr, ...documentTitles];
235
+ } else {
236
+ sql = `SELECT id, content, document_title, 1 - (embedding <=> $1::vector) AS similarity
237
+ FROM document_local_chunks
238
+ ORDER BY embedding <=> $1::vector
239
+ LIMIT ${limit}`;
240
+ params = [embeddingStr];
241
+ }
242
+
243
+ const rows: any[] = await ag.rawQuery(sql, params);
244
+ return (rows || []).map((r: any) => ({
245
+ id: r.id,
246
+ content: r.content,
247
+ similarity: parseFloat(r.similarity) || 0,
248
+ }));
249
+ }
250
+
251
+ private async queryLanceDB(
252
+ queryText: string,
253
+ documentTitles?: string[],
254
+ limit: number = 10
255
+ ): Promise<Array<{ id: string; content: string; similarity: number }>> {
256
+ if (!this.vectorStore) return [];
257
+
258
+ const queryEmbedding = await this.embeddingProvider!.embedText(queryText);
259
+ const searchResults = await this.vectorStore.search(
260
+ queryEmbedding,
261
+ undefined,
262
+ undefined,
263
+ limit
264
+ );
265
+
266
+ const results: Array<{ id: string; content: string; similarity: number }> = [];
267
+
268
+ for (const sr of searchResults) {
269
+ const chunk = this.localChunks.get(sr.id);
270
+ if (!chunk) continue;
271
+
272
+ if (documentTitles && documentTitles.length > 0) {
273
+ if (!documentTitles.includes(chunk.documentTitle)) continue;
274
+ }
275
+
276
+ results.push({
277
+ id: sr.id,
278
+ content: chunk.content,
279
+ similarity: 1 - (sr.distance || 0),
280
+ });
281
+ }
282
+
283
+ return results.slice(0, limit);
284
+ }
285
+
286
+ async close(): Promise<void> {
287
+ if (this.vectorStore) {
288
+ await this.vectorStore.close();
289
+ this.vectorStore = null;
290
+ }
291
+ this.localChunks.clear();
292
+ this.processedDocuments.clear();
293
+ this.initialized = false;
294
+ }
295
+ }
296
+
297
+ let retrieverInstance: DocumentRetriever | null = null;
298
+
299
+ export function getDocumentRetriever(): DocumentRetriever {
300
+ if (!retrieverInstance) {
301
+ retrieverInstance = new DocumentRetriever();
302
+ }
303
+ return retrieverInstance;
304
+ }
305
+
306
+ export function resetDocumentRetriever(): void {
307
+ if (retrieverInstance) {
308
+ retrieverInstance.close().catch(() => {});
309
+ retrieverInstance = null;
310
+ }
311
+ }
@@ -1,41 +1,79 @@
1
+ /**
2
+ * Streaming text chunker - yields chunks one at a time without storing all in memory.
3
+ */
1
4
  export class TextChunker {
2
5
  private chunkSize: number;
3
6
  private chunkOverlap: number;
4
7
  private separators: string[] = ['\n\n', '\n', '. ', ' ', ''];
5
8
 
6
9
  constructor(chunkSize: number = 1000, chunkOverlap: number = 200) {
7
- this.chunkSize = chunkSize;
8
- this.chunkOverlap = chunkOverlap;
10
+ // Ensure valid values - overlap must be less than chunk size to avoid infinite loop
11
+ this.chunkSize = Math.max(100, chunkSize || 1000);
12
+ // Cap overlap to at most 20% of chunk size to ensure progress
13
+ this.chunkOverlap = Math.max(
14
+ 0,
15
+ Math.min(chunkOverlap || 200, Math.floor(this.chunkSize * 0.2))
16
+ );
9
17
  }
10
18
 
11
- splitText(text: string): string[] {
19
+ /**
20
+ * Calculate total chunks without creating them all in memory.
21
+ * Used for logging/progress tracking.
22
+ */
23
+ estimateChunks(text: string): number {
24
+ if (text.length <= this.chunkSize) {
25
+ return 1;
26
+ }
27
+ // Rough estimate: (text length / effective chunk size) + 1
28
+ const effectiveChunkSize = this.chunkSize - this.chunkOverlap;
29
+ return Math.ceil(text.length / effectiveChunkSize);
30
+ }
31
+
32
+ /**
33
+ * Streaming generator that yields chunks one at a time.
34
+ * Memory-efficient: doesn't store all chunks in an array.
35
+ */
36
+ *streamChunks(text: string): Generator<string, void, unknown> {
12
37
  if (text.length <= this.chunkSize) {
13
- return [text];
38
+ yield text;
39
+ return;
14
40
  }
15
41
 
16
- const chunks: string[] = [];
17
42
  let start = 0;
43
+ const minAdvance = Math.max(50, this.chunkSize - this.chunkOverlap); // Ensure we always advance
18
44
 
19
45
  while (start < text.length) {
20
46
  let end = Math.min(start + this.chunkSize, text.length);
21
47
 
22
48
  if (end < text.length) {
23
- end = this.findBestSplitPoint(text, start, end);
49
+ // Try to find a good split point, but ensure we advance by at least minAdvance
50
+ const splitPoint = this.findBestSplitPoint(text, start, end);
51
+ // Only use split point if it gives us reasonable progress
52
+ if (splitPoint - start >= minAdvance * 0.5) {
53
+ end = splitPoint;
54
+ }
55
+ // Otherwise use the hard end to ensure progress
24
56
  }
25
57
 
26
- chunks.push(text.substring(start, end));
27
- start = end - this.chunkOverlap;
58
+ yield text.substring(start, end);
28
59
 
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
- }
60
+ // Advance by at least minAdvance characters to avoid infinite loops
61
+ const nextStart = end - this.chunkOverlap;
62
+ start = Math.max(nextStart, start + minAdvance * 0.5);
63
+
64
+ if (start >= text.length) {
34
65
  break;
35
66
  }
36
67
  }
68
+ }
37
69
 
38
- return chunks;
70
+ /**
71
+ * Legacy method for backwards compatibility.
72
+ * ⚠️ WARNING: This creates all chunks in memory and can cause OOM on large documents.
73
+ * Prefer streamChunks() for large documents.
74
+ */
75
+ splitText(text: string): string[] {
76
+ return Array.from(this.streamChunks(text));
39
77
  }
40
78
 
41
79
  private findBestSplitPoint(text: string, start: number, end: number): number {
@@ -1,6 +1,7 @@
1
1
  import { OpenAIEmbeddings } from '@langchain/openai';
2
2
  import { EmbeddingProvider, EmbeddingProviderConfig } from './provider.js';
3
3
  import { getLocalEnv } from '../auth/defs.js';
4
+ import { logger } from '../logger.js';
4
5
 
5
6
  export interface OpenAIEmbeddingConfig extends EmbeddingProviderConfig {
6
7
  model?: string;
@@ -14,6 +15,9 @@ export class OpenAIEmbeddingProvider extends EmbeddingProvider {
14
15
  constructor(config?: EmbeddingProviderConfig) {
15
16
  super(config || {});
16
17
  this.openaiConfig = (this.config as OpenAIEmbeddingConfig) || {};
18
+ logger.debug(
19
+ `[OPENAI-EMBEDDING] Provider created with model: ${this.openaiConfig.model || 'default'}`
20
+ );
17
21
  }
18
22
 
19
23
  protected createEmbeddings(): OpenAIEmbeddings {
@@ -21,26 +25,40 @@ export class OpenAIEmbeddingProvider extends EmbeddingProvider {
21
25
  apiKey: this.resolveApiKey(),
22
26
  };
23
27
 
24
- if (this.openaiConfig.model) {
25
- config.model = this.openaiConfig.model;
28
+ // Use this.config directly since this.openaiConfig is not initialized yet
29
+ // during parent constructor (super() calls createEmbeddings before child init)
30
+ const openaiConfig = (this.config as OpenAIEmbeddingConfig) || {};
31
+
32
+ if (openaiConfig?.model) {
33
+ config.model = openaiConfig.model;
26
34
  }
27
35
 
28
- if (this.openaiConfig.dimensions) {
29
- config.dimensions = this.openaiConfig.dimensions;
36
+ if (openaiConfig?.dimensions) {
37
+ config.dimensions = openaiConfig.dimensions;
30
38
  }
31
39
 
32
- if (this.openaiConfig.maxRetries !== undefined) {
33
- config.maxRetries = this.openaiConfig.maxRetries;
40
+ if (openaiConfig?.maxRetries !== undefined) {
41
+ config.maxRetries = openaiConfig.maxRetries;
34
42
  }
35
43
 
36
44
  return new OpenAIEmbeddings(config);
37
45
  }
38
46
 
39
47
  protected resolveApiKey(): string {
40
- if (this.openaiConfig.apiKey) {
41
- return this.openaiConfig.apiKey;
48
+ // Use this.config directly since this.openaiConfig may not be initialized yet
49
+ // during constructor (createEmbeddings is called during super())
50
+ const config = this.openaiConfig || (this.config as OpenAIEmbeddingConfig) || {};
51
+ if (config.apiKey) {
52
+ return config.apiKey;
53
+ }
54
+ const envKey = process.env.AGENTLANG_OPENAI_KEY || getLocalEnv('AGENTLANG_OPENAI_KEY');
55
+ if (envKey) {
56
+ return envKey;
42
57
  }
43
- return process.env.AGENTLANG_OPENAI_KEY || getLocalEnv('AGENTLANG_OPENAI_KEY') || '';
58
+ logger.warn(
59
+ `[OPENAI-EMBEDDING] No API key found! Set AGENTLANG_OPENAI_KEY environment variable.`
60
+ );
61
+ return '';
44
62
  }
45
63
 
46
64
  getProviderName(): string {
@@ -1,4 +1,5 @@
1
1
  import { Embeddings } from '@langchain/core/embeddings';
2
+ import { logger } from '../logger.js';
2
3
 
3
4
  export interface EmbeddingProviderConfig {
4
5
  chunkSize?: number;
@@ -23,7 +24,27 @@ export abstract class EmbeddingProvider {
23
24
  abstract getProviderName(): string;
24
25
 
25
26
  async embedText(text: string): Promise<number[]> {
26
- return await this.embeddings.embedQuery(text);
27
+ logger.debug(`[EMBEDDING-PROVIDER] embedText called (${text.length} chars)`);
28
+ const startTime = Date.now();
29
+ const result = await this.embeddings.embedQuery(text);
30
+ const duration = Date.now() - startTime;
31
+ logger.debug(
32
+ `[EMBEDDING-PROVIDER] embedText completed in ${duration}ms (${result.length} dimensions)`
33
+ );
34
+ return result;
35
+ }
36
+
37
+ async embedTexts(texts: string[]): Promise<number[][]> {
38
+ if (texts.length === 0) return [];
39
+ if (texts.length === 1) return [await this.embedText(texts[0])];
40
+ logger.debug(`[EMBEDDING-PROVIDER] embedTexts called (${texts.length} texts)`);
41
+ const startTime = Date.now();
42
+ const results = await this.embeddings.embedDocuments(texts);
43
+ const duration = Date.now() - startTime;
44
+ logger.debug(
45
+ `[EMBEDDING-PROVIDER] embedTexts completed in ${duration}ms (${texts.length} texts, ${results[0]?.length || 0} dimensions)`
46
+ );
47
+ return results;
27
48
  }
28
49
 
29
50
  getConfig(): EmbeddingProviderConfig {
@@ -304,8 +304,8 @@ export async function executeGraph(execGraph: ExecGraph, env: Environment): Prom
304
304
  throw reason;
305
305
  } finally {
306
306
  if (monitoringEnabled) {
307
- if (monitorIncr) env.decrementMonitor();
308
307
  env.setMonitorEntryResult(env.getLastResult());
308
+ if (monitorIncr) env.decrementMonitor();
309
309
  }
310
310
  }
311
311
  }
@@ -425,10 +425,28 @@ export async function executeEvent(
425
425
  env.setActiveEvent(eventInstance);
426
426
  await executeEventHelper(eventInstance, env);
427
427
  } else if (isAgentEventInstance(eventInstance)) {
428
+ env.setActiveEvent(eventInstance);
429
+ if (isMonitoringEnabled()) {
430
+ env.appendEntryToMonitor(
431
+ `{${eventInstance.getFqName()} {message "${eventInstance.lookup('message')}"}}`
432
+ );
433
+ }
428
434
  await handleAgentInvocation(eventInstance, env);
435
+ if (isMonitoringEnabled()) {
436
+ env.setMonitorEntryResult(env.getLastResult());
437
+ }
429
438
  }
430
439
  const r = env.getLastResult();
431
- if (continuation) continuation(r);
440
+ if (continuation) {
441
+ if (env.getLastPattern() && isAgentEventInstance(eventInstance)) {
442
+ continuation({
443
+ result: r.map((res: any) => (res.attributes ? Object.fromEntries(res.attributes) : res)),
444
+ pattern: env.getLastPattern(),
445
+ });
446
+ } else {
447
+ continuation(r);
448
+ }
449
+ }
432
450
  return r;
433
451
  } catch (err) {
434
452
  if (env && env.hasHandlers()) {
@@ -490,6 +508,9 @@ export async function executeEventHelper(eventInstance: Instance, env?: Environm
490
508
  );
491
509
  }
492
510
  await handleAgentInvocation(eventInstance, env);
511
+ if (isMonitoringEnabled()) {
512
+ env.setMonitorEntryResult(env.getLastResult());
513
+ }
493
514
  }
494
515
  if (isLocalEnv) {
495
516
  await env.commitAllTransactions();