langflower 0.0.7 → 0.0.9
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/package.json +1 -1
- package/ui-dist/{chunk-Y6VMBLSJ.js → chunk-7UMVYN3A.js} +1 -1
- package/ui-dist/{chunk-6DQ2XSRW.js → chunk-MDGXX73Z.js} +3 -3
- package/ui-dist/index.html +1 -1
- package/ui-dist/main-2JJGCNTD.js +313 -0
- package/vendor/common-nodes/dist/ai/chat-completion-stream.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/critique/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/critique/node.js +2 -2
- package/vendor/common-nodes/dist/ai/fake-llm/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/features/chat-completion-stream.d.ts +53 -0
- package/vendor/common-nodes/dist/ai/features/chat-completion-stream.js +1 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/autokick-recovery.d.ts +13 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/autokick-recovery.js +17 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/classify-llm-failure.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/classify-llm-failure.js +113 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/dead-loop-detector.d.ts +28 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/dead-loop-detector.js +194 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/live-get-tools.d.ts +9 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/live-get-tools.js +21 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-reducer.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-reducer.js +216 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.d.ts +125 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.js +39 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/normalize-llm-recovery-policy.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/normalize-llm-recovery-policy.js +59 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/normalize-tool-result.d.ts +1 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/normalize-tool-result.js +7 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/observe-provider-stream.d.ts +43 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/operators/observe-provider-stream.js +96 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.d.ts +32 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.js +58 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.d.ts +80 -0
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.js +681 -0
- package/vendor/common-nodes/dist/ai/features/llm-role-preset.d.ts +69 -0
- package/vendor/common-nodes/dist/ai/features/llm-role-preset.js +265 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.d.ts +148 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.js +198 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.d.ts +21 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.js +118 -0
- package/vendor/common-nodes/dist/ai/features/openai/context-length-error.d.ts +14 -0
- package/vendor/common-nodes/dist/ai/features/openai/context-length-error.js +63 -0
- package/vendor/common-nodes/dist/ai/features/openai/create-chat-completion-stream.d.ts +19 -0
- package/vendor/common-nodes/dist/ai/features/openai/create-chat-completion-stream.js +182 -0
- package/vendor/common-nodes/dist/ai/features/openai/list-provider-models.d.ts +28 -0
- package/vendor/common-nodes/dist/ai/features/openai/list-provider-models.js +52 -0
- package/vendor/common-nodes/dist/ai/features/openai/llm-context-compaction.d.ts +31 -0
- package/vendor/common-nodes/dist/ai/features/openai/llm-context-compaction.js +326 -0
- package/vendor/common-nodes/dist/ai/features/openai/normalize-compaction-params.d.ts +14 -0
- package/vendor/common-nodes/dist/ai/features/openai/normalize-compaction-params.js +21 -0
- package/vendor/common-nodes/dist/ai/features/openai/prepare-chat-completion.d.ts +30 -0
- package/vendor/common-nodes/dist/ai/features/openai/prepare-chat-completion.js +132 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/control-tools.d.ts +20 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/control-tools.js +70 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.d.ts +35 -0
- package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.js +106 -0
- package/vendor/common-nodes/dist/ai/features/prompt/build-effective-system-prompt.d.ts +9 -0
- package/vendor/common-nodes/dist/ai/features/prompt/build-effective-system-prompt.js +21 -0
- package/vendor/common-nodes/dist/ai/features/prompt/normalize-max-iterations.d.ts +13 -0
- package/vendor/common-nodes/dist/ai/features/prompt/normalize-max-iterations.js +23 -0
- package/vendor/common-nodes/dist/ai/features/prompt/resolve-chat-provider-model.d.ts +10 -0
- package/vendor/common-nodes/dist/ai/features/prompt/resolve-chat-provider-model.js +8 -0
- package/vendor/common-nodes/dist/ai/features/run-host-services.d.ts +54 -0
- package/vendor/common-nodes/dist/ai/features/run-host-services.js +29 -0
- package/vendor/common-nodes/dist/ai/features/scripted-chat-completion-stream.d.ts +17 -0
- package/vendor/common-nodes/dist/ai/features/scripted-chat-completion-stream.js +61 -0
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.d.ts +39 -0
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.js +43 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-compaction-ui-schema.d.ts +14 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-compaction-ui-schema.js +18 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-panel-ui-schema.d.ts +80 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-panel-ui-schema.js +76 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.d.ts +113 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.js +131 -0
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.d.ts +6 -0
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.js +30 -0
- package/vendor/common-nodes/dist/ai/llm-loop/autokick-recovery.d.ts +13 -0
- package/vendor/common-nodes/dist/ai/llm-loop/autokick-recovery.js +17 -0
- package/vendor/common-nodes/dist/ai/llm-loop/dead-loop-detector.d.ts +28 -0
- package/vendor/common-nodes/dist/ai/llm-loop/dead-loop-detector.js +194 -0
- package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-reducer.js +36 -0
- package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-types.d.ts +35 -0
- package/vendor/common-nodes/dist/ai/llm-loop/llm-loop-types.js +19 -0
- package/vendor/common-nodes/dist/ai/llm-loop/normalize-llm-recovery-policy.js +52 -9
- package/vendor/common-nodes/dist/ai/llm-loop/operators/observe-provider-stream.d.ts +8 -2
- package/vendor/common-nodes/dist/ai/llm-loop/operators/observe-provider-stream.js +43 -13
- package/vendor/common-nodes/dist/ai/llm-loop/run-llm-loop.d.ts +3 -5
- package/vendor/common-nodes/dist/ai/llm-loop/run-llm-loop.js +116 -1
- package/vendor/common-nodes/dist/ai/llm-recovery-ui-schema.d.ts +82 -0
- package/vendor/common-nodes/dist/ai/llm-recovery-ui-schema.js +95 -0
- package/vendor/common-nodes/dist/ai/llm-session-shell.d.ts +4 -9
- package/vendor/common-nodes/dist/ai/llm-session-shell.js +2 -4
- package/vendor/common-nodes/dist/ai/nodes/critique/node.d.ts +588 -0
- package/vendor/common-nodes/dist/ai/nodes/critique/node.js +240 -0
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.d.ts +571 -0
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.js +208 -0
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.d.ts +587 -0
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.js +91 -0
- package/vendor/common-nodes/dist/ai/nodes/review/node.d.ts +593 -0
- package/vendor/common-nodes/dist/ai/nodes/review/node.js +240 -0
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.d.ts +630 -0
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.js +302 -0
- package/vendor/common-nodes/dist/ai/openai/create-chat-completion-stream.js +6 -0
- package/vendor/common-nodes/dist/ai/openai/prepare-chat-completion.d.ts +2 -0
- package/vendor/common-nodes/dist/ai/openai/prepare-chat-completion.js +18 -0
- package/vendor/common-nodes/dist/ai/openai-llm/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/review/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/review/node.js +2 -2
- package/vendor/common-nodes/dist/ai/sub-agent/node.d.ts +246 -0
- package/vendor/common-nodes/dist/ai/sub-agent/node.js +2 -2
- package/vendor/common-nodes/dist/catalog.js +7 -5
- package/vendor/common-nodes/dist/compile-custom-nodes/node.d.ts +21 -0
- package/vendor/common-nodes/dist/compile-custom-nodes/node.js +45 -0
- package/vendor/common-nodes/dist/crawl/crawl/node.js +1 -1
- package/vendor/common-nodes/dist/crawl/fetch-url/node.js +1 -1
- package/vendor/common-nodes/dist/flow/repeat/node.js +6 -1
- package/vendor/common-nodes/dist/langflower-tools/emit-registration-tools.d.ts +18 -0
- package/vendor/common-nodes/dist/langflower-tools/emit-registration-tools.js +29 -0
- package/vendor/common-nodes/dist/langflower-tools/node.d.ts +20 -0
- package/vendor/common-nodes/dist/langflower-tools/node.js +77 -0
- package/vendor/common-nodes/dist/text/append-file/node.js +1 -1
- package/vendor/common-nodes/dist/text/read-file/node.js +1 -1
- package/vendor/common-nodes/dist/text/write-file/node.js +1 -1
- package/vendor/common-nodes/dist/tools/inventory-tool-round.d.ts +2 -2
- package/vendor/common-nodes/package.json +10 -10
- package/vendor/compiler/dist/compile-project-nodes.d.ts +2 -1
- package/vendor/compiler/dist/compile-project-nodes.js +35 -6
- package/vendor/compiler/dist/resolve-host-types.d.ts +0 -5
- package/vendor/compiler/dist/resolve-host-types.js +0 -8
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.d.ts +1 -1
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.js +1 -1
- package/vendor/node-sdk/dist/node-factory/define-llm-node/recovery-notice.d.ts +8 -0
- package/vendor/node-sdk/dist/node-factory/define-llm-node/recovery-notice.js +14 -0
- package/vendor/runtime/dist/runtime-editor.d.ts +5 -1
- package/vendor/runtime/dist/runtime-editor.js +19 -0
- package/vendor/runtime/dist/runtime-runner.js +17 -17
- package/vendor/runtime/dist/runtime.d.ts +2 -1
- package/vendor/runtime/dist/runtime.js +1 -0
- package/vendor/runtime/dist/testing/workflows/workflow-events.d.ts +8 -6
- package/vendor/runtime/dist/testing/workflows/workflow-events.js +19 -31
- package/vendor/runtime/dist/types.d.ts +33 -38
- package/vendor/runtime/dist/types.js +2 -1
- package/vendor/server/dist/bridge/attach-langflower-bridge.js +3 -2
- package/vendor/server/dist/bridge/bridge-event-log.js +17 -102
- package/vendor/server/dist/bridge/build-execution-context.d.ts +5 -2
- package/vendor/server/dist/bridge/build-execution-context.js +45 -5
- package/vendor/server/dist/bridge/forward-runner-event.d.ts +1 -1
- package/vendor/server/dist/bridge/forward-runner-event.js +7 -12
- package/vendor/server/dist/bridge/get-live-wired-tools.d.ts +12 -0
- package/vendor/server/dist/bridge/get-live-wired-tools.js +80 -0
- package/vendor/server/dist/bridge/langflower-tools-rpc.d.ts +7 -0
- package/vendor/server/dist/bridge/langflower-tools-rpc.js +29 -0
- package/vendor/server/dist/bridge/settings-draft-controller.js +26 -3
- package/vendor/server/dist/bridge/wire-custom-palette-handlers.d.ts +2 -1
- package/vendor/server/dist/bridge/wire-custom-palette-handlers.js +3 -5
- package/vendor/server/dist/bridge/wire-runner-handlers.js +15 -8
- package/vendor/server/dist/checkpoint/run-checkpoint-session.d.ts +1 -1
- package/vendor/server/dist/checkpoint/run-checkpoint-session.js +10 -9
- package/vendor/server/dist/palette/compile-and-hot-swap-custom-nodes.d.ts +10 -0
- package/vendor/server/dist/palette/compile-and-hot-swap-custom-nodes.js +23 -0
- package/vendor/server/dist/workflow/apply-editor-mutation.d.ts +5 -0
- package/vendor/server/dist/workflow/apply-editor-mutation.js +32 -0
- package/vendor/server/skeleton/instructions.md +9 -5
- package/vendor/server/skeleton/nodes/my-nodes/README.md +4 -3
- package/vendor/server/skeleton/schemas/langflower-config.schema.json +11 -1
- package/vendor/server/skeleton/skills/langflower-helper/SKILL.md +49 -19
- package/vendor/server/skeleton/skills/langflower-helper/architecture.md +22 -9
- package/vendor/server/skeleton/skills/langflower-helper/layout.md +15 -15
- package/vendor/server/skeleton/skills/langflower-node-writer/SKILL.md +19 -9
- package/vendor/server/skeleton/skills/langflower-workflow-writer/SKILL.md +14 -10
- package/vendor/server/skeleton/workflows/starter.json +30 -3
- package/vendor/shared/dist/execution/derive-run-settle-outcome.d.ts +1 -1
- package/vendor/shared/dist/execution/derive-run-settle-outcome.js +4 -3
- package/vendor/shared/dist/execution/derive-run-settle-outcome.test.js +9 -9
- package/vendor/shared/dist/langflower-bus-config.d.ts +6 -14
- package/vendor/shared/dist/langflower-bus-config.js +4 -12
- package/vendor/shared/dist/langflower-config/resolve-wired-tool-options.js +6 -0
- package/vendor/shared/dist/langflower-config/resolve-wired-tool-options.test.js +30 -0
- package/vendor/shared/dist/langflower-ws-waits.d.ts +7 -14
- package/vendor/shared/dist/langflower-ws-waits.js +14 -14
- package/vendor/websocket-bridge/dist/bridge-codec.d.ts +1 -0
- package/vendor/websocket-bridge/dist/bridge-codec.js +11 -11
- package/vendor/websocket-bridge/dist/bridge-frame.js +1 -1
- package/vendor/websocket-bridge/dist/bridge-subjects.d.ts +1 -1
- package/vendor/websocket-bridge/dist/bridge-subjects.js +2 -2
- package/vendor/websocket-bridge/dist/bridge-types.d.ts +13 -1
- package/vendor/websocket-bridge/dist/create-client.js +3 -3
- package/vendor/websocket-bridge/dist/create-server.js +12 -6
- package/vendor/websocket-bridge/package.json +8 -0
- package/ui-dist/main-SMO3L7U5.js +0 -311
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { isContextLengthExceededError } from './context-length-error.js';
|
|
2
|
+
const SUMMARY_PREFIX = '## Compacted conversation context\n';
|
|
3
|
+
export const SUMMARY_SYSTEM_PROMPT = [
|
|
4
|
+
'Summarize the following conversation history for continuity.',
|
|
5
|
+
'Preserve requirements, decisions, unfinished tasks, tool outcomes,',
|
|
6
|
+
'and exact constraints. Be dense and factual. Do not invent content.',
|
|
7
|
+
].join(' ');
|
|
8
|
+
const estimateApproxTokens = (value) => {
|
|
9
|
+
try {
|
|
10
|
+
return Math.ceil(JSON.stringify(value).length / 4);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return Math.ceil(String(value).length / 4);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export const estimateMessagesTokens = (messages) => estimateApproxTokens(messages);
|
|
17
|
+
export const estimateToolsTokens = (tools) => (tools.length === 0 ? 0 : estimateApproxTokens(tools));
|
|
18
|
+
export const estimateRequestTokens = (messages, tools) => estimateMessagesTokens(messages) + estimateToolsTokens(tools);
|
|
19
|
+
const effectiveInputBudget = (contextSize) => Math.floor(contextSize * 0.8);
|
|
20
|
+
const summarySlotTokens = (inputBudget) => Math.min(4096, Math.max(256, Math.floor(inputBudget * 0.15)));
|
|
21
|
+
const truncateToApproxTokens = (text, maxTokens) => {
|
|
22
|
+
const maxChars = Math.max(0, maxTokens * 4);
|
|
23
|
+
if (text.length <= maxChars) {
|
|
24
|
+
return text;
|
|
25
|
+
}
|
|
26
|
+
return text.slice(text.length - maxChars);
|
|
27
|
+
};
|
|
28
|
+
const splitLeadingSystem = (messages) => {
|
|
29
|
+
let end = 0;
|
|
30
|
+
while (end < messages.length && messages[end]?.role === 'system') {
|
|
31
|
+
end += 1;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
system: messages.slice(0, end),
|
|
35
|
+
rest: messages.slice(end),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const findLastUserIndex = (messages) => {
|
|
39
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
40
|
+
if (messages[i]?.role === 'user') {
|
|
41
|
+
return i;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return -1;
|
|
45
|
+
};
|
|
46
|
+
const toBlocks = (messages) => {
|
|
47
|
+
const blocks = [];
|
|
48
|
+
const lastUserIndex = findLastUserIndex(messages);
|
|
49
|
+
let i = 0;
|
|
50
|
+
while (i < messages.length) {
|
|
51
|
+
const message = messages[i];
|
|
52
|
+
if (message === undefined) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (message.role === 'tool') {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
message: 'Cannot compact history: orphan tool message without a preceding assistant tool_calls block',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (message.role === 'assistant' &&
|
|
62
|
+
message.tool_calls !== undefined &&
|
|
63
|
+
message.tool_calls.length > 0) {
|
|
64
|
+
const callIds = new Set(message.tool_calls.map((call) => call.id));
|
|
65
|
+
const group = [message];
|
|
66
|
+
let j = i + 1;
|
|
67
|
+
while (j < messages.length) {
|
|
68
|
+
const next = messages[j];
|
|
69
|
+
if (next === undefined || next.role !== 'tool') {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
if (!callIds.has(next.tool_call_id)) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
message: `Cannot compact history: tool message id ${next.tool_call_id} is not part of the preceding assistant tool_calls`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
group.push(next);
|
|
79
|
+
callIds.delete(next.tool_call_id);
|
|
80
|
+
j += 1;
|
|
81
|
+
}
|
|
82
|
+
if (callIds.size > 0) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
message: 'Cannot compact history: incomplete assistant tool_calls / tool result block',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const isNewest = j === messages.length;
|
|
89
|
+
blocks.push({
|
|
90
|
+
messages: group,
|
|
91
|
+
isProtected: isNewest,
|
|
92
|
+
});
|
|
93
|
+
i = j;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
blocks.push({
|
|
97
|
+
messages: [message],
|
|
98
|
+
isProtected: i === lastUserIndex || i === messages.length - 1,
|
|
99
|
+
});
|
|
100
|
+
i += 1;
|
|
101
|
+
}
|
|
102
|
+
if (blocks.length > 0) {
|
|
103
|
+
const last = blocks[blocks.length - 1];
|
|
104
|
+
if (last !== undefined && !last.isProtected) {
|
|
105
|
+
blocks[blocks.length - 1] = { ...last, isProtected: true };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return { ok: true, blocks };
|
|
109
|
+
};
|
|
110
|
+
const flattenBlocks = (blocks) => blocks.flatMap((block) => block.messages);
|
|
111
|
+
const serializeMessagesForSummary = (messages) => messages
|
|
112
|
+
.map((message) => {
|
|
113
|
+
if (message.role === 'tool') {
|
|
114
|
+
return `[tool ${message.tool_call_id}]\n${message.content}`;
|
|
115
|
+
}
|
|
116
|
+
if (message.role === 'assistant' &&
|
|
117
|
+
message.tool_calls !== undefined &&
|
|
118
|
+
message.tool_calls.length > 0) {
|
|
119
|
+
const calls = message.tool_calls
|
|
120
|
+
.map((call) => `${call.name}(${call.arguments}) id=${call.id}`)
|
|
121
|
+
.join('; ');
|
|
122
|
+
return `[assistant tool_calls]\n${message.content}\n${calls}`;
|
|
123
|
+
}
|
|
124
|
+
return `[${message.role}]\n${message.content}`;
|
|
125
|
+
})
|
|
126
|
+
.join('\n\n');
|
|
127
|
+
const collectDoneText = async (stream) => {
|
|
128
|
+
let text = '';
|
|
129
|
+
for await (const chunk of stream) {
|
|
130
|
+
if (chunk.kind === 'draft' && typeof chunk.text === 'string') {
|
|
131
|
+
text += chunk.text;
|
|
132
|
+
}
|
|
133
|
+
else if (chunk.kind === 'done' && typeof chunk.text === 'string') {
|
|
134
|
+
text = chunk.text.length > 0 ? chunk.text : text;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return text.trim();
|
|
138
|
+
};
|
|
139
|
+
const summarizeRange = async (args) => {
|
|
140
|
+
let sourceBudget = args.sourceBudgetTokens;
|
|
141
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
142
|
+
if (args.signal !== undefined && args.signal.aborted) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
message: 'Compaction aborted',
|
|
146
|
+
aborted: true,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const serialized = truncateToApproxTokens(serializeMessagesForSummary(args.source), sourceBudget);
|
|
150
|
+
try {
|
|
151
|
+
const stream = await args.factory({
|
|
152
|
+
providerId: args.providerId,
|
|
153
|
+
model: args.model,
|
|
154
|
+
messages: [
|
|
155
|
+
{ role: 'system', content: SUMMARY_SYSTEM_PROMPT },
|
|
156
|
+
{ role: 'user', content: serialized },
|
|
157
|
+
],
|
|
158
|
+
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
159
|
+
});
|
|
160
|
+
const content = await collectDoneText(stream);
|
|
161
|
+
if (content.length === 0) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
message: 'Compaction summary returned empty text',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return { ok: true, content };
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (args.signal !== undefined && args.signal.aborted) {
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
message: 'Compaction aborted',
|
|
174
|
+
aborted: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (isContextLengthExceededError(error)) {
|
|
178
|
+
sourceBudget = Math.max(256, Math.floor(sourceBudget / 2));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
182
|
+
return {
|
|
183
|
+
ok: false,
|
|
184
|
+
message: `Compaction summary failed: ${message}`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
ok: false,
|
|
190
|
+
message: 'Compaction summary failed after repeated context-length errors',
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
const findRemovableRange = (blocks) => {
|
|
194
|
+
let start = -1;
|
|
195
|
+
for (let i = 0; i < blocks.length; i += 1) {
|
|
196
|
+
if (!blocks[i].isProtected) {
|
|
197
|
+
start = i;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (start < 0) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
let end = start;
|
|
205
|
+
while (end + 1 < blocks.length && !blocks[end + 1].isProtected) {
|
|
206
|
+
end += 1;
|
|
207
|
+
}
|
|
208
|
+
return { start, end };
|
|
209
|
+
};
|
|
210
|
+
const buildSummaryMessage = (content, slotTokens) => ({
|
|
211
|
+
role: 'user',
|
|
212
|
+
content: `${SUMMARY_PREFIX}${truncateToApproxTokens(content, slotTokens)}`,
|
|
213
|
+
});
|
|
214
|
+
/**
|
|
215
|
+
* LLM-summary compaction: replace contiguous unprotected message blocks with
|
|
216
|
+
* a summary user message while preserving leading system + last user + newest
|
|
217
|
+
* block and full assistant/tool groups.
|
|
218
|
+
*/
|
|
219
|
+
export const compactMessagesWithSummary = async (args) => {
|
|
220
|
+
const beforeTokens = estimateRequestTokens(args.messages, args.tools);
|
|
221
|
+
if (beforeTokens <= args.targetTokens) {
|
|
222
|
+
return {
|
|
223
|
+
ok: true,
|
|
224
|
+
messages: args.messages,
|
|
225
|
+
beforeTokens,
|
|
226
|
+
afterTokens: beforeTokens,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const { system, rest } = splitLeadingSystem(args.messages);
|
|
230
|
+
const blocked = toBlocks(rest);
|
|
231
|
+
if (!blocked.ok) {
|
|
232
|
+
return blocked;
|
|
233
|
+
}
|
|
234
|
+
let blocks = [...blocked.blocks];
|
|
235
|
+
const toolsTokens = estimateToolsTokens(args.tools);
|
|
236
|
+
const slot = summarySlotTokens(args.targetTokens);
|
|
237
|
+
const requiredTokens = estimateMessagesTokens([
|
|
238
|
+
...system,
|
|
239
|
+
...(findLastUserIndex(rest) >= 0
|
|
240
|
+
? [rest[findLastUserIndex(rest)]]
|
|
241
|
+
: []),
|
|
242
|
+
]) +
|
|
243
|
+
toolsTokens +
|
|
244
|
+
slot;
|
|
245
|
+
if (requiredTokens > args.targetTokens) {
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
message: `Cannot compact history: required system/user/tools already need ~${requiredTokens} approx tokens (budget ${args.targetTokens})`,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
let workingMessages = args.messages;
|
|
252
|
+
let passes = 0;
|
|
253
|
+
while (estimateRequestTokens(workingMessages, args.tools) >
|
|
254
|
+
args.targetTokens &&
|
|
255
|
+
passes < 2) {
|
|
256
|
+
if (args.signal !== undefined && args.signal.aborted) {
|
|
257
|
+
return { ok: false, message: 'Compaction aborted' };
|
|
258
|
+
}
|
|
259
|
+
const range = findRemovableRange(blocks);
|
|
260
|
+
if (range === undefined) {
|
|
261
|
+
return {
|
|
262
|
+
ok: false,
|
|
263
|
+
message: 'Cannot compact history: no removable message blocks left under the protected prefix/suffix',
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const source = flattenBlocks(blocks.slice(range.start, range.end + 1));
|
|
267
|
+
const remainingBudget = Math.max(256, args.targetTokens -
|
|
268
|
+
toolsTokens -
|
|
269
|
+
estimateMessagesTokens([
|
|
270
|
+
...system,
|
|
271
|
+
...flattenBlocks(blocks.slice(0, range.start)),
|
|
272
|
+
...flattenBlocks(blocks.slice(range.end + 1)),
|
|
273
|
+
]) -
|
|
274
|
+
slot);
|
|
275
|
+
const summary = await summarizeRange({
|
|
276
|
+
factory: args.factory,
|
|
277
|
+
providerId: args.providerId,
|
|
278
|
+
model: args.model,
|
|
279
|
+
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
280
|
+
source,
|
|
281
|
+
sourceBudgetTokens: remainingBudget,
|
|
282
|
+
});
|
|
283
|
+
if (!summary.ok) {
|
|
284
|
+
return {
|
|
285
|
+
ok: false,
|
|
286
|
+
message: summary.message,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const summaryMessage = buildSummaryMessage(summary.content, slot);
|
|
290
|
+
blocks = [
|
|
291
|
+
...blocks.slice(0, range.start),
|
|
292
|
+
{
|
|
293
|
+
messages: [summaryMessage],
|
|
294
|
+
isProtected: true,
|
|
295
|
+
},
|
|
296
|
+
...blocks.slice(range.end + 1),
|
|
297
|
+
];
|
|
298
|
+
workingMessages = [...system, ...flattenBlocks(blocks)];
|
|
299
|
+
passes += 1;
|
|
300
|
+
}
|
|
301
|
+
const afterTokens = estimateRequestTokens(workingMessages, args.tools);
|
|
302
|
+
if (afterTokens > args.targetTokens) {
|
|
303
|
+
return {
|
|
304
|
+
ok: false,
|
|
305
|
+
message: `Cannot compact history below ~${args.targetTokens} approx tokens (still ~${afterTokens})`,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
ok: true,
|
|
310
|
+
messages: workingMessages,
|
|
311
|
+
beforeTokens,
|
|
312
|
+
afterTokens,
|
|
313
|
+
};
|
|
314
|
+
};
|
|
315
|
+
export const resolveProactiveTargetTokens = (config) => {
|
|
316
|
+
if (config.contextSize <= 0) {
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
return effectiveInputBudget(config.contextSize);
|
|
320
|
+
};
|
|
321
|
+
export const resolveForceTargetTokens = (config, currentInputEstimate) => {
|
|
322
|
+
if (config.contextSize > 0) {
|
|
323
|
+
return Math.min(effectiveInputBudget(config.contextSize), Math.max(256, Math.floor(currentInputEstimate * 0.5)));
|
|
324
|
+
}
|
|
325
|
+
return Math.max(256, Math.floor(currentInputEstimate * 0.5));
|
|
326
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type LlmCompactionConfig = {
|
|
2
|
+
/**
|
|
3
|
+
* Approx input token budget. `0` disables proactive compaction.
|
|
4
|
+
* Invalid values coerce to the Inspector default `200000`.
|
|
5
|
+
*/
|
|
6
|
+
readonly contextSize: number;
|
|
7
|
+
/** Retry once after a typed context-length error by force-compacting. */
|
|
8
|
+
readonly compactOnError: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare const DEFAULT_CONTEXT_SIZE = 200000;
|
|
11
|
+
export declare const DISABLED_COMPACTION_CONFIG: LlmCompactionConfig;
|
|
12
|
+
export declare const normalizeContextSize: (value: unknown) => number;
|
|
13
|
+
export declare const normalizeCompactOnError: (value: unknown) => boolean;
|
|
14
|
+
export declare const normalizeCompactionConfig: (params: Readonly<Record<string, unknown>>) => LlmCompactionConfig;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const DEFAULT_CONTEXT_SIZE = 200000;
|
|
2
|
+
const MAX_CONTEXT_SIZE = 1_000_000;
|
|
3
|
+
export const DISABLED_COMPACTION_CONFIG = {
|
|
4
|
+
contextSize: 0,
|
|
5
|
+
compactOnError: false,
|
|
6
|
+
};
|
|
7
|
+
export const normalizeContextSize = (value) => {
|
|
8
|
+
const n = typeof value === 'number' ? value : Number(value);
|
|
9
|
+
if (!Number.isFinite(n)) {
|
|
10
|
+
return DEFAULT_CONTEXT_SIZE;
|
|
11
|
+
}
|
|
12
|
+
if (n <= 0) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
return Math.min(MAX_CONTEXT_SIZE, Math.floor(n));
|
|
16
|
+
};
|
|
17
|
+
export const normalizeCompactOnError = (value) => value === true;
|
|
18
|
+
export const normalizeCompactionConfig = (params) => ({
|
|
19
|
+
contextSize: normalizeContextSize(params['contextSize']),
|
|
20
|
+
compactOnError: normalizeCompactOnError(params['compactOnError']),
|
|
21
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ChatCompletionMessage, ChatCompletionStreamChunk, ChatCompletionToolDefinition, CreateChatCompletionStream } from '../chat-completion-stream.js';
|
|
2
|
+
import type { LlmCompactionConfig } from './normalize-compaction-params.js';
|
|
3
|
+
export type PreparedChatCompletion = {
|
|
4
|
+
readonly ok: true;
|
|
5
|
+
readonly messages: readonly ChatCompletionMessage[];
|
|
6
|
+
readonly stream: AsyncIterable<ChatCompletionStreamChunk>;
|
|
7
|
+
readonly compaction?: {
|
|
8
|
+
readonly reason: 'proactive' | 'context-error';
|
|
9
|
+
readonly beforeTokens: number;
|
|
10
|
+
readonly afterTokens: number;
|
|
11
|
+
};
|
|
12
|
+
} | {
|
|
13
|
+
readonly ok: false;
|
|
14
|
+
readonly error: unknown;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Proactive + compact-on-error gate before consuming a chat stream.
|
|
18
|
+
* Context retries happen only at create-stream time (pre reasoning/draft).
|
|
19
|
+
*/
|
|
20
|
+
export declare const prepareChatCompletion: (args: {
|
|
21
|
+
readonly factory: CreateChatCompletionStream;
|
|
22
|
+
readonly providerId: string;
|
|
23
|
+
readonly model: string;
|
|
24
|
+
readonly messages: readonly ChatCompletionMessage[];
|
|
25
|
+
readonly tools?: readonly ChatCompletionToolDefinition[];
|
|
26
|
+
readonly signal?: AbortSignal;
|
|
27
|
+
readonly compaction?: LlmCompactionConfig;
|
|
28
|
+
readonly frequency_penalty?: number;
|
|
29
|
+
readonly presence_penalty?: number;
|
|
30
|
+
}) => Promise<PreparedChatCompletion>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { classifyContextLengthError, isContextLengthExceededError, } from './context-length-error.js';
|
|
2
|
+
import { compactMessagesWithSummary, estimateRequestTokens, resolveForceTargetTokens, resolveProactiveTargetTokens, } from './llm-context-compaction.js';
|
|
3
|
+
import { DISABLED_COMPACTION_CONFIG } from './normalize-compaction-params.js';
|
|
4
|
+
const createMainStream = async (args) => args.factory({
|
|
5
|
+
providerId: args.providerId,
|
|
6
|
+
model: args.model,
|
|
7
|
+
messages: args.messages,
|
|
8
|
+
...(args.tools.length > 0 ? { tools: args.tools } : {}),
|
|
9
|
+
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
10
|
+
...(args.frequency_penalty !== undefined
|
|
11
|
+
? { frequency_penalty: args.frequency_penalty }
|
|
12
|
+
: {}),
|
|
13
|
+
...(args.presence_penalty !== undefined
|
|
14
|
+
? { presence_penalty: args.presence_penalty }
|
|
15
|
+
: {}),
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Proactive + compact-on-error gate before consuming a chat stream.
|
|
19
|
+
* Context retries happen only at create-stream time (pre reasoning/draft).
|
|
20
|
+
*/
|
|
21
|
+
export const prepareChatCompletion = async (args) => {
|
|
22
|
+
const tools = args.tools ?? [];
|
|
23
|
+
const config = args.compaction ?? DISABLED_COMPACTION_CONFIG;
|
|
24
|
+
let messages = args.messages;
|
|
25
|
+
let compaction;
|
|
26
|
+
const proactiveTarget = resolveProactiveTargetTokens(config);
|
|
27
|
+
if (proactiveTarget !== undefined &&
|
|
28
|
+
estimateRequestTokens(messages, tools) > proactiveTarget) {
|
|
29
|
+
const compacted = await compactMessagesWithSummary({
|
|
30
|
+
factory: args.factory,
|
|
31
|
+
providerId: args.providerId,
|
|
32
|
+
model: args.model,
|
|
33
|
+
messages,
|
|
34
|
+
tools,
|
|
35
|
+
targetTokens: proactiveTarget,
|
|
36
|
+
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
37
|
+
});
|
|
38
|
+
if (!compacted.ok) {
|
|
39
|
+
return { ok: false, error: new Error(compacted.message) };
|
|
40
|
+
}
|
|
41
|
+
if (compacted.afterTokens < compacted.beforeTokens) {
|
|
42
|
+
messages = compacted.messages;
|
|
43
|
+
compaction = {
|
|
44
|
+
reason: 'proactive',
|
|
45
|
+
beforeTokens: compacted.beforeTokens,
|
|
46
|
+
afterTokens: compacted.afterTokens,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const stream = await createMainStream({
|
|
52
|
+
factory: args.factory,
|
|
53
|
+
providerId: args.providerId,
|
|
54
|
+
model: args.model,
|
|
55
|
+
messages,
|
|
56
|
+
tools,
|
|
57
|
+
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
58
|
+
...(args.frequency_penalty !== undefined
|
|
59
|
+
? { frequency_penalty: args.frequency_penalty }
|
|
60
|
+
: {}),
|
|
61
|
+
...(args.presence_penalty !== undefined
|
|
62
|
+
? { presence_penalty: args.presence_penalty }
|
|
63
|
+
: {}),
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
messages,
|
|
68
|
+
stream,
|
|
69
|
+
...(compaction !== undefined ? { compaction } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (args.signal !== undefined && args.signal.aborted) {
|
|
74
|
+
return { ok: false, error };
|
|
75
|
+
}
|
|
76
|
+
const contextError = classifyContextLengthError(error) ??
|
|
77
|
+
(isContextLengthExceededError(error) ? error : undefined);
|
|
78
|
+
if (contextError === undefined || !config.compactOnError) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
error: contextError ?? error,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const forceTarget = resolveForceTargetTokens(config, estimateRequestTokens(messages, tools));
|
|
85
|
+
const compacted = await compactMessagesWithSummary({
|
|
86
|
+
factory: args.factory,
|
|
87
|
+
providerId: args.providerId,
|
|
88
|
+
model: args.model,
|
|
89
|
+
messages,
|
|
90
|
+
tools,
|
|
91
|
+
targetTokens: forceTarget,
|
|
92
|
+
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
93
|
+
});
|
|
94
|
+
if (!compacted.ok) {
|
|
95
|
+
return { ok: false, error: new Error(compacted.message) };
|
|
96
|
+
}
|
|
97
|
+
messages = compacted.messages;
|
|
98
|
+
compaction = {
|
|
99
|
+
reason: 'context-error',
|
|
100
|
+
beforeTokens: compacted.beforeTokens,
|
|
101
|
+
afterTokens: compacted.afterTokens,
|
|
102
|
+
};
|
|
103
|
+
try {
|
|
104
|
+
const stream = await createMainStream({
|
|
105
|
+
factory: args.factory,
|
|
106
|
+
providerId: args.providerId,
|
|
107
|
+
model: args.model,
|
|
108
|
+
messages,
|
|
109
|
+
tools,
|
|
110
|
+
...(args.signal !== undefined ? { signal: args.signal } : {}),
|
|
111
|
+
...(args.frequency_penalty !== undefined
|
|
112
|
+
? { frequency_penalty: args.frequency_penalty }
|
|
113
|
+
: {}),
|
|
114
|
+
...(args.presence_penalty !== undefined
|
|
115
|
+
? { presence_penalty: args.presence_penalty }
|
|
116
|
+
: {}),
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
ok: true,
|
|
120
|
+
messages,
|
|
121
|
+
stream,
|
|
122
|
+
compaction,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (retryError) {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
error: classifyContextLengthError(retryError) ?? retryError,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ChatCompletionToolCall, ChatCompletionToolDefinition } from '../chat-completion-stream.js';
|
|
2
|
+
/**
|
|
3
|
+
* Path-choice control tools (accept / feedback). Must not be merged into shared
|
|
4
|
+
* inventory or harness allowlists. Import only from `ai/nodes/review/` and
|
|
5
|
+
* `ai/nodes/critique/`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const REVIEW_ACCEPT_TOOL = "accept";
|
|
8
|
+
export declare const REVIEW_FEEDBACK_TOOL = "feedback";
|
|
9
|
+
export declare const REVIEW_TOOL_REMINDER = "You must finish by calling exactly one control tool: accept or feedback. Free-form text answers are not valid. Call accept to approve the artifact, or feedback with notes to send it back for revision.";
|
|
10
|
+
/** Chat tool definitions injected only into path-choice completion calls. */
|
|
11
|
+
export declare const REVIEW_CHAT_TOOLS: readonly ChatCompletionToolDefinition[];
|
|
12
|
+
export declare const notesFromControlToolArgs: (args: Readonly<Record<string, unknown>>) => string;
|
|
13
|
+
export declare const findControlToolCall: (toolCalls: readonly ChatCompletionToolCall[]) => {
|
|
14
|
+
readonly kind: "accept";
|
|
15
|
+
readonly call: ChatCompletionToolCall;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: "feedback";
|
|
18
|
+
readonly call: ChatCompletionToolCall;
|
|
19
|
+
} | undefined;
|
|
20
|
+
export declare const isReviewControlToolName: (name: string) => boolean;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path-choice control tools (accept / feedback). Must not be merged into shared
|
|
3
|
+
* inventory or harness allowlists. Import only from `ai/nodes/review/` and
|
|
4
|
+
* `ai/nodes/critique/`.
|
|
5
|
+
*/
|
|
6
|
+
export const REVIEW_ACCEPT_TOOL = 'accept';
|
|
7
|
+
export const REVIEW_FEEDBACK_TOOL = 'feedback';
|
|
8
|
+
export const REVIEW_TOOL_REMINDER = 'You must finish by calling exactly one control tool: accept or feedback. Free-form text answers are not valid. Call accept to approve the artifact, or feedback with notes to send it back for revision.';
|
|
9
|
+
/** Chat tool definitions injected only into path-choice completion calls. */
|
|
10
|
+
export const REVIEW_CHAT_TOOLS = [
|
|
11
|
+
{
|
|
12
|
+
type: 'function',
|
|
13
|
+
function: {
|
|
14
|
+
name: REVIEW_ACCEPT_TOOL,
|
|
15
|
+
description: 'REQUIRED path when the artifact meets the task criteria. Call this tool to accept — never write a free-form approval. Optional notes may summarize why it passes. Exactly one of accept or feedback must be called to finish.',
|
|
16
|
+
parameters: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
notes: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Optional short acceptance notes',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'function',
|
|
29
|
+
function: {
|
|
30
|
+
name: REVIEW_FEEDBACK_TOOL,
|
|
31
|
+
description: 'REQUIRED path when the artifact fails the task criteria. Call this tool with concrete revision notes — never write a free-form reject essay. Exactly one of accept or feedback must be called to finish.',
|
|
32
|
+
parameters: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
notes: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'Required revision notes for the upstream agent',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['notes'],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
export const notesFromControlToolArgs = (args) => {
|
|
46
|
+
const notes = args.notes;
|
|
47
|
+
if (typeof notes === 'string') {
|
|
48
|
+
return notes;
|
|
49
|
+
}
|
|
50
|
+
const feedback = args.feedback;
|
|
51
|
+
if (typeof feedback === 'string') {
|
|
52
|
+
return feedback;
|
|
53
|
+
}
|
|
54
|
+
if (typeof args.__raw === 'string') {
|
|
55
|
+
return args.__raw;
|
|
56
|
+
}
|
|
57
|
+
return '';
|
|
58
|
+
};
|
|
59
|
+
export const findControlToolCall = (toolCalls) => {
|
|
60
|
+
for (const call of toolCalls) {
|
|
61
|
+
if (call.name === REVIEW_ACCEPT_TOOL) {
|
|
62
|
+
return { kind: 'accept', call };
|
|
63
|
+
}
|
|
64
|
+
if (call.name === REVIEW_FEEDBACK_TOOL) {
|
|
65
|
+
return { kind: 'feedback', call };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
};
|
|
70
|
+
export const isReviewControlToolName = (name) => name === REVIEW_ACCEPT_TOOL || name === REVIEW_FEEDBACK_TOOL;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ToolHandle } from '@langflower/node-sdk';
|
|
2
|
+
import type { SteerControlPayload } from '@langflower/node-sdk/llm';
|
|
3
|
+
import type { Harness } from '@langflower/tools/create-project-harness';
|
|
4
|
+
import type { ToolHandlerContext } from '@langflower/tools/domain-tool-configs';
|
|
5
|
+
import type { Observable } from 'rxjs';
|
|
6
|
+
import type { ChatCompletionMessage, CreateChatCompletionStream } from '../chat-completion-stream.js';
|
|
7
|
+
import type { SubAgentRegistration } from '../sub-agent-protocol.js';
|
|
8
|
+
import type { LlmCompactionConfig } from '../openai/normalize-compaction-params.js';
|
|
9
|
+
import { type LlmRecoveryPolicy } from '../llm-loop/llm-loop-types.js';
|
|
10
|
+
import { type SharedLlmLoopChunk } from '../llm-loop/run-llm-loop.js';
|
|
11
|
+
export type ReviewLoopChunk = SharedLlmLoopChunk | {
|
|
12
|
+
readonly kind: 'reminder';
|
|
13
|
+
readonly text: string;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: 'accept';
|
|
16
|
+
readonly notes: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: 'feedback';
|
|
19
|
+
readonly notes: string;
|
|
20
|
+
};
|
|
21
|
+
export declare const runPathChoiceToolLoop: (args: {
|
|
22
|
+
readonly factory: CreateChatCompletionStream;
|
|
23
|
+
readonly providerId: string;
|
|
24
|
+
readonly model: string;
|
|
25
|
+
readonly messages: readonly ChatCompletionMessage[];
|
|
26
|
+
readonly maxIterations: number;
|
|
27
|
+
readonly tools?: readonly ToolHandle[];
|
|
28
|
+
readonly harness?: Harness;
|
|
29
|
+
readonly toolCtx?: ToolHandlerContext;
|
|
30
|
+
readonly subagentRegistrations?: readonly SubAgentRegistration[];
|
|
31
|
+
readonly compaction?: LlmCompactionConfig;
|
|
32
|
+
readonly recovery?: LlmRecoveryPolicy;
|
|
33
|
+
readonly steerControl$?: Observable<SteerControlPayload>;
|
|
34
|
+
readonly waitForSubagentResult?: (callId: string, signal: AbortSignal) => Promise<string>;
|
|
35
|
+
}) => Observable<ReviewLoopChunk>;
|