agentlang 0.9.5 → 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.
- package/out/cli/main.d.ts.map +1 -1
- package/out/cli/main.js +8 -3
- package/out/cli/main.js.map +1 -1
- package/out/language/generated/ast.d.ts +80 -3
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +62 -0
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +432 -218
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/main.cjs +485 -218
- package/out/language/main.cjs.map +2 -2
- package/out/language/parser.js +8 -8
- package/out/language/parser.js.map +1 -1
- package/out/runtime/agents/common.d.ts +7 -1
- package/out/runtime/agents/common.d.ts.map +1 -1
- package/out/runtime/agents/common.js +101 -0
- package/out/runtime/agents/common.js.map +1 -1
- package/out/runtime/agents/impl/anthropic.js +4 -4
- package/out/runtime/agents/impl/anthropic.js.map +1 -1
- package/out/runtime/agents/impl/openai.js +4 -4
- package/out/runtime/agents/impl/openai.js.map +1 -1
- package/out/runtime/docs.d.ts.map +1 -1
- package/out/runtime/docs.js +109 -7
- package/out/runtime/docs.js.map +1 -1
- package/out/runtime/embeddings/chunker.d.ts +9 -0
- package/out/runtime/embeddings/chunker.d.ts.map +1 -0
- package/out/runtime/embeddings/chunker.js +41 -0
- package/out/runtime/embeddings/chunker.js.map +1 -0
- package/out/runtime/embeddings/index.d.ts +6 -0
- package/out/runtime/embeddings/index.d.ts.map +1 -0
- package/out/runtime/embeddings/index.js +6 -0
- package/out/runtime/embeddings/index.js.map +1 -0
- package/out/runtime/embeddings/openai.d.ts +15 -0
- package/out/runtime/embeddings/openai.d.ts.map +1 -0
- package/out/runtime/embeddings/openai.js +34 -0
- package/out/runtime/embeddings/openai.js.map +1 -0
- package/out/runtime/embeddings/provider.d.ts +20 -0
- package/out/runtime/embeddings/provider.d.ts.map +1 -0
- package/out/runtime/embeddings/provider.js +17 -0
- package/out/runtime/embeddings/provider.js.map +1 -0
- package/out/runtime/embeddings/registry.d.ts +3 -0
- package/out/runtime/embeddings/registry.d.ts.map +1 -0
- package/out/runtime/embeddings/registry.js +16 -0
- package/out/runtime/embeddings/registry.js.map +1 -0
- package/out/runtime/exec-graph.d.ts.map +1 -1
- package/out/runtime/exec-graph.js +5 -0
- package/out/runtime/exec-graph.js.map +1 -1
- package/out/runtime/interpreter.d.ts +4 -0
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +27 -7
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/loader.d.ts.map +1 -1
- package/out/runtime/loader.js +42 -5
- package/out/runtime/loader.js.map +1 -1
- package/out/runtime/module.d.ts +22 -4
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +76 -10
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/ai.d.ts +20 -2
- package/out/runtime/modules/ai.d.ts.map +1 -1
- package/out/runtime/modules/ai.js +333 -37
- package/out/runtime/modules/ai.js.map +1 -1
- package/out/runtime/modules/auth.d.ts.map +1 -1
- package/out/runtime/modules/auth.js +11 -5
- package/out/runtime/modules/auth.js.map +1 -1
- package/out/runtime/resolvers/interface.d.ts +1 -1
- package/out/runtime/resolvers/interface.d.ts.map +1 -1
- package/out/runtime/resolvers/interface.js.map +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/database.js +139 -50
- package/out/runtime/resolvers/sqldb/database.js.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts +22 -2
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.js +178 -47
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
- package/out/runtime/state.d.ts +1 -0
- package/out/runtime/state.d.ts.map +1 -1
- package/out/runtime/state.js +3 -0
- package/out/runtime/state.js.map +1 -1
- package/out/syntaxes/agentlang.monarch.js +1 -1
- package/out/syntaxes/agentlang.monarch.js.map +1 -1
- package/package.json +188 -185
- package/public/pdf.worker.mjs +65152 -0
- package/src/cli/main.ts +7 -2
- package/src/language/agentlang.langium +8 -2
- package/src/language/generated/ast.ts +96 -2
- package/src/language/generated/grammar.ts +432 -218
- package/src/language/parser.ts +8 -8
- package/src/runtime/agents/common.ts +107 -0
- package/src/runtime/agents/impl/anthropic.ts +4 -4
- package/src/runtime/agents/impl/openai.ts +4 -4
- package/src/runtime/docs.ts +120 -9
- package/src/runtime/embeddings/chunker.ts +50 -0
- package/src/runtime/embeddings/index.ts +5 -0
- package/src/runtime/embeddings/openai.ts +49 -0
- package/src/runtime/embeddings/provider.ts +37 -0
- package/src/runtime/embeddings/registry.ts +17 -0
- package/src/runtime/exec-graph.ts +4 -0
- package/src/runtime/interpreter.ts +39 -16
- package/src/runtime/loader.ts +42 -3
- package/src/runtime/module.ts +127 -41
- package/src/runtime/modules/ai.ts +467 -38
- package/src/runtime/modules/auth.ts +11 -5
- package/src/runtime/resolvers/interface.ts +1 -1
- package/src/runtime/resolvers/sqldb/database.ts +146 -56
- package/src/runtime/resolvers/sqldb/impl.ts +238 -61
- package/src/runtime/state.ts +4 -0
- package/src/syntaxes/agentlang.monarch.ts +1 -1
- package/out/setupClassic.d.ts +0 -98
- package/out/setupClassic.d.ts.map +0 -1
- package/out/setupClassic.js +0 -38
- package/out/setupClassic.js.map +0 -1
- package/out/setupCommon.d.ts +0 -2
- package/out/setupCommon.d.ts.map +0 -1
- package/out/setupCommon.js +0 -33
- package/out/setupCommon.js.map +0 -1
- package/out/setupExtended.d.ts +0 -40
- package/out/setupExtended.d.ts.map +0 -1
- package/out/setupExtended.js +0 -67
- package/out/setupExtended.js.map +0 -1
package/src/language/parser.ts
CHANGED
|
@@ -411,27 +411,27 @@ function asFnCallPattern(fnCall: FnCall): FunctionCallPattern {
|
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
function introspectLiteral(lit: Literal): BasePattern {
|
|
414
|
-
if (lit.id) {
|
|
414
|
+
if (lit.id !== undefined) {
|
|
415
415
|
return LiteralPattern.Id(lit.id);
|
|
416
|
-
} else if (lit.num) {
|
|
416
|
+
} else if (lit.num !== undefined) {
|
|
417
417
|
return LiteralPattern.Number(lit.num);
|
|
418
|
-
} else if (lit.ref) {
|
|
418
|
+
} else if (lit.ref !== undefined) {
|
|
419
419
|
return LiteralPattern.Reference(lit.ref);
|
|
420
420
|
} else if (lit.str !== undefined) {
|
|
421
421
|
return LiteralPattern.String(lit.str);
|
|
422
|
-
} else if (lit.bool) {
|
|
422
|
+
} else if (lit.bool !== undefined) {
|
|
423
423
|
return LiteralPattern.Boolean(lit.bool == 'true' ? true : false);
|
|
424
|
-
} else if (lit.fnCall) {
|
|
424
|
+
} else if (lit.fnCall !== undefined) {
|
|
425
425
|
return asFnCallPattern(lit.fnCall);
|
|
426
|
-
} else if (lit.asyncFnCall) {
|
|
426
|
+
} else if (lit.asyncFnCall !== undefined) {
|
|
427
427
|
return asFnCallPattern(lit.asyncFnCall.fnCall).asAsync();
|
|
428
|
-
} else if (lit.array) {
|
|
428
|
+
} else if (lit.array !== undefined) {
|
|
429
429
|
return LiteralPattern.Array(
|
|
430
430
|
lit.array.vals.map((stmt: Statement) => {
|
|
431
431
|
return introspectStatement(stmt);
|
|
432
432
|
})
|
|
433
433
|
);
|
|
434
|
-
} else if (lit.map) {
|
|
434
|
+
} else if (lit.map !== undefined) {
|
|
435
435
|
return introspectMapLiteral(lit.map);
|
|
436
436
|
} else {
|
|
437
437
|
throw new Error(`Invalid literal - ${lit}`);
|
|
@@ -167,6 +167,9 @@ The following usage is NOT valid:
|
|
|
167
167
|
|
|
168
168
|
<pattern> if (<expr>)
|
|
169
169
|
|
|
170
|
+
Also C-style ternary-expressions of the form \`condition ? consequent : alternative\` IS INVALID in Agentlang. Always use a proper
|
|
171
|
+
\`if-else\` statement.
|
|
172
|
+
|
|
170
173
|
A pattern may execute asynchronously and its eventual result can be handled by patterns provided in the '@then' clause. An example is shown below:
|
|
171
174
|
|
|
172
175
|
{sendChatMessage {to "amy", "text" "hello"}} @as response @then {
|
|
@@ -362,6 +365,105 @@ case or cases you selected and no additional text or comments. If you decide to
|
|
|
362
365
|
Also select the case that is the best match for the given context, no need to look for a perfect match for all values specified in the context.
|
|
363
366
|
Now apply the same analysis to the following context and cases provided by the user.
|
|
364
367
|
`;
|
|
368
|
+
export const EvalInstructions = `Consider the following request passed to an intelligent agent and its response. Rate the response on a scale of
|
|
369
|
+
1-5, where 1 being the lowest score (response unacceptable) and 5 being the highest score (best response). Also describe why you gave a particular score.
|
|
370
|
+
Reply in the JSON format {score: <score-for-the-response, summary: <your-reasons-for-giving-the-score>}`;
|
|
371
|
+
|
|
372
|
+
export const LearningAgentInstructions = `Summarize requirements and use-cases as three constructs: 'decisions' and 'glossaries'.
|
|
373
|
+
Your response must be encoded in JSON. The exact schema will be described later, but let's first consider an example that will help clarify
|
|
374
|
+
how decisions and glossaries could be generate from a use-case. This use-case is from the insurance industry:
|
|
375
|
+
|
|
376
|
+
In insurance, a claim is a formal request made by a policyholder (or beneficiary) to an insurance company asking for financial
|
|
377
|
+
compensation or service because a covered event (loss) has occurred. When the company receives a claim, look at its risk-factor and
|
|
378
|
+
set its approval-mode accordingly. If the risk-factor is 'low', then set the approval-mode to 'auto', if it's 'medium' then set approval-mode to
|
|
379
|
+
'manual'. If the risk-factor is 'high', then set approval-mode to 'investigate'.
|
|
380
|
+
|
|
381
|
+
Given the above use-case you should return the summary as:
|
|
382
|
+
{
|
|
383
|
+
"summary": "A claim is a request for insurance benefits after a covered loss, and its risk level determines
|
|
384
|
+
whether it is auto-approved, manually reviewed, or investigated.",
|
|
385
|
+
"glossary": [
|
|
386
|
+
{
|
|
387
|
+
"word": "claim",
|
|
388
|
+
"meaning": "A claim is a request to an insurer for payment or service after a covered loss."
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
"decisions": [
|
|
392
|
+
{
|
|
393
|
+
"name": "setApprovalByRiskFactor",
|
|
394
|
+
"conditions": [
|
|
395
|
+
{
|
|
396
|
+
"if": "risk-factor is low",
|
|
397
|
+
"then": "auto"
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"if": "risk-factor is medium",
|
|
401
|
+
"then": "manual"
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"if": "risk-factor is high",
|
|
405
|
+
"then": "investigate"
|
|
406
|
+
}
|
|
407
|
+
]
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
Sometimes, the use-case will also contain some examples, which you can summarize as 'scenarios'. An example follows:
|
|
413
|
+
|
|
414
|
+
The ACME company gives salary-hikes to employees based on their sales. If the sales is $50000 or above, a 5-percent hike is given.
|
|
415
|
+
If the sales is between $20000 and $50000, a hike of 2.5% is given. For sales below $20000, no hike is given. An employee who hit
|
|
416
|
+
the target of $50000 is said to have hit a "jackpot". For example, if you see a message that says - "jake@acme.com hit a jackpot", that means
|
|
417
|
+
the employee 'jake@acme.com' is eligible for a 5% hike.
|
|
418
|
+
|
|
419
|
+
Given the above instruction, you should return:
|
|
420
|
+
|
|
421
|
+
{
|
|
422
|
+
"summary": "ACME determines employee salary hikes based on achieved sales figures, with higher sales resulting in higher percentage increases. Employees who meet or exceed the top sales threshold are described as having hit a jackpot.",
|
|
423
|
+
"glossary": [
|
|
424
|
+
{
|
|
425
|
+
"word": "salary-hike",
|
|
426
|
+
"meaning": "An increase in an employee's salary based on defined performance criteria."
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"word": "sales",
|
|
430
|
+
"meaning": "The total revenue generated by an employee, used as the basis for determining salary hikes."
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
"word": "jackpot",
|
|
434
|
+
"meaning": "A term used for employees whose sales are 50000 or above, making them eligible for the highest salary hike."
|
|
435
|
+
}
|
|
436
|
+
],
|
|
437
|
+
"decisions": [
|
|
438
|
+
{
|
|
439
|
+
"name": "determineSalaryHikeBySales",
|
|
440
|
+
"conditions": [
|
|
441
|
+
{
|
|
442
|
+
"if": "sales is 50000 or above",
|
|
443
|
+
"then": "5-percent hike"
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"if": "sales is between 20000 and 50000",
|
|
447
|
+
"then": "2.5-percent hike"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
"if": "sales is below 20000",
|
|
451
|
+
"then": "no hike"
|
|
452
|
+
}
|
|
453
|
+
]
|
|
454
|
+
}
|
|
455
|
+
],
|
|
456
|
+
"scenarios": [
|
|
457
|
+
{
|
|
458
|
+
"user": "jake@acme.com hit a jackpot",
|
|
459
|
+
"ai": "give jake@acme.com a hike of 5-percent"
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
For some user-requests, it may not be able to produce glossary or decisions (or both). In such cases, just return the 'summary'.
|
|
465
|
+
Now process the user-request that follows.
|
|
466
|
+
`;
|
|
365
467
|
|
|
366
468
|
export type AgentCondition = {
|
|
367
469
|
if: string;
|
|
@@ -575,3 +677,8 @@ export function getAgentScratchNames(agentFqName: string): Set<string> | undefin
|
|
|
575
677
|
export function removeAgentScratchNames(agentFqName: string) {
|
|
576
678
|
AgentScratchNames.delete(agentFqName);
|
|
577
679
|
}
|
|
680
|
+
|
|
681
|
+
export type AgentSummary = {
|
|
682
|
+
data: string;
|
|
683
|
+
summary: string;
|
|
684
|
+
};
|
|
@@ -181,15 +181,15 @@ export class AnthropicProvider implements AgentServiceProvider {
|
|
|
181
181
|
if (!config) {
|
|
182
182
|
return {
|
|
183
183
|
...defaultConfig,
|
|
184
|
-
apiKey: process.env.
|
|
184
|
+
apiKey: process.env.AGENTLANG_ANTHROPIC_KEY || getLocalEnv('AGENTLANG_ANTHROPIC_KEY'),
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
const apiKey =
|
|
189
189
|
config.get('apiKey') ||
|
|
190
190
|
config.get('api_key') ||
|
|
191
|
-
process.env.
|
|
192
|
-
getLocalEnv('
|
|
191
|
+
process.env.AGENTLANG_ANTHROPIC_KEY ||
|
|
192
|
+
getLocalEnv('AGENTLANG_ANTHROPIC_KEY');
|
|
193
193
|
|
|
194
194
|
return {
|
|
195
195
|
model: config.get('model') || defaultConfig.model,
|
|
@@ -266,7 +266,7 @@ export class AnthropicProvider implements AgentServiceProvider {
|
|
|
266
266
|
): Promise<AIResponse> {
|
|
267
267
|
if (!this.config.apiKey) {
|
|
268
268
|
throw new Error(
|
|
269
|
-
'Anthropic API key is required. Set
|
|
269
|
+
'Anthropic API key is required. Set AGENTLANG_ANTHROPIC_KEY environment variable or use setLocalEnv("AGENTLANG_ANTHROPIC_KEY", key) or provide apiKey in config.'
|
|
270
270
|
);
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -69,15 +69,15 @@ export class OpenAIProvider implements AgentServiceProvider {
|
|
|
69
69
|
if (!config) {
|
|
70
70
|
return {
|
|
71
71
|
...defaultConfig,
|
|
72
|
-
apiKey: process.env.
|
|
72
|
+
apiKey: process.env.AGENTLANG_OPENAI_KEY || getLocalEnv('AGENTLANG_OPENAI_KEY'),
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
const apiKey =
|
|
77
77
|
config.get('apiKey') ||
|
|
78
78
|
config.get('api_key') ||
|
|
79
|
-
process.env.
|
|
80
|
-
getLocalEnv('
|
|
79
|
+
process.env.AGENTLANG_OPENAI_KEY ||
|
|
80
|
+
getLocalEnv('AGENTLANG_OPENAI_KEY');
|
|
81
81
|
|
|
82
82
|
return {
|
|
83
83
|
model: config.get('model') || defaultConfig.model,
|
|
@@ -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
|
|
108
|
+
'OpenAI API key is required. Set AGENTLANG_OPENAI_KEY environment variable or use setLocalEnv("AGENTLANG_OPENAI_KEY", key) or provide apiKey in config.'
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
if (externalToolSpecs) {
|
package/src/runtime/docs.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
}
|
|
@@ -468,15 +468,18 @@ export async function executeEventHelper(eventInstance: Instance, env?: Environm
|
|
|
468
468
|
isLocalEnv = true;
|
|
469
469
|
}
|
|
470
470
|
let g: ExecGraph | undefined;
|
|
471
|
+
let escalatedRole: string | undefined;
|
|
471
472
|
if (!isAgentEventInstance(eventInstance)) {
|
|
472
473
|
g = await generateExecutionGraph(fqn);
|
|
473
474
|
if (!g) {
|
|
474
475
|
throw new Error(`Failed to generate graph for event ${fqn}`);
|
|
475
476
|
}
|
|
477
|
+
escalatedRole = getWorkflowForEvent(fqn).getRoleEscalation();
|
|
476
478
|
}
|
|
477
479
|
const oldModuleName = env.switchActiveModuleName(eventInstance.moduleName);
|
|
478
480
|
env.bind(eventInstance.name, eventInstance);
|
|
479
481
|
env.bind(eventInstance.getFqName(), eventInstance);
|
|
482
|
+
if (escalatedRole) env.setEscalatedRole(escalatedRole);
|
|
480
483
|
try {
|
|
481
484
|
if (g) {
|
|
482
485
|
await executeGraph(g, env);
|
|
@@ -498,6 +501,7 @@ export async function executeEventHelper(eventInstance: Instance, env?: Environm
|
|
|
498
501
|
}
|
|
499
502
|
throw err;
|
|
500
503
|
} finally {
|
|
504
|
+
env.resetEscalatedRole();
|
|
501
505
|
if (!isLocalEnv) env.switchActiveModuleName(oldModuleName);
|
|
502
506
|
}
|
|
503
507
|
}
|