@yukino.js/yukino 0.0.1
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 +21 -0
- package/README.md +320 -0
- package/dist/acp-IVY5HZR4.js +14 -0
- package/dist/agent-QANDY7FO.js +4 -0
- package/dist/anthropic-6YLT4JZR.js +4 -0
- package/dist/bwrap-MLSZLWHD.js +4 -0
- package/dist/chunk-2LHY3FND.js +67 -0
- package/dist/chunk-3KKA6537.js +4 -0
- package/dist/chunk-3QL446GD.js +4 -0
- package/dist/chunk-4GE22WHI.js +4 -0
- package/dist/chunk-5VOCXSSX.js +5 -0
- package/dist/chunk-77W2UPS2.js +4 -0
- package/dist/chunk-7MKA3SHV.js +4 -0
- package/dist/chunk-AGSYLE4E.js +16 -0
- package/dist/chunk-CU3IISCC.js +4 -0
- package/dist/chunk-GYBJR2RB.js +65 -0
- package/dist/chunk-HLE7243Q.js +4 -0
- package/dist/chunk-HMZQYFYG.js +4 -0
- package/dist/chunk-JFKC4VCA.js +4 -0
- package/dist/chunk-K4BYVVW7.js +5 -0
- package/dist/chunk-KCMBAHWU.js +4 -0
- package/dist/chunk-L2BWSPCF.js +10 -0
- package/dist/chunk-MDPSQ6L7.js +44 -0
- package/dist/chunk-OFC2GD5K.js +4 -0
- package/dist/chunk-RAS3ORX3.js +164 -0
- package/dist/chunk-RCXKUD3Y.js +4 -0
- package/dist/chunk-SAJCBGHL.js +4 -0
- package/dist/chunk-SOO5OXX2.js +237 -0
- package/dist/chunk-T4O4GQVL.js +607 -0
- package/dist/chunk-UPNNYP7M.js +60 -0
- package/dist/chunk-VCIRD267.js +4 -0
- package/dist/chunk-VU3JWAJR.js +4 -0
- package/dist/chunk-VWUGGYUP.js +6 -0
- package/dist/chunk-XO7ITAWR.js +4 -0
- package/dist/devtools-YWVUN4HX.js +7 -0
- package/dist/esm-57CJEGGK.js +4 -0
- package/dist/esm-6L5BJ5GT.js +4 -0
- package/dist/esm-DIYJ5HX6.js +4 -0
- package/dist/esm-HW2JMFYC.js +4 -0
- package/dist/esm-RPR2GBJ2.js +4 -0
- package/dist/esm-YKAO4ES6.js +4 -0
- package/dist/esm-ZTIYTMTS.js +229 -0
- package/dist/getMachineId-bsd-E27XJQLX.js +4 -0
- package/dist/getMachineId-darwin-35UPAIXP.js +5 -0
- package/dist/getMachineId-linux-MKXIYT6A.js +4 -0
- package/dist/getMachineId-unsupported-ZXVGGFX2.js +4 -0
- package/dist/getMachineId-win-RNVIYJG5.js +4 -0
- package/dist/lib/agent-6FZX4P2Y.js +16 -0
- package/dist/lib/anthropic-IP23YZ3P.js +21 -0
- package/dist/lib/bwrap-2OL7VPSO.js +7 -0
- package/dist/lib/chunk-2VF3XTGH.js +3382 -0
- package/dist/lib/chunk-5DVQ64YL.js +619 -0
- package/dist/lib/chunk-5TGXH7BX.js +624 -0
- package/dist/lib/chunk-6OMQD7MK.js +591 -0
- package/dist/lib/chunk-AZRC67RW.js +512 -0
- package/dist/lib/chunk-F2YJG72O.js +150 -0
- package/dist/lib/chunk-FFM2LB4C.js +132 -0
- package/dist/lib/chunk-FLKB4BK7.js +50 -0
- package/dist/lib/chunk-I34MAQRE.js +255 -0
- package/dist/lib/chunk-KA6POJKI.js +889 -0
- package/dist/lib/chunk-MLKGABMK.js +9 -0
- package/dist/lib/chunk-O7ELJUEK.js +262 -0
- package/dist/lib/chunk-QYBQT2DM.js +655 -0
- package/dist/lib/chunk-YIWNQ7OA.js +47 -0
- package/dist/lib/index.d.ts +8581 -0
- package/dist/lib/index.js +15905 -0
- package/dist/lib/openai-EMJ6U4FW.js +15 -0
- package/dist/lib/permissions-KZ5ACZAX.js +19 -0
- package/dist/lib/providers-2CGZPBFI.js +9 -0
- package/dist/lib/sandbox-runtime-CCQ3X3SM.js +9 -0
- package/dist/lib/seatbelt-WO7YWKLR.js +7 -0
- package/dist/lib/tool-filter-CKHZUUBQ.js +21 -0
- package/dist/main.js +332 -0
- package/dist/node-634LOHDX.js +15 -0
- package/dist/openai-YXYO5OW6.js +34 -0
- package/dist/permissions-VCDMNX3L.js +4 -0
- package/dist/providers-HNMBGGW6.js +4 -0
- package/dist/sandbox-runtime-ELKAYRIL.js +4 -0
- package/dist/seatbelt-JOCANLYS.js +4 -0
- package/dist/server-ZHQILOBP.js +4 -0
- package/dist/src-43CPCEK2.js +12 -0
- package/dist/src-JBRNH4FA.js +4 -0
- package/dist/src-R4BSSIJJ.js +4 -0
- package/dist/src-X6JWWHHT.js +4 -0
- package/dist/tool-filter-BQZDV3TF.js +4 -0
- package/dist/turndown.es-NQ3KDXM7.js +83 -0
- package/package.json +146 -0
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeToolResultContentBlock
|
|
3
|
+
} from "./chunk-F2YJG72O.js";
|
|
4
|
+
import {
|
|
5
|
+
AuthenticationError,
|
|
6
|
+
ContextTooLongError,
|
|
7
|
+
LLMError,
|
|
8
|
+
MIN_THINKING_ANSWER_TOKENS,
|
|
9
|
+
NetworkError,
|
|
10
|
+
RateLimitError,
|
|
11
|
+
clampThinkingLevel,
|
|
12
|
+
ensureToolPairing,
|
|
13
|
+
getMaxOutputTokens,
|
|
14
|
+
getSupportedThinkingLevels,
|
|
15
|
+
getThinkingLevel,
|
|
16
|
+
resolveAPIKey,
|
|
17
|
+
thinkingBudgetForLevel,
|
|
18
|
+
toAnthropicThinkingEffort,
|
|
19
|
+
toReasoningEffort
|
|
20
|
+
} from "./chunk-QYBQT2DM.js";
|
|
21
|
+
import {
|
|
22
|
+
MCP_TOOL_PREFIX,
|
|
23
|
+
isMcpToolLike
|
|
24
|
+
} from "./chunk-I34MAQRE.js";
|
|
25
|
+
import {
|
|
26
|
+
asErrorString,
|
|
27
|
+
asRecord,
|
|
28
|
+
asString,
|
|
29
|
+
contentToText,
|
|
30
|
+
createChildLogger,
|
|
31
|
+
isRecord,
|
|
32
|
+
strArg
|
|
33
|
+
} from "./chunk-6OMQD7MK.js";
|
|
34
|
+
import {
|
|
35
|
+
__export
|
|
36
|
+
} from "./chunk-MLKGABMK.js";
|
|
37
|
+
|
|
38
|
+
// src/llm/anthropic.ts
|
|
39
|
+
var anthropic_exports = {};
|
|
40
|
+
__export(anthropic_exports, {
|
|
41
|
+
AnthropicClient: () => AnthropicClient,
|
|
42
|
+
buildAnthropicMessages: () => buildAnthropicMessages,
|
|
43
|
+
fetchModelContextWindow: () => fetchModelContextWindow,
|
|
44
|
+
markLastUserTailForCache: () => markLastUserTailForCache,
|
|
45
|
+
markToolsForCache: () => markToolsForCache,
|
|
46
|
+
needsToolSearchBeta: () => needsToolSearchBeta
|
|
47
|
+
});
|
|
48
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
49
|
+
import { safeParseAsync, z } from "zod";
|
|
50
|
+
|
|
51
|
+
// src/mcp/strategy.ts
|
|
52
|
+
var strategy_exports = {};
|
|
53
|
+
__export(strategy_exports, {
|
|
54
|
+
CHARS_PER_TOKEN: () => CHARS_PER_TOKEN,
|
|
55
|
+
DEFAULT_EAGER_THRESHOLD_PERCENT: () => DEFAULT_EAGER_THRESHOLD_PERCENT,
|
|
56
|
+
NATIVE_TOOL_USE_BETA: () => NATIVE_TOOL_USE_BETA,
|
|
57
|
+
applyMode: () => applyMode,
|
|
58
|
+
decideAndApply: () => decideAndApply,
|
|
59
|
+
decideMode: () => decideMode,
|
|
60
|
+
estimateSchemaTokens: () => estimateSchemaTokens,
|
|
61
|
+
isOfficialAnthropicEndpoint: () => isOfficialAnthropicEndpoint,
|
|
62
|
+
measureSchemaChars: () => measureSchemaChars
|
|
63
|
+
});
|
|
64
|
+
var DEFAULT_EAGER_THRESHOLD_PERCENT = 10;
|
|
65
|
+
var CHARS_PER_TOKEN = 2.5;
|
|
66
|
+
var NATIVE_TOOL_USE_BETA = "advanced-tool-use-2025-11-20";
|
|
67
|
+
var OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["api.anthropic.com"]);
|
|
68
|
+
var ENV_OVERRIDE = "YUKINO_MCP_LOADING";
|
|
69
|
+
function isOfficialAnthropicEndpoint(baseUrl) {
|
|
70
|
+
if (!baseUrl) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
return OFFICIAL_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function estimateSchemaTokens(schemaChars) {
|
|
80
|
+
return Math.floor(schemaChars / CHARS_PER_TOKEN);
|
|
81
|
+
}
|
|
82
|
+
function decideMode(baseUrl, contextWindow, mcpSchemaChars, thresholdPercent = DEFAULT_EAGER_THRESHOLD_PERCENT) {
|
|
83
|
+
const override = (process.env[ENV_OVERRIDE] ?? "").trim().toLowerCase();
|
|
84
|
+
if (override === "eager" || override === "native" || override === "dispatch") {
|
|
85
|
+
return override;
|
|
86
|
+
}
|
|
87
|
+
if (mcpSchemaChars <= 0) {
|
|
88
|
+
return "eager";
|
|
89
|
+
}
|
|
90
|
+
const budget = contextWindow * thresholdPercent / 100;
|
|
91
|
+
if (estimateSchemaTokens(mcpSchemaChars) < budget) {
|
|
92
|
+
return "eager";
|
|
93
|
+
}
|
|
94
|
+
return isOfficialAnthropicEndpoint(baseUrl) ? "native" : "dispatch";
|
|
95
|
+
}
|
|
96
|
+
function measureSchemaChars(registry) {
|
|
97
|
+
let total = 0;
|
|
98
|
+
for (const tool of registry.listTools()) {
|
|
99
|
+
if (!tool.name.startsWith(MCP_TOOL_PREFIX)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
total += JSON.stringify(tool.schema()).length;
|
|
104
|
+
} catch {
|
|
105
|
+
total += tool.name.length + (tool.description?.length ?? 0);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return total;
|
|
109
|
+
}
|
|
110
|
+
function applyMode(registry, mode) {
|
|
111
|
+
registry.mcpLoadingMode = mode;
|
|
112
|
+
const eager = mode === "eager";
|
|
113
|
+
for (const tool of registry.listTools()) {
|
|
114
|
+
if (isMcpToolLike(tool) && typeof tool.setDeferLoading === "function") {
|
|
115
|
+
tool.setDeferLoading(!eager);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
registry.exposeToolSearch = !eager;
|
|
119
|
+
registry.exposeMcpCall = mode === "dispatch";
|
|
120
|
+
}
|
|
121
|
+
function decideAndApply(registry, baseUrl, contextWindow) {
|
|
122
|
+
const mode = decideMode(baseUrl, contextWindow, measureSchemaChars(registry));
|
|
123
|
+
applyMode(registry, mode);
|
|
124
|
+
return mode;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/llm/anthropic.ts
|
|
128
|
+
function markToolsForCache(tools) {
|
|
129
|
+
for (let i = tools.length - 1; i >= 0; i--) {
|
|
130
|
+
const t = tools[i];
|
|
131
|
+
if (t.defer_loading === true) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
t.cache_control = { type: "ephemeral" };
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function needsToolSearchBeta(toolSchemas) {
|
|
139
|
+
return toolSchemas.some(
|
|
140
|
+
(schema) => "defer_loading" in schema && schema.defer_loading === true
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function toAnthropicToolSchema(schema, useExplicitCustomType) {
|
|
144
|
+
if ("input_schema" in schema) {
|
|
145
|
+
const tool = schema;
|
|
146
|
+
const { cache_control: cacheControl, ...rest } = tool;
|
|
147
|
+
if (!useExplicitCustomType) {
|
|
148
|
+
delete rest.type;
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
...rest,
|
|
152
|
+
...useExplicitCustomType ? { type: "custom" } : {},
|
|
153
|
+
...tool.defer_loading !== true && cacheControl ? { cache_control: cacheControl } : {}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
throw new Error(
|
|
157
|
+
"Anthropic received a tool schema serialized for another protocol."
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
var log = createChildLogger({ module: "llm" });
|
|
161
|
+
var MODEL_FETCH_TIMEOUT_MS = 3e3;
|
|
162
|
+
var ModelContextWindowResSchema = z.object({
|
|
163
|
+
max_input_tokens: z.coerce.number()
|
|
164
|
+
});
|
|
165
|
+
async function fetchModelContextWindow(config) {
|
|
166
|
+
if (config.protocol !== "anthropic") {
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
const apiKey = resolveAPIKey(config);
|
|
170
|
+
const base = config.base_url.replace(/\/+$/, "");
|
|
171
|
+
const url = `${base}/v1/models/${encodeURIComponent(config.model)}`;
|
|
172
|
+
const controller = new AbortController();
|
|
173
|
+
const timer = setTimeout(() => {
|
|
174
|
+
controller.abort();
|
|
175
|
+
}, MODEL_FETCH_TIMEOUT_MS);
|
|
176
|
+
try {
|
|
177
|
+
const res = await fetch(url, {
|
|
178
|
+
method: "GET",
|
|
179
|
+
headers: {
|
|
180
|
+
"anthropic-version": "2023-06-01",
|
|
181
|
+
...apiKey ? { "x-api-key": apiKey } : {}
|
|
182
|
+
},
|
|
183
|
+
signal: controller.signal
|
|
184
|
+
});
|
|
185
|
+
if (!res.ok) {
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
const body = await res.json();
|
|
189
|
+
const { success, error, data } = await safeParseAsync(
|
|
190
|
+
ModelContextWindowResSchema,
|
|
191
|
+
body
|
|
192
|
+
);
|
|
193
|
+
if (!success) {
|
|
194
|
+
log.warn(
|
|
195
|
+
{ message: error.message },
|
|
196
|
+
"model context window schema validation failed"
|
|
197
|
+
);
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
const maxInputTokens = data.max_input_tokens;
|
|
201
|
+
return Math.max(maxInputTokens, 0);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
log.error({ err }, "failed to fetch model context window");
|
|
204
|
+
return 0;
|
|
205
|
+
} finally {
|
|
206
|
+
clearTimeout(timer);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function userBlocksFor(content) {
|
|
210
|
+
if (typeof content === "string") {
|
|
211
|
+
return [{ type: "text", text: content }];
|
|
212
|
+
}
|
|
213
|
+
return content.map((raw) => {
|
|
214
|
+
const block = normalizeToolResultContentBlock(raw);
|
|
215
|
+
if (!block || block.type === "tool_reference") {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`Unsupported user content block: ${strArg(raw, "type", "unknown")}`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return block;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
function buildAnthropicMessages(messages) {
|
|
224
|
+
const result = [];
|
|
225
|
+
for (const m of messages) {
|
|
226
|
+
if (m.role === "assistant") {
|
|
227
|
+
const blocks = [];
|
|
228
|
+
if (m.thinkingBlocks) {
|
|
229
|
+
for (const tb of m.thinkingBlocks) {
|
|
230
|
+
blocks.push({
|
|
231
|
+
type: "thinking",
|
|
232
|
+
thinking: tb.thinking,
|
|
233
|
+
signature: tb.signature
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const assistantText = typeof m.content === "string" ? m.content : contentToText(m.content);
|
|
238
|
+
if (assistantText) {
|
|
239
|
+
blocks.push({
|
|
240
|
+
type: "text",
|
|
241
|
+
text: assistantText
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (m.toolUses) {
|
|
245
|
+
for (const tu of m.toolUses) {
|
|
246
|
+
blocks.push({
|
|
247
|
+
type: "tool_use",
|
|
248
|
+
// tool use **request**
|
|
249
|
+
id: tu.toolUseId,
|
|
250
|
+
name: tu.toolName === "ComputerUse" ? "computer" : tu.toolName,
|
|
251
|
+
input: tu.arguments
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (blocks.length === 0) {
|
|
256
|
+
blocks.push({ type: "text", text: "" });
|
|
257
|
+
}
|
|
258
|
+
result.push({ role: "assistant", content: blocks });
|
|
259
|
+
} else if (m.toolResults && m.toolResults.length > 0) {
|
|
260
|
+
const blocks = [];
|
|
261
|
+
for (const tr of m.toolResults) {
|
|
262
|
+
blocks.push({
|
|
263
|
+
type: "tool_result",
|
|
264
|
+
// tool result
|
|
265
|
+
tool_use_id: tr.toolUseId,
|
|
266
|
+
is_error: tr.isError,
|
|
267
|
+
content: tr.contentBlocks?.length ? tr.contentBlocks : tr.content
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
if (m.content.length > 0) {
|
|
271
|
+
blocks.push(...userBlocksFor(m.content));
|
|
272
|
+
}
|
|
273
|
+
result.push({ role: "user", content: blocks });
|
|
274
|
+
} else {
|
|
275
|
+
if (result.length === 0) {
|
|
276
|
+
result.push({
|
|
277
|
+
role: "user",
|
|
278
|
+
content: userBlocksFor(m.content)
|
|
279
|
+
});
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
let canMerge = false;
|
|
283
|
+
const prev = result[result.length - 1];
|
|
284
|
+
let content = prev.content;
|
|
285
|
+
if (prev.role === "user" && (typeof content === "string" || Array.isArray(content) && content.length > 0 && // content[0].type !== "tool_result"
|
|
286
|
+
(content[0].type === "text" || content[0].type === "image"))) {
|
|
287
|
+
canMerge = true;
|
|
288
|
+
}
|
|
289
|
+
if (canMerge) {
|
|
290
|
+
if (typeof content === "string") {
|
|
291
|
+
content = prev.content = content.trim().length > 0 ? [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: content
|
|
295
|
+
}
|
|
296
|
+
] : [];
|
|
297
|
+
}
|
|
298
|
+
content.push(...userBlocksFor(m.content));
|
|
299
|
+
} else {
|
|
300
|
+
result.push({
|
|
301
|
+
role: "user",
|
|
302
|
+
content: userBlocksFor(m.content)
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
var AnthropicClient = class {
|
|
310
|
+
protocol = "anthropic";
|
|
311
|
+
client;
|
|
312
|
+
model;
|
|
313
|
+
/** Effective logical level for budget or explicitly configured adaptive mode. */
|
|
314
|
+
thinkingLevel;
|
|
315
|
+
systemPrompt;
|
|
316
|
+
maxOutputTokens;
|
|
317
|
+
config;
|
|
318
|
+
useExplicitCustomToolType;
|
|
319
|
+
constructor(config, systemPrompt) {
|
|
320
|
+
const apiKey = resolveAPIKey(config);
|
|
321
|
+
if (!apiKey) {
|
|
322
|
+
throw new AuthenticationError(
|
|
323
|
+
"Anthropic API key not found, set ANTHROPIC_API_KEY in ~/.yukino/config.yaml, or via ANTHROPIC_API_KEY env variable."
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
this.client = new Anthropic({
|
|
327
|
+
apiKey,
|
|
328
|
+
baseURL: config.base_url
|
|
329
|
+
});
|
|
330
|
+
this.useExplicitCustomToolType = isOfficialAnthropicEndpoint(
|
|
331
|
+
config.base_url
|
|
332
|
+
);
|
|
333
|
+
this.model = config.model;
|
|
334
|
+
this.config = { ...config };
|
|
335
|
+
this.thinkingLevel = getThinkingLevel(config);
|
|
336
|
+
this.systemPrompt = systemPrompt;
|
|
337
|
+
this.maxOutputTokens = getMaxOutputTokens(config);
|
|
338
|
+
}
|
|
339
|
+
setSystemPrompt(prompt) {
|
|
340
|
+
this.systemPrompt = prompt;
|
|
341
|
+
}
|
|
342
|
+
setMaxOutputTokens(maxTokens) {
|
|
343
|
+
this.config = { ...this.config, max_output_tokens: maxTokens };
|
|
344
|
+
this.maxOutputTokens = getMaxOutputTokens(this.config);
|
|
345
|
+
this.setThinkingLevel(this.thinkingLevel);
|
|
346
|
+
}
|
|
347
|
+
setThinkingLevel(level) {
|
|
348
|
+
this.thinkingLevel = clampThinkingLevel(this.config, level);
|
|
349
|
+
return this.thinkingLevel;
|
|
350
|
+
}
|
|
351
|
+
getThinkingLevel() {
|
|
352
|
+
return this.thinkingLevel;
|
|
353
|
+
}
|
|
354
|
+
getSupportedThinkingLevels() {
|
|
355
|
+
return getSupportedThinkingLevels(this.config);
|
|
356
|
+
}
|
|
357
|
+
async *stream(conversation, toolSchemas, abortSignal) {
|
|
358
|
+
const messages = buildAnthropicMessages(
|
|
359
|
+
ensureToolPairing(conversation.getMessages())
|
|
360
|
+
);
|
|
361
|
+
const sendToolSearchBeta = needsToolSearchBeta(toolSchemas);
|
|
362
|
+
const antToolSchemas = toolSchemas.map(
|
|
363
|
+
(schema) => toAnthropicToolSchema(schema, this.useExplicitCustomToolType)
|
|
364
|
+
);
|
|
365
|
+
markToolsForCache(antToolSchemas);
|
|
366
|
+
markLastUserTailForCache(messages);
|
|
367
|
+
const params = {
|
|
368
|
+
model: this.model,
|
|
369
|
+
max_tokens: this.maxOutputTokens,
|
|
370
|
+
stream: true,
|
|
371
|
+
system: [
|
|
372
|
+
{
|
|
373
|
+
type: "text",
|
|
374
|
+
text: this.systemPrompt,
|
|
375
|
+
cache_control: {
|
|
376
|
+
type: "ephemeral"
|
|
377
|
+
// Prompt cache
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
],
|
|
381
|
+
messages,
|
|
382
|
+
...antToolSchemas.length > 0 ? (
|
|
383
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
384
|
+
{ tools: antToolSchemas }
|
|
385
|
+
) : {}
|
|
386
|
+
};
|
|
387
|
+
const level = this.getThinkingLevel();
|
|
388
|
+
if (this.config.reasoning !== false) {
|
|
389
|
+
if (level === "off") {
|
|
390
|
+
params.thinking = { type: "disabled" };
|
|
391
|
+
} else if (this.config.thinking_mode === "adaptive") {
|
|
392
|
+
const effort = toAnthropicThinkingEffort(level, this.config);
|
|
393
|
+
if (effort !== null) {
|
|
394
|
+
params.thinking = { type: "adaptive" };
|
|
395
|
+
params.output_config = { effort };
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
const effort = toReasoningEffort(level, this.config);
|
|
399
|
+
if (effort !== null && effort !== "none") {
|
|
400
|
+
params.thinking = {
|
|
401
|
+
type: "enabled",
|
|
402
|
+
budget_tokens: Math.min(
|
|
403
|
+
thinkingBudgetForLevel(effort),
|
|
404
|
+
this.maxOutputTokens - MIN_THINKING_ANSWER_TOKENS
|
|
405
|
+
)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
let inputTokens = 0;
|
|
411
|
+
let outputTokens = 0;
|
|
412
|
+
let cacheReadInputTokens = 0;
|
|
413
|
+
let cacheCreationInputTokens = 0;
|
|
414
|
+
let stopReason = "end_turn";
|
|
415
|
+
let thinkingAccumulate = "";
|
|
416
|
+
let thinkingSignature = "";
|
|
417
|
+
let inThinking = false;
|
|
418
|
+
try {
|
|
419
|
+
const betas = [...sendToolSearchBeta ? [NATIVE_TOOL_USE_BETA] : []];
|
|
420
|
+
const response = this.client.messages.stream(params, {
|
|
421
|
+
...abortSignal ? { signal: abortSignal } : {},
|
|
422
|
+
...betas.length > 0 ? { headers: { "anthropic-beta": betas.join(",") } } : {}
|
|
423
|
+
});
|
|
424
|
+
let currentToolName = "";
|
|
425
|
+
let currentToolId = "";
|
|
426
|
+
let jsonAccumulate = "";
|
|
427
|
+
for await (const event of response) {
|
|
428
|
+
switch (event.type) {
|
|
429
|
+
case "content_block_start": {
|
|
430
|
+
const block = event.content_block;
|
|
431
|
+
if (block.type === "thinking") {
|
|
432
|
+
inThinking = true;
|
|
433
|
+
thinkingAccumulate = "";
|
|
434
|
+
thinkingSignature = "";
|
|
435
|
+
} else if (block.type === "tool_use") {
|
|
436
|
+
currentToolId = block.id;
|
|
437
|
+
currentToolName = block.name === "computer" ? "ComputerUse" : block.name;
|
|
438
|
+
jsonAccumulate = "";
|
|
439
|
+
yield {
|
|
440
|
+
type: "tool_call_start",
|
|
441
|
+
toolName: currentToolName,
|
|
442
|
+
toolId: currentToolId
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
// end case "content_block_start"
|
|
448
|
+
case "content_block_delta": {
|
|
449
|
+
const delta = event.delta;
|
|
450
|
+
if (delta.type === "thinking_delta") {
|
|
451
|
+
thinkingAccumulate += delta.thinking;
|
|
452
|
+
yield {
|
|
453
|
+
type: "thinking_delta",
|
|
454
|
+
text: delta.thinking
|
|
455
|
+
};
|
|
456
|
+
} else if (delta.type === "signature_delta") {
|
|
457
|
+
thinkingSignature += delta.signature;
|
|
458
|
+
} else if (delta.type === "text_delta") {
|
|
459
|
+
yield {
|
|
460
|
+
type: "text_delta",
|
|
461
|
+
text: delta.text
|
|
462
|
+
};
|
|
463
|
+
} else if (delta.type === "input_json_delta") {
|
|
464
|
+
jsonAccumulate += delta.partial_json;
|
|
465
|
+
yield {
|
|
466
|
+
type: "tool_call_delta",
|
|
467
|
+
text: delta.partial_json
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
// end case "content_block_delta"
|
|
473
|
+
case "content_block_stop": {
|
|
474
|
+
if (inThinking) {
|
|
475
|
+
yield {
|
|
476
|
+
type: "thinking_complete",
|
|
477
|
+
thinking: thinkingAccumulate,
|
|
478
|
+
signature: thinkingSignature
|
|
479
|
+
};
|
|
480
|
+
inThinking = false;
|
|
481
|
+
}
|
|
482
|
+
if (currentToolName) {
|
|
483
|
+
let args = {};
|
|
484
|
+
if (jsonAccumulate) {
|
|
485
|
+
try {
|
|
486
|
+
const parsed = JSON.parse(jsonAccumulate);
|
|
487
|
+
args = isRecord(parsed) ? asRecord(parsed) : {};
|
|
488
|
+
} catch (err) {
|
|
489
|
+
log.error({ err }, "llm operation failed");
|
|
490
|
+
args = {};
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
yield {
|
|
494
|
+
type: "tool_call_complete",
|
|
495
|
+
toolId: currentToolId,
|
|
496
|
+
toolName: currentToolName,
|
|
497
|
+
arguments: args
|
|
498
|
+
};
|
|
499
|
+
currentToolName = "";
|
|
500
|
+
currentToolId = "";
|
|
501
|
+
jsonAccumulate = "";
|
|
502
|
+
}
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
// end case "content_block_stop"
|
|
506
|
+
case "message_delta": {
|
|
507
|
+
if (event.delta.stop_reason) {
|
|
508
|
+
stopReason = event.delta.stop_reason;
|
|
509
|
+
}
|
|
510
|
+
if (event.usage.output_tokens) {
|
|
511
|
+
outputTokens = event.usage.output_tokens;
|
|
512
|
+
if (event.usage.input_tokens) {
|
|
513
|
+
inputTokens = event.usage.input_tokens;
|
|
514
|
+
}
|
|
515
|
+
if (event.usage.cache_read_input_tokens) {
|
|
516
|
+
cacheReadInputTokens = event.usage.cache_read_input_tokens;
|
|
517
|
+
}
|
|
518
|
+
if (event.usage.cache_creation_input_tokens) {
|
|
519
|
+
cacheCreationInputTokens = event.usage.cache_creation_input_tokens;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
// end case "message_delta"
|
|
525
|
+
case "message_start": {
|
|
526
|
+
inputTokens = event.message.usage.input_tokens;
|
|
527
|
+
outputTokens = event.message.usage.output_tokens;
|
|
528
|
+
cacheReadInputTokens = event.message.usage.cache_read_input_tokens ?? 0;
|
|
529
|
+
cacheCreationInputTokens = event.message.usage.cache_creation_input_tokens ?? 0;
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
yield {
|
|
535
|
+
type: "stream_end",
|
|
536
|
+
stopReason,
|
|
537
|
+
usage: {
|
|
538
|
+
inputTokens,
|
|
539
|
+
outputTokens,
|
|
540
|
+
cacheReadInputTokens,
|
|
541
|
+
cacheCreationInputTokens
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
} catch (err) {
|
|
545
|
+
log.error({ err }, "llm operation failed");
|
|
546
|
+
throw classifyAnthropicError(err);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
function markLastUserTailForCache(messages) {
|
|
551
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
552
|
+
if (messages[i].role !== "user") {
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
let content = messages[i].content;
|
|
556
|
+
if (typeof content === "string" && content.length === 0 || Array.isArray(content) && content.length === 0) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (typeof content === "string") {
|
|
560
|
+
content = messages[i].content = [
|
|
561
|
+
{
|
|
562
|
+
type: "text",
|
|
563
|
+
text: content
|
|
564
|
+
}
|
|
565
|
+
];
|
|
566
|
+
}
|
|
567
|
+
let last = content[content.length - 1];
|
|
568
|
+
for (let j = content.length - 1; j >= 0; j--) {
|
|
569
|
+
if (content[j].type !== "image") {
|
|
570
|
+
last = content[j];
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
Reflect.set(last, "cache_control", {
|
|
575
|
+
type: "ephemeral"
|
|
576
|
+
});
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function classifyAnthropicError(err) {
|
|
581
|
+
if (err instanceof Anthropic.APIError) {
|
|
582
|
+
if (err.status === 413 /* PromptTooLong */ || /prompts?\s+too\s+long/i.test(err.message)) {
|
|
583
|
+
return new ContextTooLongError(`Prompt too long: ${err.message}`);
|
|
584
|
+
}
|
|
585
|
+
if (err.status === 401 /* InvalidAPIKey */) {
|
|
586
|
+
return new AuthenticationError(`Invalid API key: ${err.message}`);
|
|
587
|
+
}
|
|
588
|
+
if (err.status === 429 /* RateLimitError */) {
|
|
589
|
+
const headers = err.headers;
|
|
590
|
+
const retryAfter = headers instanceof Headers ? headers.get("retry-after") : void 0;
|
|
591
|
+
let message = "Rate Limited";
|
|
592
|
+
if (retryAfter) {
|
|
593
|
+
const s = Number.parseInt(asString(retryAfter));
|
|
594
|
+
if (Number.isNaN(s)) {
|
|
595
|
+
message += ", please wait.";
|
|
596
|
+
} else {
|
|
597
|
+
message += `, retry after ${asString(s)}s.`;
|
|
598
|
+
}
|
|
599
|
+
} else {
|
|
600
|
+
message += ", please wait.";
|
|
601
|
+
}
|
|
602
|
+
return new RateLimitError(
|
|
603
|
+
message,
|
|
604
|
+
retryAfter ? asString(retryAfter) : void 0
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
return new LLMError(
|
|
608
|
+
`Anthropic API error (${asString(err.status)}): ${err.message}`
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
return new NetworkError(`Network error: ${asErrorString(err)}`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export {
|
|
615
|
+
decideAndApply,
|
|
616
|
+
strategy_exports,
|
|
617
|
+
markToolsForCache,
|
|
618
|
+
needsToolSearchBeta,
|
|
619
|
+
fetchModelContextWindow,
|
|
620
|
+
buildAnthropicMessages,
|
|
621
|
+
AnthropicClient,
|
|
622
|
+
markLastUserTailForCache,
|
|
623
|
+
anthropic_exports
|
|
624
|
+
};
|