@dawn-ai/langchain 0.1.8 → 0.3.0
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/LICENSE +1 -1
- package/README.md +16 -2
- package/dist/agent-adapter.d.ts +60 -4
- package/dist/agent-adapter.d.ts.map +1 -1
- package/dist/agent-adapter.js +396 -77
- package/dist/chat-model-factory.d.ts +11 -0
- package/dist/chat-model-factory.d.ts.map +1 -0
- package/dist/chat-model-factory.js +55 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/model-provider-resolver.d.ts +8 -0
- package/dist/model-provider-resolver.d.ts.map +1 -0
- package/dist/model-provider-resolver.js +40 -0
- package/dist/offload/offload-store.d.ts +30 -0
- package/dist/offload/offload-store.d.ts.map +1 -0
- package/dist/offload/offload-store.js +105 -0
- package/dist/offload/offload-tool-output.d.ts +10 -0
- package/dist/offload/offload-tool-output.d.ts.map +1 -0
- package/dist/offload/offload-tool-output.js +18 -0
- package/dist/offload/stub.d.ts +8 -0
- package/dist/offload/stub.d.ts.map +1 -0
- package/dist/offload/stub.js +14 -0
- package/dist/subagent-dispatcher.d.ts +33 -0
- package/dist/subagent-dispatcher.d.ts.map +1 -0
- package/dist/subagent-dispatcher.js +182 -0
- package/dist/subagent-tool-bridge.d.ts +27 -0
- package/dist/subagent-tool-bridge.d.ts.map +1 -0
- package/dist/subagent-tool-bridge.js +32 -0
- package/dist/summarization/hook.d.ts +31 -0
- package/dist/summarization/hook.d.ts.map +1 -0
- package/dist/summarization/hook.js +44 -0
- package/dist/summarization/index.d.ts +5 -0
- package/dist/summarization/index.d.ts.map +1 -0
- package/dist/summarization/index.js +4 -0
- package/dist/summarization/split.d.ts +12 -0
- package/dist/summarization/split.d.ts.map +1 -0
- package/dist/summarization/split.js +27 -0
- package/dist/summarization/summarize.d.ts +10 -0
- package/dist/summarization/summarize.d.ts.map +1 -0
- package/dist/summarization/summarize.js +30 -0
- package/dist/summarization/token-counter.d.ts +10 -0
- package/dist/summarization/token-counter.d.ts.map +1 -0
- package/dist/summarization/token-counter.js +29 -0
- package/dist/tool-converter.d.ts +5 -1
- package/dist/tool-converter.d.ts.map +1 -1
- package/dist/tool-converter.js +74 -21
- package/dist/unwrap-tool-result.d.ts +35 -0
- package/dist/unwrap-tool-result.d.ts.map +1 -0
- package/dist/unwrap-tool-result.js +42 -0
- package/package.json +55 -10
package/dist/index.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { Command } from "@langchain/langgraph";
|
|
2
|
+
export { __resetMaterializedAgentsForTests, executeAgent, materializeAgentGraph, streamAgent, } from "./agent-adapter.js";
|
|
2
3
|
export { chainAdapter } from "./chain-adapter.js";
|
|
4
|
+
export { createChatModel } from "./chat-model-factory.js";
|
|
5
|
+
export { inferProvider, resolveProvider } from "./model-provider-resolver.js";
|
|
6
|
+
export { buildOffloadFileName, OffloadStore } from "./offload/offload-store.js";
|
|
7
|
+
export { offloadToolOutput } from "./offload/offload-tool-output.js";
|
|
8
|
+
export { buildStub } from "./offload/stub.js";
|
|
3
9
|
export { isRetryableError, withRetry } from "./retry.js";
|
|
4
10
|
export { materializeStateSchema } from "./state-adapter.js";
|
|
11
|
+
export { createSubagentStreamContext, dispatchSubagent, MAX_SUBAGENT_DEPTH, } from "./subagent-dispatcher.js";
|
|
12
|
+
export { bridgeSubagentTool } from "./subagent-tool-bridge.js";
|
|
13
|
+
export * from "./summarization/index.js";
|
|
5
14
|
export { convertToolToLangChain } from "./tool-converter.js";
|
|
6
15
|
export { executeWithToolLoop } from "./tool-loop.js";
|
|
16
|
+
export { unwrapToolResult } from "./unwrap-tool-result.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BuiltInModelProviderId, ModelProviderId } from "@dawn-ai/sdk";
|
|
2
|
+
export declare const SUPPORTED_AGENT_PROVIDERS: readonly ["openai", "anthropic", "google", "mistral", "groq", "ollama", "xai", "openrouter"];
|
|
3
|
+
export declare function inferProvider(model: string): BuiltInModelProviderId | undefined;
|
|
4
|
+
export declare function resolveProvider(options: {
|
|
5
|
+
readonly model: string;
|
|
6
|
+
readonly provider?: ModelProviderId;
|
|
7
|
+
}): BuiltInModelProviderId;
|
|
8
|
+
//# sourceMappingURL=model-provider-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-provider-resolver.d.ts","sourceRoot":"","sources":["../src/model-provider-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE3E,eAAO,MAAM,yBAAyB,8FASgB,CAAA;AAItD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAgB/E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAA;CACpC,GAAG,sBAAsB,CAgBzB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const SUPPORTED_AGENT_PROVIDERS = [
|
|
2
|
+
"openai",
|
|
3
|
+
"anthropic",
|
|
4
|
+
"google",
|
|
5
|
+
"mistral",
|
|
6
|
+
"groq",
|
|
7
|
+
"ollama",
|
|
8
|
+
"xai",
|
|
9
|
+
"openrouter",
|
|
10
|
+
];
|
|
11
|
+
const supportedProviderSet = new Set(SUPPORTED_AGENT_PROVIDERS);
|
|
12
|
+
export function inferProvider(model) {
|
|
13
|
+
const normalized = model.trim().toLowerCase();
|
|
14
|
+
if (/^(gpt-|o3|o4)/.test(normalized))
|
|
15
|
+
return "openai";
|
|
16
|
+
if (normalized.startsWith("claude-"))
|
|
17
|
+
return "anthropic";
|
|
18
|
+
if (normalized.startsWith("gemini-"))
|
|
19
|
+
return "google";
|
|
20
|
+
if (normalized.startsWith("mistral-") ||
|
|
21
|
+
normalized.startsWith("mixtral-") ||
|
|
22
|
+
normalized.startsWith("codestral-")) {
|
|
23
|
+
return "mistral";
|
|
24
|
+
}
|
|
25
|
+
if (normalized.startsWith("grok-"))
|
|
26
|
+
return "xai";
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
export function resolveProvider(options) {
|
|
30
|
+
if (options.provider !== undefined) {
|
|
31
|
+
if (supportedProviderSet.has(options.provider)) {
|
|
32
|
+
return options.provider;
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`Unsupported agent provider "${options.provider}". Supported providers: ${SUPPORTED_AGENT_PROVIDERS.join(", ")}.`);
|
|
35
|
+
}
|
|
36
|
+
const inferred = inferProvider(options.model);
|
|
37
|
+
if (inferred)
|
|
38
|
+
return inferred;
|
|
39
|
+
throw new Error(`Could not infer a LangChain provider for model "${options.model}". Set provider explicitly on agent({ provider: "...", model: "${options.model}", ... }).`);
|
|
40
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { FilesystemBackend } from "@dawn-ai/workspace";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic offload filename. Keyed on the tool_call_id when present
|
|
4
|
+
* (unique per call in production; fixture-controlled in replay tests). Falls
|
|
5
|
+
* back to a content hash when no id is available — still deterministic and
|
|
6
|
+
* reproducible, since the caller controls the content.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildOffloadFileName(toolName: string, content: string, toolCallId?: string): string;
|
|
9
|
+
export interface OffloadStoreOptions {
|
|
10
|
+
readonly backend: FilesystemBackend;
|
|
11
|
+
readonly workspaceRoot: string;
|
|
12
|
+
readonly signal: AbortSignal;
|
|
13
|
+
readonly maxBytes: number;
|
|
14
|
+
readonly ttlMs: number;
|
|
15
|
+
readonly gcThrottleMs: number;
|
|
16
|
+
/** Injectable clock for tests. Defaults to Date.now. */
|
|
17
|
+
readonly now?: () => number;
|
|
18
|
+
}
|
|
19
|
+
export declare class OffloadStore {
|
|
20
|
+
private readonly opts;
|
|
21
|
+
private lastGcAt;
|
|
22
|
+
constructor(opts: OffloadStoreOptions);
|
|
23
|
+
private get ctx();
|
|
24
|
+
private now;
|
|
25
|
+
/** Persist full content; returns the workspace-relative path. Runs throttled GC. */
|
|
26
|
+
write(toolName: string, content: string, toolCallId?: string): Promise<string>;
|
|
27
|
+
private maybeGc;
|
|
28
|
+
private safeRemove;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=offload-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offload-store.d.ts","sourceRoot":"","sources":["../../src/offload/offload-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAS3D;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAOR;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAC5B;AAED,qBAAa,YAAY;IAEX,OAAO,CAAC,QAAQ,CAAC,IAAI;IADjC,OAAO,CAAC,QAAQ,CAAQ;gBACK,IAAI,EAAE,mBAAmB;IAItD,OAAO,KAAK,GAAG,GAEd;IACD,OAAO,CAAC,GAAG;IAIX,oFAAoF;IAC9E,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAWtE,OAAO;YA+CP,UAAU;CAQzB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
// NOTE: must match the tool-outputs/ predicate in @dawn-ai/core workspace capability readFile.
|
|
4
|
+
const SUBDIR = "tool-outputs";
|
|
5
|
+
function sanitizeSegment(value) {
|
|
6
|
+
return value.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Deterministic offload filename. Keyed on the tool_call_id when present
|
|
10
|
+
* (unique per call in production; fixture-controlled in replay tests). Falls
|
|
11
|
+
* back to a content hash when no id is available — still deterministic and
|
|
12
|
+
* reproducible, since the caller controls the content.
|
|
13
|
+
*/
|
|
14
|
+
export function buildOffloadFileName(toolName, content, toolCallId) {
|
|
15
|
+
const name = sanitizeSegment(toolName);
|
|
16
|
+
if (toolCallId && toolCallId.length > 0) {
|
|
17
|
+
return `${name}-${sanitizeSegment(toolCallId)}.txt`;
|
|
18
|
+
}
|
|
19
|
+
const hash = createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
20
|
+
return `${name}-${hash}.txt`;
|
|
21
|
+
}
|
|
22
|
+
export class OffloadStore {
|
|
23
|
+
opts;
|
|
24
|
+
lastGcAt;
|
|
25
|
+
constructor(opts) {
|
|
26
|
+
this.opts = opts;
|
|
27
|
+
this.lastGcAt = (opts.now ?? Date.now)();
|
|
28
|
+
}
|
|
29
|
+
get ctx() {
|
|
30
|
+
return { signal: this.opts.signal, workspaceRoot: this.opts.workspaceRoot };
|
|
31
|
+
}
|
|
32
|
+
now() {
|
|
33
|
+
return (this.opts.now ?? Date.now)();
|
|
34
|
+
}
|
|
35
|
+
/** Persist full content; returns the workspace-relative path. Runs throttled GC. */
|
|
36
|
+
async write(toolName, content, toolCallId) {
|
|
37
|
+
const fileName = buildOffloadFileName(toolName, content, toolCallId);
|
|
38
|
+
const relPath = `${SUBDIR}/${fileName}`;
|
|
39
|
+
const dirAbs = join(this.opts.workspaceRoot, SUBDIR);
|
|
40
|
+
await this.opts.backend.mkdir?.(dirAbs, this.ctx);
|
|
41
|
+
const absPath = join(this.opts.workspaceRoot, relPath);
|
|
42
|
+
await this.opts.backend.writeFile(absPath, content, this.ctx);
|
|
43
|
+
await this.maybeGc();
|
|
44
|
+
return relPath;
|
|
45
|
+
}
|
|
46
|
+
async maybeGc() {
|
|
47
|
+
const now = this.now();
|
|
48
|
+
if (now - this.lastGcAt < this.opts.gcThrottleMs)
|
|
49
|
+
return;
|
|
50
|
+
this.lastGcAt = now;
|
|
51
|
+
const { backend } = this.opts;
|
|
52
|
+
if (!backend.statFile || !backend.removeFile)
|
|
53
|
+
return; // GC unsupported by backend
|
|
54
|
+
const dirAbs = join(this.opts.workspaceRoot, SUBDIR);
|
|
55
|
+
let names;
|
|
56
|
+
try {
|
|
57
|
+
names = await backend.listDir(dirAbs, this.ctx);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return; // dir not created yet / unreadable
|
|
61
|
+
}
|
|
62
|
+
const entries = [];
|
|
63
|
+
for (const name of names) {
|
|
64
|
+
const abs = join(dirAbs, name);
|
|
65
|
+
try {
|
|
66
|
+
const s = await backend.statFile(abs, this.ctx);
|
|
67
|
+
entries.push({ abs, size: s.size, mtimeMs: s.mtimeMs });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* skip unstattable */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// TTL pass
|
|
74
|
+
const ttlCutoff = now - this.opts.ttlMs;
|
|
75
|
+
const survivors = [];
|
|
76
|
+
for (const e of entries) {
|
|
77
|
+
if (e.mtimeMs < ttlCutoff) {
|
|
78
|
+
await this.safeRemove(e.abs);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
survivors.push(e);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Size pass: oldest-first until under maxBytes
|
|
85
|
+
let total = survivors.reduce((sum, e) => sum + e.size, 0);
|
|
86
|
+
if (total <= this.opts.maxBytes)
|
|
87
|
+
return;
|
|
88
|
+
survivors.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
89
|
+
for (const e of survivors) {
|
|
90
|
+
if (total <= this.opts.maxBytes)
|
|
91
|
+
break;
|
|
92
|
+
if (await this.safeRemove(e.abs))
|
|
93
|
+
total -= e.size;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async safeRemove(abs) {
|
|
97
|
+
try {
|
|
98
|
+
await this.opts.backend.removeFile?.(abs, this.ctx);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OffloadStore } from "./offload-store.js";
|
|
2
|
+
export interface OffloadToolOutputCtx {
|
|
3
|
+
readonly toolName: string;
|
|
4
|
+
readonly thresholdChars: number;
|
|
5
|
+
readonly previewLines: number;
|
|
6
|
+
readonly store: Pick<OffloadStore, "write">;
|
|
7
|
+
readonly toolCallId?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function offloadToolOutput(content: string, ctx: OffloadToolOutputCtx): Promise<string>;
|
|
10
|
+
//# sourceMappingURL=offload-tool-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offload-tool-output.d.ts","sourceRoot":"","sources":["../../src/offload/offload-tool-output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAGtD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,MAAM,CAAC,CAcjB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { buildStub } from "./stub.js";
|
|
2
|
+
export async function offloadToolOutput(content, ctx) {
|
|
3
|
+
if (content.length <= ctx.thresholdChars)
|
|
4
|
+
return content;
|
|
5
|
+
try {
|
|
6
|
+
const relPath = await ctx.store.write(ctx.toolName, content, ctx.toolCallId);
|
|
7
|
+
return buildStub({
|
|
8
|
+
content,
|
|
9
|
+
relPath,
|
|
10
|
+
previewLines: ctx.previewLines,
|
|
11
|
+
thresholdChars: ctx.thresholdChars,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Never break a tool because offloading failed; keep the original content.
|
|
16
|
+
return content;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stub.d.ts","sourceRoot":"","sources":["../../src/offload/stub.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAChC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAarD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function buildStub(args) {
|
|
2
|
+
const lines = args.content.split("\n").slice(0, args.previewLines);
|
|
3
|
+
const shown = lines.length;
|
|
4
|
+
const preview = lines.join("\n");
|
|
5
|
+
const chars = args.content.length.toLocaleString("en-US");
|
|
6
|
+
const threshold = args.thresholdChars.toLocaleString("en-US");
|
|
7
|
+
return [
|
|
8
|
+
`[Tool output offloaded — ${chars} chars exceeded the ${threshold}-char limit.`,
|
|
9
|
+
`Full output saved to: ${args.relPath}`,
|
|
10
|
+
`Preview (first ${shown} lines):`,
|
|
11
|
+
preview,
|
|
12
|
+
`Read the full output with the readFile tool at the path above.]`,
|
|
13
|
+
].join("\n");
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface SubagentEvent {
|
|
2
|
+
readonly event: string;
|
|
3
|
+
readonly data: Record<string, unknown>;
|
|
4
|
+
}
|
|
5
|
+
export interface SubagentStreamContext {
|
|
6
|
+
/**
|
|
7
|
+
* Active child-run depth. While > 0, the parent's streamFromRunnable should
|
|
8
|
+
* suppress on_chat_model_stream emissions because those events are firing
|
|
9
|
+
* for the child run via LangChain v2 streamEvents async-local-storage
|
|
10
|
+
* tracing. The dispatcher already emits a `subagent.message` envelope for
|
|
11
|
+
* each child token, so suppressing avoids duplicates on the parent stream.
|
|
12
|
+
*/
|
|
13
|
+
activeChildRuns: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function createSubagentStreamContext(): SubagentStreamContext;
|
|
16
|
+
export interface DispatchArgs {
|
|
17
|
+
readonly childGraph: {
|
|
18
|
+
invoke: (input: unknown, config: unknown) => Promise<unknown>;
|
|
19
|
+
};
|
|
20
|
+
readonly input: string;
|
|
21
|
+
readonly parentConfig: Record<string, unknown>;
|
|
22
|
+
readonly writer: (event: SubagentEvent) => void;
|
|
23
|
+
readonly callId: string;
|
|
24
|
+
readonly childRouteId: string;
|
|
25
|
+
readonly subagentName: string;
|
|
26
|
+
readonly streamContext?: SubagentStreamContext;
|
|
27
|
+
}
|
|
28
|
+
export interface DispatchResult {
|
|
29
|
+
readonly finalText: string;
|
|
30
|
+
}
|
|
31
|
+
export declare const MAX_SUBAGENT_DEPTH = 3;
|
|
32
|
+
export declare function dispatchSubagent(args: DispatchArgs): Promise<DispatchResult>;
|
|
33
|
+
//# sourceMappingURL=subagent-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent-dispatcher.d.ts","sourceRoot":"","sources":["../src/subagent-dispatcher.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACvC;AA2BD,MAAM,WAAW,qBAAqB;IACpC;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,wBAAgB,2BAA2B,IAAI,qBAAqB,CAEnE;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE;QAAE,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAA;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,qBAAqB,CAAA;CAC/C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,eAAO,MAAM,kBAAkB,IAAI,CAAA;AA4CnC,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CA4BlF"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
export function createSubagentStreamContext() {
|
|
2
|
+
return { activeChildRuns: 0 };
|
|
3
|
+
}
|
|
4
|
+
export const MAX_SUBAGENT_DEPTH = 3;
|
|
5
|
+
function readDepth(config) {
|
|
6
|
+
const meta = config?.metadata ?? {};
|
|
7
|
+
const dawn = meta.dawn ?? {};
|
|
8
|
+
const depth = dawn.subagent_depth;
|
|
9
|
+
return typeof depth === "number" ? depth : 0;
|
|
10
|
+
}
|
|
11
|
+
function buildChildConfig(parentConfig, nextDepth, callId) {
|
|
12
|
+
const parentMeta = parentConfig.metadata ?? {};
|
|
13
|
+
const parentDawn = parentMeta.dawn ?? {};
|
|
14
|
+
// Build a CLEAN child config: only Dawn metadata + abort signal.
|
|
15
|
+
// We deliberately omit `callbacks`, `runId`, `tags`, `runName`, etc. that
|
|
16
|
+
// LangChain's streamEvents v2 uses to propagate child events to the parent
|
|
17
|
+
// listener. Without this, every child token would also appear as a raw
|
|
18
|
+
// chunk on the parent stream (duplicating subagent.message envelopes).
|
|
19
|
+
const childConfig = {
|
|
20
|
+
metadata: {
|
|
21
|
+
// Preserve Dawn-namespaced metadata so depth + thread continuity work,
|
|
22
|
+
// but drop everything else.
|
|
23
|
+
dawn: { ...parentDawn, subagent_depth: nextDepth, parent_call_id: callId },
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
// Forward the abort signal if the parent provided one — the child should
|
|
27
|
+
// cancel when the parent cancels.
|
|
28
|
+
if (parentConfig.signal)
|
|
29
|
+
childConfig.signal = parentConfig.signal;
|
|
30
|
+
return childConfig;
|
|
31
|
+
}
|
|
32
|
+
function extractFinalText(graphOutput) {
|
|
33
|
+
const messages = graphOutput?.messages ?? [];
|
|
34
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
35
|
+
const m = messages[i];
|
|
36
|
+
const kind = typeof m.getType === "function" ? m.getType() : m.type;
|
|
37
|
+
if (kind === "ai" && typeof m.content === "string")
|
|
38
|
+
return m.content;
|
|
39
|
+
}
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
export async function dispatchSubagent(args) {
|
|
43
|
+
const currentDepth = readDepth(args.parentConfig);
|
|
44
|
+
const nextDepth = currentDepth + 1;
|
|
45
|
+
if (nextDepth > MAX_SUBAGENT_DEPTH) {
|
|
46
|
+
return {
|
|
47
|
+
finalText: `subagent_depth_exceeded: cannot dispatch '${args.subagentName}' at depth ${nextDepth} (max ${MAX_SUBAGENT_DEPTH}).`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const childConfig = buildChildConfig(args.parentConfig, nextDepth, args.callId);
|
|
51
|
+
args.writer({
|
|
52
|
+
event: "subagent.start",
|
|
53
|
+
data: {
|
|
54
|
+
call_id: args.callId,
|
|
55
|
+
subagent: args.subagentName,
|
|
56
|
+
route_id: args.childRouteId,
|
|
57
|
+
depth: nextDepth,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
if (args.streamContext)
|
|
61
|
+
args.streamContext.activeChildRuns++;
|
|
62
|
+
try {
|
|
63
|
+
return await runChild(args, childConfig);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
if (args.streamContext)
|
|
67
|
+
args.streamContext.activeChildRuns--;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function runChild(args, childConfig) {
|
|
71
|
+
let output;
|
|
72
|
+
try {
|
|
73
|
+
const streamable = args.childGraph;
|
|
74
|
+
if (typeof streamable.dawnStream === "function") {
|
|
75
|
+
for await (const chunk of streamable.dawnStream({ messages: [{ role: "user", content: args.input }] }, childConfig)) {
|
|
76
|
+
switch (chunk.type) {
|
|
77
|
+
case "done":
|
|
78
|
+
output = chunk.output;
|
|
79
|
+
break;
|
|
80
|
+
case "tool_call":
|
|
81
|
+
args.writer({
|
|
82
|
+
event: "subagent.tool_call",
|
|
83
|
+
data: {
|
|
84
|
+
call_id: args.callId,
|
|
85
|
+
tool: chunk.name,
|
|
86
|
+
input: chunk.input,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
break;
|
|
90
|
+
case "tool_result":
|
|
91
|
+
args.writer({
|
|
92
|
+
event: "subagent.tool_result",
|
|
93
|
+
data: {
|
|
94
|
+
call_id: args.callId,
|
|
95
|
+
tool: chunk.name,
|
|
96
|
+
output: chunk.output,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
break;
|
|
100
|
+
case "chunk": {
|
|
101
|
+
const data = chunk.data;
|
|
102
|
+
if (typeof data === "string" && data.length > 0) {
|
|
103
|
+
args.writer({
|
|
104
|
+
event: "subagent.message",
|
|
105
|
+
data: { call_id: args.callId, chunk: data },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
default: {
|
|
111
|
+
// Capability-contributed events (e.g. plan_update). Prefix with
|
|
112
|
+
// subagent. and forward the chunk's data payload verbatim,
|
|
113
|
+
// attaching call_id.
|
|
114
|
+
const data = chunk.data;
|
|
115
|
+
args.writer({
|
|
116
|
+
event: `subagent.${chunk.type}`,
|
|
117
|
+
data: {
|
|
118
|
+
call_id: args.callId,
|
|
119
|
+
...(typeof data === "object" && data !== null ? data : { value: data }),
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (typeof streamable.streamEvents === "function") {
|
|
128
|
+
for await (const event of streamable.streamEvents({ messages: [{ role: "user", content: args.input }] }, { ...childConfig, version: "v2" })) {
|
|
129
|
+
switch (event.event) {
|
|
130
|
+
case "on_tool_start":
|
|
131
|
+
args.writer({
|
|
132
|
+
event: "subagent.tool_call",
|
|
133
|
+
data: {
|
|
134
|
+
call_id: args.callId,
|
|
135
|
+
tool: event.name,
|
|
136
|
+
input: event.data.chunk ?? event.data.output,
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
break;
|
|
140
|
+
case "on_tool_end":
|
|
141
|
+
args.writer({
|
|
142
|
+
event: "subagent.tool_result",
|
|
143
|
+
data: { call_id: args.callId, tool: event.name, output: event.data.output },
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
case "on_chat_model_stream": {
|
|
147
|
+
const content = event.data.chunk?.content;
|
|
148
|
+
if (typeof content === "string" && content.length > 0) {
|
|
149
|
+
args.writer({
|
|
150
|
+
event: "subagent.message",
|
|
151
|
+
data: { call_id: args.callId, chunk: content },
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case "on_chain_end":
|
|
157
|
+
if (event.name === "LangGraph") {
|
|
158
|
+
output = event.data.output;
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
output = await streamable.invoke({ messages: [{ role: "user", content: args.input }] }, childConfig);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
170
|
+
args.writer({
|
|
171
|
+
event: "subagent.end",
|
|
172
|
+
data: { call_id: args.callId, error: message },
|
|
173
|
+
});
|
|
174
|
+
return { finalText: `subagent_failed: ${message}` };
|
|
175
|
+
}
|
|
176
|
+
const finalText = extractFinalText(output);
|
|
177
|
+
args.writer({
|
|
178
|
+
event: "subagent.end",
|
|
179
|
+
data: { call_id: args.callId, final_message: finalText },
|
|
180
|
+
});
|
|
181
|
+
return { finalText };
|
|
182
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type SubagentEvent, type SubagentStreamContext } from "./subagent-dispatcher.js";
|
|
2
|
+
export interface SubagentResolverResult {
|
|
3
|
+
readonly routeId: string;
|
|
4
|
+
readonly graph: {
|
|
5
|
+
readonly invoke: (input: unknown, config: unknown) => Promise<unknown>;
|
|
6
|
+
readonly streamEvents?: (input: unknown, options: Record<string, unknown>) => AsyncIterable<unknown>;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export interface BridgeOptions {
|
|
10
|
+
readonly subagentResolver: (leafName: string) => SubagentResolverResult | undefined;
|
|
11
|
+
readonly writer: (event: SubagentEvent) => void;
|
|
12
|
+
readonly parentConfig?: Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Shared counter that gates the parent's `on_chat_model_stream` emissions
|
|
15
|
+
* while a child run is active. See `SubagentStreamContext`.
|
|
16
|
+
*/
|
|
17
|
+
readonly streamContext?: SubagentStreamContext;
|
|
18
|
+
}
|
|
19
|
+
export interface BridgedTaskTool {
|
|
20
|
+
readonly name: "task";
|
|
21
|
+
readonly description: string;
|
|
22
|
+
readonly run: (input: unknown, context: {
|
|
23
|
+
readonly signal: AbortSignal;
|
|
24
|
+
}) => Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
export declare function bridgeSubagentTool(options: BridgeOptions): BridgedTaskTool;
|
|
27
|
+
//# sourceMappingURL=subagent-tool-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent-tool-bridge.d.ts","sourceRoot":"","sources":["../src/subagent-tool-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC3B,MAAM,0BAA0B,CAAA;AAEjC,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;QACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,CACtB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,aAAa,CAAC,OAAO,CAAC,CAAA;KAC5B,CAAA;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,sBAAsB,GAAG,SAAS,CAAA;IACnF,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/C;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,qBAAqB,CAAA;CAC/C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC7F;AASD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,eAAe,CA0B1E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { dispatchSubagent, } from "./subagent-dispatcher.js";
|
|
2
|
+
const TASK_TOOL_DESCRIPTION = "Dispatch a sub-task to a specialized subagent. See the # Subagents section of your system prompt for available agents and when to use each.";
|
|
3
|
+
function generateCallId() {
|
|
4
|
+
return `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5
|
+
}
|
|
6
|
+
export function bridgeSubagentTool(options) {
|
|
7
|
+
return {
|
|
8
|
+
name: "task",
|
|
9
|
+
description: TASK_TOOL_DESCRIPTION,
|
|
10
|
+
run: async (input, _ctx) => {
|
|
11
|
+
const { subagent, input: taskInput } = input;
|
|
12
|
+
const resolved = options.subagentResolver(subagent);
|
|
13
|
+
if (!resolved) {
|
|
14
|
+
return `subagent_unknown: no subagent named '${subagent}' (resolver returned undefined)`;
|
|
15
|
+
}
|
|
16
|
+
// Cast through `never`: dispatcher's `childGraph` type expects a `Streamable`-shaped
|
|
17
|
+
// graph; resolver returns a slightly different shape with optional streamEvents. The
|
|
18
|
+
// dispatcher narrows internally via its own type guards.
|
|
19
|
+
const result = await dispatchSubagent({
|
|
20
|
+
childGraph: resolved.graph,
|
|
21
|
+
input: taskInput,
|
|
22
|
+
parentConfig: options.parentConfig ?? {},
|
|
23
|
+
writer: options.writer,
|
|
24
|
+
callId: generateCallId(),
|
|
25
|
+
childRouteId: resolved.routeId,
|
|
26
|
+
subagentName: subagent,
|
|
27
|
+
...(options.streamContext ? { streamContext: options.streamContext } : {}),
|
|
28
|
+
});
|
|
29
|
+
return result.finalText;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
export interface RunningSummary {
|
|
3
|
+
readonly summary: string;
|
|
4
|
+
readonly coveredCount: number;
|
|
5
|
+
}
|
|
6
|
+
export type TokenCounter = (text: string) => number | Promise<number>;
|
|
7
|
+
export type SummarizeFn = (args: {
|
|
8
|
+
readonly messages: readonly BaseMessage[];
|
|
9
|
+
readonly model: string;
|
|
10
|
+
readonly previousSummary?: string;
|
|
11
|
+
readonly signal: AbortSignal;
|
|
12
|
+
}) => Promise<string>;
|
|
13
|
+
export interface ResolvedSummarizationConfig {
|
|
14
|
+
readonly maxTokens: number;
|
|
15
|
+
readonly keepRecentTurns: number;
|
|
16
|
+
readonly model: string;
|
|
17
|
+
readonly tokenCounter: TokenCounter;
|
|
18
|
+
readonly summarize: SummarizeFn;
|
|
19
|
+
}
|
|
20
|
+
export interface PreModelHookState {
|
|
21
|
+
readonly messages: BaseMessage[];
|
|
22
|
+
readonly runningSummary?: RunningSummary;
|
|
23
|
+
}
|
|
24
|
+
export interface PreModelHookResult {
|
|
25
|
+
llmInputMessages?: BaseMessage[];
|
|
26
|
+
runningSummary?: RunningSummary;
|
|
27
|
+
}
|
|
28
|
+
export declare function buildSummarizationHook(cfg: ResolvedSummarizationConfig): (state: PreModelHookState, nodeConfig?: {
|
|
29
|
+
readonly signal?: AbortSignal;
|
|
30
|
+
}) => Promise<PreModelHookResult>;
|
|
31
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/summarization/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,0BAA0B,CAAA;AAI1E,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;AAErE,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAC7B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAErB,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAA;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;CACzC;AAED,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,CAAC,EAAE,WAAW,EAAE,CAAA;IAChC,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,2BAA2B,IAEnE,OAAO,iBAAiB,EACxB,aAAa;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAC7C,OAAO,CAAC,kBAAkB,CAAC,CA0C/B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SystemMessage } from "@langchain/core/messages";
|
|
2
|
+
import { splitForSummary } from "./split.js";
|
|
3
|
+
import { countMessagesTokens } from "./token-counter.js";
|
|
4
|
+
export function buildSummarizationHook(cfg) {
|
|
5
|
+
return async (state, nodeConfig) => {
|
|
6
|
+
const messages = state.messages ?? [];
|
|
7
|
+
const total = await countMessagesTokens(messages, cfg.tokenCounter);
|
|
8
|
+
if (total <= cfg.maxTokens)
|
|
9
|
+
return {};
|
|
10
|
+
const prev = state.runningSummary;
|
|
11
|
+
const coveredCount = prev?.coveredCount ?? 0;
|
|
12
|
+
const { toSummarize, recent } = splitForSummary(messages, cfg.keepRecentTurns);
|
|
13
|
+
const newlyAged = toSummarize.slice(coveredCount);
|
|
14
|
+
let summary = prev?.summary ?? "";
|
|
15
|
+
if (newlyAged.length > 0) {
|
|
16
|
+
try {
|
|
17
|
+
summary = await cfg.summarize({
|
|
18
|
+
messages: newlyAged,
|
|
19
|
+
model: cfg.model,
|
|
20
|
+
...(prev?.summary ? { previousSummary: prev.summary } : {}),
|
|
21
|
+
signal: nodeConfig?.signal ?? new AbortController().signal,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
// Summarization failed this turn — fall back to the FULL history.
|
|
26
|
+
// We must explicitly set llmInputMessages to the full messages (not
|
|
27
|
+
// return {}), otherwise a stale condensed view from a prior turn would
|
|
28
|
+
// remain in the channel and the model would answer without the latest
|
|
29
|
+
// turn's messages.
|
|
30
|
+
if (process.env.DAWN_DEBUG_SUMMARIZATION === "1") {
|
|
31
|
+
console.warn("[dawn] summarization failed — falling back to full history this turn:", error instanceof Error ? error.message : String(error));
|
|
32
|
+
}
|
|
33
|
+
return { llmInputMessages: messages };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (!summary)
|
|
37
|
+
return {};
|
|
38
|
+
const summaryMessage = new SystemMessage(`Summary of earlier conversation:\n${summary}`);
|
|
39
|
+
return {
|
|
40
|
+
llmInputMessages: [summaryMessage, ...recent],
|
|
41
|
+
runningSummary: { summary, coveredCount: toSummarize.length },
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { buildSummarizationHook, type PreModelHookResult, type PreModelHookState, type ResolvedSummarizationConfig, type RunningSummary, type SummarizeFn, type TokenCounter, } from "./hook.js";
|
|
2
|
+
export { splitForSummary } from "./split.js";
|
|
3
|
+
export { defaultSummarize } from "./summarize.js";
|
|
4
|
+
export { countMessagesTokens, defaultTokenCounter } from "./token-counter.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/summarization/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA"}
|