llm-agent-runtime 0.1.5 → 0.1.16
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/README.md +8 -2
- package/USAGE.md +530 -234
- package/dist/agent.d.ts +2 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +36 -4
- package/dist/create-runtime.d.ts +14 -4
- package/dist/create-runtime.d.ts.map +1 -1
- package/dist/create-runtime.js +112 -14
- package/dist/embeddings/xenova.d.ts +19 -0
- package/dist/embeddings/xenova.d.ts.map +1 -0
- package/dist/embeddings/xenova.js +69 -0
- package/dist/env-llm.d.ts +17 -7
- package/dist/env-llm.d.ts.map +1 -1
- package/dist/env-llm.js +67 -13
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +17 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/intents/define.d.ts +35 -0
- package/dist/intents/define.d.ts.map +1 -0
- package/dist/intents/define.js +52 -0
- package/dist/intents/general.d.ts +37 -0
- package/dist/intents/general.d.ts.map +1 -0
- package/dist/intents/general.js +67 -0
- package/dist/llm.d.ts +2 -2
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +60 -13
- package/dist/rag/index.d.ts +67 -0
- package/dist/rag/index.d.ts.map +1 -0
- package/dist/rag/index.js +138 -0
- package/dist/rag/loaders.d.ts +11 -0
- package/dist/rag/loaders.d.ts.map +1 -0
- package/dist/rag/loaders.js +80 -0
- package/dist/rag/sources.d.ts +29 -0
- package/dist/rag/sources.d.ts.map +1 -0
- package/dist/rag/sources.js +101 -0
- package/dist/services/flow-router.d.ts +1 -0
- package/dist/services/flow-router.d.ts.map +1 -1
- package/dist/services/flow-router.js +12 -0
- package/dist/services/intent-embeddings.d.ts.map +1 -1
- package/dist/services/intent-embeddings.js +5 -6
- package/dist/services/token-usage.d.ts +26 -0
- package/dist/services/token-usage.d.ts.map +1 -0
- package/dist/services/token-usage.js +63 -0
- package/dist/services/usage-store.d.ts +24 -0
- package/dist/services/usage-store.d.ts.map +1 -0
- package/dist/services/usage-store.js +92 -0
- package/dist/types.d.ts +50 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -3
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type RagSourceKind = "pdf" | "docx" | "txt" | "unknown";
|
|
2
|
+
export type ResolvedRagSource = {
|
|
3
|
+
/** Original path or URL from config. */
|
|
4
|
+
source: string;
|
|
5
|
+
/** Local file path after download (URL) or as given (path). */
|
|
6
|
+
localPath: string;
|
|
7
|
+
kind: RagSourceKind;
|
|
8
|
+
/** True when we downloaded a remote file into a temp location. */
|
|
9
|
+
isTemp: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function isHttpUrl(value: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Collect RAG sources from env.
|
|
14
|
+
*
|
|
15
|
+
* Preferred:
|
|
16
|
+
* RAG_SOURCES=/path/a.pdf,https://cdn.example.com/b.docx
|
|
17
|
+
*
|
|
18
|
+
* Also accepted:
|
|
19
|
+
* RAG_PDF_PATH=/one.pdf
|
|
20
|
+
* RAG_PDF_PATHS=/a.pdf,/b.pdf
|
|
21
|
+
* RAG_DOC_URLS=https://.../a.pdf,https://.../b.docx
|
|
22
|
+
*/
|
|
23
|
+
export declare function ragSourcesFromEnv(env?: NodeJS.ProcessEnv): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a single source (local path or http(s) URL) to a local file.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveRagSource(source: string): Promise<ResolvedRagSource>;
|
|
28
|
+
export declare function resolveRagSources(sources: string[]): Promise<ResolvedRagSource[]>;
|
|
29
|
+
//# sourceMappingURL=sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/rag/sources.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC;IACpB,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAUF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,CAiBV;AAwBD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CA6B5B;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAM9B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
function detectKind(nameOrUrl) {
|
|
5
|
+
const lower = nameOrUrl.split("?")[0].toLowerCase();
|
|
6
|
+
if (lower.endsWith(".pdf"))
|
|
7
|
+
return "pdf";
|
|
8
|
+
if (lower.endsWith(".docx"))
|
|
9
|
+
return "docx";
|
|
10
|
+
if (lower.endsWith(".txt") || lower.endsWith(".md"))
|
|
11
|
+
return "txt";
|
|
12
|
+
return "unknown";
|
|
13
|
+
}
|
|
14
|
+
export function isHttpUrl(value) {
|
|
15
|
+
return /^https?:\/\//i.test(value.trim());
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Collect RAG sources from env.
|
|
19
|
+
*
|
|
20
|
+
* Preferred:
|
|
21
|
+
* RAG_SOURCES=/path/a.pdf,https://cdn.example.com/b.docx
|
|
22
|
+
*
|
|
23
|
+
* Also accepted:
|
|
24
|
+
* RAG_PDF_PATH=/one.pdf
|
|
25
|
+
* RAG_PDF_PATHS=/a.pdf,/b.pdf
|
|
26
|
+
* RAG_DOC_URLS=https://.../a.pdf,https://.../b.docx
|
|
27
|
+
*/
|
|
28
|
+
export function ragSourcesFromEnv(env = process.env) {
|
|
29
|
+
const parts = [];
|
|
30
|
+
const pushCsv = (raw) => {
|
|
31
|
+
if (!raw?.trim())
|
|
32
|
+
return;
|
|
33
|
+
for (const item of raw.split(",")) {
|
|
34
|
+
const s = item.trim();
|
|
35
|
+
if (s)
|
|
36
|
+
parts.push(s);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
pushCsv(env.RAG_SOURCES);
|
|
40
|
+
pushCsv(env.RAG_PDF_PATH);
|
|
41
|
+
pushCsv(env.RAG_PDF_PATHS);
|
|
42
|
+
pushCsv(env.RAG_DOC_URLS);
|
|
43
|
+
return [...new Set(parts)];
|
|
44
|
+
}
|
|
45
|
+
async function downloadToTemp(url) {
|
|
46
|
+
const res = await fetch(url);
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(`Failed to download RAG source (${res.status}): ${url}`);
|
|
49
|
+
}
|
|
50
|
+
const kind = detectKind(url);
|
|
51
|
+
const ext = kind === "pdf"
|
|
52
|
+
? ".pdf"
|
|
53
|
+
: kind === "docx"
|
|
54
|
+
? ".docx"
|
|
55
|
+
: kind === "txt"
|
|
56
|
+
? ".txt"
|
|
57
|
+
: path.extname(new URL(url).pathname) || ".bin";
|
|
58
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "llm-agent-rag-"));
|
|
59
|
+
const localPath = path.join(dir, `doc${ext}`);
|
|
60
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
61
|
+
await fs.writeFile(localPath, buf);
|
|
62
|
+
return localPath;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a single source (local path or http(s) URL) to a local file.
|
|
66
|
+
*/
|
|
67
|
+
export async function resolveRagSource(source) {
|
|
68
|
+
const trimmed = source.trim();
|
|
69
|
+
if (!trimmed) {
|
|
70
|
+
throw new Error("Empty RAG source");
|
|
71
|
+
}
|
|
72
|
+
if (isHttpUrl(trimmed)) {
|
|
73
|
+
const localPath = await downloadToTemp(trimmed);
|
|
74
|
+
return {
|
|
75
|
+
source: trimmed,
|
|
76
|
+
localPath,
|
|
77
|
+
kind: detectKind(trimmed),
|
|
78
|
+
isTemp: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const localPath = path.resolve(trimmed);
|
|
82
|
+
try {
|
|
83
|
+
await fs.access(localPath);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
throw new Error(`RAG file not found: ${localPath}`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
source: trimmed,
|
|
90
|
+
localPath,
|
|
91
|
+
kind: detectKind(localPath),
|
|
92
|
+
isTemp: false,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export async function resolveRagSources(sources) {
|
|
96
|
+
const out = [];
|
|
97
|
+
for (const source of sources) {
|
|
98
|
+
out.push(await resolveRagSource(source));
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
@@ -5,6 +5,7 @@ export declare function createFlowRouter(options: {
|
|
|
5
5
|
stickyFlows: Set<string>;
|
|
6
6
|
menuMap: Record<string, string>;
|
|
7
7
|
normalizeIntent: (intent: string) => string;
|
|
8
|
+
intents: IntentDefinition[];
|
|
8
9
|
getEmbeddedIntents: () => Promise<{
|
|
9
10
|
name: string;
|
|
10
11
|
embedding: number[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow-router.d.ts","sourceRoot":"","sources":["../../src/services/flow-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAOjD,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,OAI/C,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,kBAAkB,EAAE,MAAM,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC,CAAC;IAC3E,sBAAsB,EAAE,CACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,KAC9C,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACrC,qBAAqB,EAAE,CAAC,KAAK,EAAE;QAC7B,OAAO,EAAE,MAAM,CAAC;QAChB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/B,kBAAkB,EAAE,CAClB,YAAY,EAAE,oBAAoB,EAClC,SAAS,EAAE,eAAe,KACvB,oBAAoB,GAAG;QAAE,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC;IAClE,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,CAAC,EAAE,CACb,KAAK,EAAE,iBAAiB,KACrB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACtD,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;sFAoBI;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,aAAa,GAAG,IAAI,CAAC;QACtC,mBAAmB,EAAE,MAAM,CAAC;KAC7B,KAAG,OAAO,CAAC,cAAc,CAAC;+
|
|
1
|
+
{"version":3,"file":"flow-router.d.ts","sourceRoot":"","sources":["../../src/services/flow-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAOjD,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,OAI/C,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,kBAAkB,EAAE,MAAM,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC,CAAC;IAC3E,sBAAsB,EAAE,CACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,KAC9C,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACrC,qBAAqB,EAAE,CAAC,KAAK,EAAE;QAC7B,OAAO,EAAE,MAAM,CAAC;QAChB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/B,kBAAkB,EAAE,CAClB,YAAY,EAAE,oBAAoB,EAClC,SAAS,EAAE,eAAe,KACvB,oBAAoB,GAAG;QAAE,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC;IAClE,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,CAAC,EAAE,CACb,KAAK,EAAE,iBAAiB,KACrB,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACtD,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;sFAoBI;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,aAAa,GAAG,IAAI,CAAC;QACtC,mBAAmB,EAAE,MAAM,CAAC;KAC7B,KAAG,OAAO,CAAC,cAAc,CAAC;+BAyLlB,iBAAiB,KACvB,OAAO,CAAC,kBAAkB,CAAC;EAkB/B;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAE7E;AAED,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -52,6 +52,18 @@ export function createFlowRouter(options) {
|
|
|
52
52
|
promptIntent: menuIntent,
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
// Regex fast-path: skip LLM/embeddings if a pattern matches
|
|
56
|
+
const intentsWithRegex = options.intents.filter((i) => i.regex);
|
|
57
|
+
for (const intent of intentsWithRegex) {
|
|
58
|
+
if (intent.regex.test(message)) {
|
|
59
|
+
const sticky = options.stickyFlows.has(intent.name);
|
|
60
|
+
return {
|
|
61
|
+
messageIntent: { intent: intent.name, score: 1 },
|
|
62
|
+
flow: { active: sticky, intent: sticky ? intent.name : null },
|
|
63
|
+
promptIntent: intent.name,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
55
67
|
const embeddedIntents = await options.getEmbeddedIntents();
|
|
56
68
|
const [embeddingScores, llmResult] = await Promise.all([
|
|
57
69
|
options.scoreIntentsForMessage(message, embeddedIntents),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-embeddings.d.ts","sourceRoot":"","sources":["../../src/services/intent-embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAgB1E,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;
|
|
1
|
+
{"version":3,"file":"intent-embeddings.d.ts","sourceRoot":"","sources":["../../src/services/intent-embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAgB1E,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;8BAqBsC,OAAO,CAAC,cAAc,EAAE,CAAC;sCAQnD,MAAM,mBACE,cAAc,EAAE,KAChC,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAchC,uDAAuD;;EAK1D"}
|
|
@@ -12,13 +12,12 @@ function cosineSimilarity(a, b) {
|
|
|
12
12
|
export function createIntentEmbeddings(options) {
|
|
13
13
|
let cached = null;
|
|
14
14
|
const build = async () => {
|
|
15
|
-
|
|
16
|
-
for (const intent of options.intents) {
|
|
15
|
+
return Promise.all(options.intents.map(async (intent) => {
|
|
17
16
|
const embeddings = await Promise.all(intent.examples.map((ex) => options.embeddings.embedQuery(ex)));
|
|
18
|
-
const avgEmbedding = embeddings[0].map((_, i) => embeddings.reduce((sum, emb) => sum + emb[i], 0) /
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
const avgEmbedding = embeddings[0].map((_, i) => embeddings.reduce((sum, emb) => sum + emb[i], 0) /
|
|
18
|
+
embeddings.length);
|
|
19
|
+
return { name: intent.name, embedding: avgEmbedding };
|
|
20
|
+
}));
|
|
22
21
|
};
|
|
23
22
|
const getEmbeddedIntents = async () => {
|
|
24
23
|
if (!cached) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type TokenCounts = {
|
|
2
|
+
inputTokens: number;
|
|
3
|
+
outputTokens: number;
|
|
4
|
+
totalTokens: number;
|
|
5
|
+
};
|
|
6
|
+
export type FlowTokenUsage = TokenCounts & {
|
|
7
|
+
/** Sticky / active flow intent, or null when not in a sticky flow. */
|
|
8
|
+
intent: string | null;
|
|
9
|
+
};
|
|
10
|
+
/** Returned on every `invoke`. */
|
|
11
|
+
export type TokenUsageReport = {
|
|
12
|
+
/** Tokens for this invoke only (agent turn messages). */
|
|
13
|
+
turn: TokenCounts;
|
|
14
|
+
/** Accumulated tokens for the current sticky flow (falls back to turn). */
|
|
15
|
+
flow: FlowTokenUsage;
|
|
16
|
+
/** Accumulated tokens for the whole thread/session. */
|
|
17
|
+
total: TokenCounts;
|
|
18
|
+
};
|
|
19
|
+
export declare function emptyTokenCounts(): TokenCounts;
|
|
20
|
+
export declare function addTokenCounts(a: TokenCounts, b: TokenCounts): TokenCounts;
|
|
21
|
+
/**
|
|
22
|
+
* Sum token usage from LangChain AI messages
|
|
23
|
+
* (`usage_metadata` and/or `response_metadata.tokenUsage` / `usage`).
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractTokenUsageFromMessages(messages: unknown[]): TokenCounts;
|
|
26
|
+
//# sourceMappingURL=token-usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-usage.d.ts","sourceRoot":"","sources":["../../src/services/token-usage.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG;IACzC,sEAAsE;IACtE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,kCAAkC;AAClC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,yDAAyD;IACzD,IAAI,EAAE,WAAW,CAAC;IAClB,2EAA2E;IAC3E,IAAI,EAAE,cAAc,CAAC;IACrB,uDAAuD;IACvD,KAAK,EAAE,WAAW,CAAC;CACpB,CAAC;AAEF,wBAAgB,gBAAgB,IAAI,WAAW,CAE9C;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,WAAW,CAM1E;AAiCD;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,WAAW,CA0B9E"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export function emptyTokenCounts() {
|
|
2
|
+
return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
3
|
+
}
|
|
4
|
+
export function addTokenCounts(a, b) {
|
|
5
|
+
return {
|
|
6
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
7
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
8
|
+
totalTokens: a.totalTokens + b.totalTokens,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function readUsageBlob(raw) {
|
|
12
|
+
if (!raw || typeof raw !== "object")
|
|
13
|
+
return null;
|
|
14
|
+
const u = raw;
|
|
15
|
+
const input = numberOrZero(u.input_tokens) ||
|
|
16
|
+
numberOrZero(u.inputTokens) ||
|
|
17
|
+
numberOrZero(u.prompt_tokens) ||
|
|
18
|
+
numberOrZero(u.promptTokens);
|
|
19
|
+
const output = numberOrZero(u.output_tokens) ||
|
|
20
|
+
numberOrZero(u.outputTokens) ||
|
|
21
|
+
numberOrZero(u.completion_tokens) ||
|
|
22
|
+
numberOrZero(u.completionTokens);
|
|
23
|
+
const total = numberOrZero(u.total_tokens) ||
|
|
24
|
+
numberOrZero(u.totalTokens) ||
|
|
25
|
+
input + output;
|
|
26
|
+
if (!input && !output && !total)
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
inputTokens: input,
|
|
30
|
+
outputTokens: output,
|
|
31
|
+
totalTokens: total || input + output,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function numberOrZero(v) {
|
|
35
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Sum token usage from LangChain AI messages
|
|
39
|
+
* (`usage_metadata` and/or `response_metadata.tokenUsage` / `usage`).
|
|
40
|
+
*/
|
|
41
|
+
export function extractTokenUsageFromMessages(messages) {
|
|
42
|
+
let sum = emptyTokenCounts();
|
|
43
|
+
for (const msg of messages) {
|
|
44
|
+
if (!msg || typeof msg !== "object")
|
|
45
|
+
continue;
|
|
46
|
+
const m = msg;
|
|
47
|
+
const fromUsageMeta = readUsageBlob(m.usage_metadata);
|
|
48
|
+
if (fromUsageMeta) {
|
|
49
|
+
sum = addTokenCounts(sum, fromUsageMeta);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const responseMeta = m.response_metadata;
|
|
53
|
+
if (responseMeta) {
|
|
54
|
+
const fromTokenUsage = readUsageBlob(responseMeta.tokenUsage) ||
|
|
55
|
+
readUsageBlob(responseMeta.usage) ||
|
|
56
|
+
readUsageBlob(responseMeta.token_usage);
|
|
57
|
+
if (fromTokenUsage) {
|
|
58
|
+
sum = addTokenCounts(sum, fromTokenUsage);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return sum;
|
|
63
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RedisClientType } from "redis";
|
|
2
|
+
import { type TokenCounts } from "./token-usage.js";
|
|
3
|
+
/**
|
|
4
|
+
* Persist per-thread and per-flow token totals in Redis (or memory fallback).
|
|
5
|
+
*/
|
|
6
|
+
export declare function createUsageStore(options: {
|
|
7
|
+
redis?: RedisClientType;
|
|
8
|
+
ttlSeconds?: number;
|
|
9
|
+
}): {
|
|
10
|
+
getThreadUsage: (threadId: string) => Promise<TokenCounts>;
|
|
11
|
+
getFlowUsage: (threadId: string, intent: string | null) => Promise<TokenCounts>;
|
|
12
|
+
recordTurn: (options: {
|
|
13
|
+
threadId: string;
|
|
14
|
+
flowIntent: string | null;
|
|
15
|
+
turn: TokenCounts;
|
|
16
|
+
}) => Promise<{
|
|
17
|
+
total: TokenCounts;
|
|
18
|
+
flow: TokenCounts;
|
|
19
|
+
}>;
|
|
20
|
+
clearFlowUsage: (threadId: string, intent: string) => Promise<void>;
|
|
21
|
+
clearThreadUsage: (threadId: string) => Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
export type UsageStore = ReturnType<typeof createUsageStore>;
|
|
24
|
+
//# sourceMappingURL=usage-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-store.d.ts","sourceRoot":"","sources":["../../src/services/usage-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAM1B;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;+BA0CmC,MAAM;6BAER,MAAM,UAAU,MAAM,GAAG,IAAI;0BAK1B;QACjC,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,IAAI,EAAE,WAAW,CAAC;KACnB;;;;+BAcuC,MAAM,UAAU,MAAM;iCAMpB,MAAM;EA2BjD;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { addTokenCounts, emptyTokenCounts, } from "./token-usage.js";
|
|
2
|
+
const DEFAULT_TTL = 60 * 60 * 24 * 7; // 7 days
|
|
3
|
+
/**
|
|
4
|
+
* Persist per-thread and per-flow token totals in Redis (or memory fallback).
|
|
5
|
+
*/
|
|
6
|
+
export function createUsageStore(options) {
|
|
7
|
+
const redis = options.redis;
|
|
8
|
+
const ttl = options.ttlSeconds ?? DEFAULT_TTL;
|
|
9
|
+
const memory = new Map();
|
|
10
|
+
const threadKey = (threadId) => `usage:thread:${threadId}`;
|
|
11
|
+
const flowKey = (threadId, intent) => `usage:flow:${threadId}:${intent}`;
|
|
12
|
+
const read = async (key) => {
|
|
13
|
+
if (redis) {
|
|
14
|
+
const raw = await redis.get(key);
|
|
15
|
+
if (!raw)
|
|
16
|
+
return emptyTokenCounts();
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(raw);
|
|
19
|
+
return {
|
|
20
|
+
inputTokens: parsed.inputTokens ?? 0,
|
|
21
|
+
outputTokens: parsed.outputTokens ?? 0,
|
|
22
|
+
totalTokens: parsed.totalTokens ?? 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return emptyTokenCounts();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return memory.get(key) ?? emptyTokenCounts();
|
|
30
|
+
};
|
|
31
|
+
const write = async (key, counts) => {
|
|
32
|
+
const payload = { ...counts, updatedAt: Date.now() };
|
|
33
|
+
if (redis) {
|
|
34
|
+
await redis.set(key, JSON.stringify(payload), { EX: ttl });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
memory.set(key, payload);
|
|
38
|
+
};
|
|
39
|
+
const addToKey = async (key, delta) => {
|
|
40
|
+
const next = addTokenCounts(await read(key), delta);
|
|
41
|
+
await write(key, next);
|
|
42
|
+
return next;
|
|
43
|
+
};
|
|
44
|
+
const getThreadUsage = (threadId) => read(threadKey(threadId));
|
|
45
|
+
const getFlowUsage = (threadId, intent) => {
|
|
46
|
+
if (!intent)
|
|
47
|
+
return Promise.resolve(emptyTokenCounts());
|
|
48
|
+
return read(flowKey(threadId, intent));
|
|
49
|
+
};
|
|
50
|
+
const recordTurn = async (options) => {
|
|
51
|
+
const total = await addToKey(threadKey(options.threadId), options.turn);
|
|
52
|
+
let flow = emptyTokenCounts();
|
|
53
|
+
if (options.flowIntent) {
|
|
54
|
+
flow = await addToKey(flowKey(options.threadId, options.flowIntent), options.turn);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
flow = options.turn;
|
|
58
|
+
}
|
|
59
|
+
return { total, flow };
|
|
60
|
+
};
|
|
61
|
+
const clearFlowUsage = async (threadId, intent) => {
|
|
62
|
+
const key = flowKey(threadId, intent);
|
|
63
|
+
if (redis)
|
|
64
|
+
await redis.del(key);
|
|
65
|
+
else
|
|
66
|
+
memory.delete(key);
|
|
67
|
+
};
|
|
68
|
+
const clearThreadUsage = async (threadId) => {
|
|
69
|
+
if (redis) {
|
|
70
|
+
const pattern = `usage:flow:${threadId}:*`;
|
|
71
|
+
// SCAN + DEL for flow keys; also delete thread total
|
|
72
|
+
await redis.del(threadKey(threadId));
|
|
73
|
+
for await (const key of redis.scanIterator({ MATCH: pattern, COUNT: 50 })) {
|
|
74
|
+
await redis.del(key);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
for (const key of [...memory.keys()]) {
|
|
79
|
+
if (key === threadKey(threadId) ||
|
|
80
|
+
key.startsWith(`usage:flow:${threadId}:`)) {
|
|
81
|
+
memory.delete(key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
getThreadUsage,
|
|
87
|
+
getFlowUsage,
|
|
88
|
+
recordTurn,
|
|
89
|
+
clearFlowUsage,
|
|
90
|
+
clearThreadUsage,
|
|
91
|
+
};
|
|
92
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { BaseChatModel } from "@langchain/core/language_models/chat_models"
|
|
|
2
2
|
import type { Embeddings } from "@langchain/core/embeddings";
|
|
3
3
|
import type { DynamicStructuredTool } from "@langchain/core/tools";
|
|
4
4
|
import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
|
|
5
|
+
import type { TokenUsageReport } from "./services/token-usage.js";
|
|
6
|
+
export type { TokenCounts, FlowTokenUsage, TokenUsageReport } from "./services/token-usage.js";
|
|
5
7
|
/**
|
|
6
8
|
* Simple tool → output key map.
|
|
7
9
|
*
|
|
@@ -28,21 +30,27 @@ export type IntentDefinition = {
|
|
|
28
30
|
sticky?: boolean;
|
|
29
31
|
/** Short description for the LLM intent router. */
|
|
30
32
|
description?: string;
|
|
33
|
+
/** Fast-path regex: if message matches, skip LLM/embeddings and use this intent directly. */
|
|
34
|
+
regex?: RegExp;
|
|
31
35
|
};
|
|
32
|
-
/** Context passed into prompt builders (app-defined shape). */
|
|
33
|
-
export type PromptContext = {
|
|
36
|
+
/** Context passed into prompt builders (app-defined shape via generic). */
|
|
37
|
+
export type PromptContext<TContext = unknown> = {
|
|
34
38
|
hasConversationHistory: boolean;
|
|
35
39
|
intent: string;
|
|
36
40
|
intentScore?: number;
|
|
37
41
|
message?: string;
|
|
38
|
-
/**
|
|
39
|
-
|
|
42
|
+
/**
|
|
43
|
+
* App payload from `invoke({ context })`.
|
|
44
|
+
* Default is `unknown` — pass your type to `createAgentRuntime<MyContext>(...)`
|
|
45
|
+
* or cast: `(ctx.context as MyContext)?.userInfo?.name`.
|
|
46
|
+
*/
|
|
47
|
+
context?: TContext;
|
|
40
48
|
};
|
|
41
|
-
export type IntentBinding = {
|
|
42
|
-
prompt: (ctx: PromptContext) => string;
|
|
49
|
+
export type IntentBinding<TContext = unknown> = {
|
|
50
|
+
prompt: (ctx: PromptContext<TContext>) => string;
|
|
43
51
|
tools: (phoneKey: string) => DynamicStructuredTool[];
|
|
44
52
|
};
|
|
45
|
-
export type IntentRegistry = Record<string, IntentBinding
|
|
53
|
+
export type IntentRegistry<TContext = unknown> = Record<string, IntentBinding<TContext>>;
|
|
46
54
|
export type DetectedIntentResult = {
|
|
47
55
|
intent: string;
|
|
48
56
|
score: number;
|
|
@@ -65,15 +73,30 @@ export type FlowResolution = {
|
|
|
65
73
|
promptIntent: string;
|
|
66
74
|
llmResult?: LlmIntentResult;
|
|
67
75
|
};
|
|
68
|
-
export type LlmProvider = "ollama" | "openai";
|
|
76
|
+
export type LlmProvider = "ollama" | "openai" | "openrouter";
|
|
77
|
+
/**
|
|
78
|
+
* Intent-embedding backends.
|
|
79
|
+
* - ollama — local Ollama server
|
|
80
|
+
* - openai — paid OpenAI embeddings API
|
|
81
|
+
* - xenova — free in-process Transformers.js (default when chat is not ollama and EMBEDDINGS_PROVIDER unset)
|
|
82
|
+
*/
|
|
83
|
+
export type EmbeddingsProvider = "ollama" | "openai" | "xenova";
|
|
69
84
|
export type LlmConfig = {
|
|
70
85
|
provider: LlmProvider;
|
|
71
86
|
model: string;
|
|
72
87
|
intentRouterModel?: string;
|
|
73
88
|
embeddingsModel?: string;
|
|
74
|
-
embeddingsProvider?:
|
|
89
|
+
embeddingsProvider?: EmbeddingsProvider;
|
|
90
|
+
/** Chat provider base URL (Ollama host or OpenRouter/OpenAI API root). */
|
|
75
91
|
baseUrl?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Embeddings host when different from chat (e.g. OpenRouter chat + local Ollama embeddings).
|
|
94
|
+
* Defaults to `baseUrl` for ollama chat, else `http://localhost:11434`.
|
|
95
|
+
*/
|
|
96
|
+
embeddingsBaseUrl?: string;
|
|
76
97
|
apiKey?: string;
|
|
98
|
+
/** Separate key for OpenAI embeddings when chat uses OpenRouter. */
|
|
99
|
+
embeddingsApiKey?: string;
|
|
77
100
|
temperature?: number;
|
|
78
101
|
};
|
|
79
102
|
export type CacheConfig = {
|
|
@@ -112,11 +135,11 @@ export type FinalizeFlowResult = {
|
|
|
112
135
|
/** Extra fields the app wants returned from invoke (campaign, tickets, …). */
|
|
113
136
|
extras?: Record<string, unknown>;
|
|
114
137
|
};
|
|
115
|
-
export type InvokeInput = {
|
|
138
|
+
export type InvokeInput<TContext = unknown> = {
|
|
116
139
|
message: string;
|
|
117
140
|
threadId: string;
|
|
118
141
|
/** App context passed to prompts and optional phoneKey helper. */
|
|
119
|
-
context?:
|
|
142
|
+
context?: TContext;
|
|
120
143
|
/** Force an intent (skips hybrid routing). */
|
|
121
144
|
explicitIntent?: string;
|
|
122
145
|
phoneKey?: string;
|
|
@@ -133,8 +156,13 @@ export type InvokeResult = {
|
|
|
133
156
|
reason?: string;
|
|
134
157
|
};
|
|
135
158
|
extras?: Record<string, unknown>;
|
|
159
|
+
/**
|
|
160
|
+
* Token usage for this turn, current sticky flow, and whole thread.
|
|
161
|
+
* Persisted in Redis when `redisUrl` is set (else in-memory).
|
|
162
|
+
*/
|
|
163
|
+
usage?: TokenUsageReport;
|
|
136
164
|
};
|
|
137
|
-
export type AgentRuntimeConfig = {
|
|
165
|
+
export type AgentRuntimeConfig<TContext = unknown> = {
|
|
138
166
|
redisUrl?: string;
|
|
139
167
|
/**
|
|
140
168
|
* LLM config. Omit to use `llmConfigFromEnv()` (OLLAMA_* / OPENAI_* env vars).
|
|
@@ -143,7 +171,7 @@ export type AgentRuntimeConfig = {
|
|
|
143
171
|
/** Intent catalog for embeddings + sticky flags. */
|
|
144
172
|
intents: IntentDefinition[];
|
|
145
173
|
/** Per-intent prompt + tools factory. */
|
|
146
|
-
intentRegistry: IntentRegistry
|
|
174
|
+
intentRegistry: IntentRegistry<TContext>;
|
|
147
175
|
/**
|
|
148
176
|
* Optional digit → intent routing (e.g. menu "1" → create_campaign).
|
|
149
177
|
* Prompt text alone cannot switch intent when the user types a number —
|
|
@@ -155,8 +183,14 @@ export type AgentRuntimeConfig = {
|
|
|
155
183
|
defaultIntent?: string;
|
|
156
184
|
/** Override LLM router system prompt. */
|
|
157
185
|
routerSystemPrompt?: string | ((intents: IntentDefinition[]) => string);
|
|
158
|
-
/**
|
|
159
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Optional rules prepended to **every** intent prompt (emojis, tone, language, …).
|
|
188
|
+
* Not the same as `withGeneralIntent` (that adds a catch-all *routing* intent).
|
|
189
|
+
* Skipped when you supply a custom `buildSystemPrompt` (compose it yourself there).
|
|
190
|
+
*/
|
|
191
|
+
sharedPrompt?: string | ((ctx: PromptContext<TContext>) => string);
|
|
192
|
+
/** Custom system prompt builder; default uses intentRegistry[intent].prompt (+ sharedPrompt). */
|
|
193
|
+
buildSystemPrompt?: (ctx: PromptContext<TContext>) => string;
|
|
160
194
|
/**
|
|
161
195
|
* Simple toolName → output key map (preferred).
|
|
162
196
|
* @example { create_campaign: "campaign", buy_airtime: "buyAirtime" }
|
|
@@ -185,7 +219,7 @@ export type AgentRuntimeConfig = {
|
|
|
185
219
|
resolvePhoneKey?: (input: {
|
|
186
220
|
threadId: string;
|
|
187
221
|
phoneKey?: string;
|
|
188
|
-
context?:
|
|
222
|
+
context?: TContext;
|
|
189
223
|
}) => string;
|
|
190
224
|
cache?: CacheConfig;
|
|
191
225
|
tracing?: TracingConfig;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llm-agent-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Reusable LangGraph agent runtime: intent routing, sticky flows, Redis checkpointer, optional answer cache",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"build": "tsc -p tsconfig.json",
|
|
22
22
|
"dev": "tsc -p tsconfig.json --watch",
|
|
23
23
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
24
|
-
"
|
|
24
|
+
"docs:pdf": "node generate-usage-pdf.mjs",
|
|
25
|
+
"test": "tsx --test src/services/extract.test.ts && tsx --test src/services/simple-config.test.ts && tsx --test src/services/embeddings-provider.test.ts && tsx --test src/rag/sources.test.ts && tsx --test src/intents/general.test.ts && tsx --test src/intents/define.test.ts && tsx --test src/services/token-usage.test.ts",
|
|
25
26
|
"prepublishOnly": "pnpm build && pnpm test"
|
|
26
27
|
},
|
|
27
28
|
"publishConfig": {
|
|
@@ -43,13 +44,19 @@
|
|
|
43
44
|
"redis": "^6.0.0"
|
|
44
45
|
},
|
|
45
46
|
"optionalDependencies": {
|
|
46
|
-
"
|
|
47
|
+
"@xenova/transformers": "^2.17.2",
|
|
48
|
+
"langsmith": "^0.7.10",
|
|
49
|
+
"mammoth": "^1.9.0",
|
|
50
|
+
"pdf-parse": "^1.1.1"
|
|
47
51
|
},
|
|
48
52
|
"devDependencies": {
|
|
49
53
|
"@langchain/core": "^1.2.0",
|
|
50
54
|
"@langchain/langgraph": "^1.4.4",
|
|
51
55
|
"@langchain/langgraph-checkpoint": "^1.1.2",
|
|
52
56
|
"@types/node": "^25.9.3",
|
|
57
|
+
"@xenova/transformers": "^2.17.2",
|
|
58
|
+
"mammoth": "^1.12.0",
|
|
59
|
+
"pdf-parse": "^1.1.4",
|
|
53
60
|
"tsx": "^4.23.0",
|
|
54
61
|
"typescript": "^6.0.3",
|
|
55
62
|
"zod": "^4.4.3"
|