@swifty.js/swifty 0.0.21 → 0.0.23
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/dist/{agent-ZCMBUWLZ.js → agent-RABW3Z3R.js} +1 -1
- package/dist/anthropic-V22T6E6E.js +4 -0
- package/dist/{checker-OJFS2MZY.js → checker-3RWAYF2U.js} +1 -1
- package/dist/{chunk-Y5WGBAGP.js → chunk-5KXBVL2A.js} +1 -1
- package/dist/{chunk-MUPVOATV.js → chunk-CV7OKXYC.js} +1 -1
- package/dist/chunk-MJR2C7S7.js +130 -0
- package/dist/{chunk-GUXBLUNR.js → chunk-NR62AA5K.js} +1 -1
- package/dist/{chunk-S6ZADC7C.js → chunk-OCQ6TYLT.js} +21 -20
- package/dist/chunk-QN27QEFS.js +4 -0
- package/dist/{chunk-LX2LK3TF.js → chunk-SIWLMWHH.js} +12 -11
- package/dist/chunk-Y43POSIW.js +389 -0
- package/dist/lib/agent-GHXYOARN.js +9 -0
- package/dist/lib/anthropic-7YEN2QTU.js +22 -0
- package/dist/lib/bwrap-QRGPUP4J.js +6 -0
- package/dist/lib/checker-6BW4222R.js +19 -0
- package/dist/lib/chunk-7IZEK7X5.js +545 -0
- package/dist/lib/chunk-BN4Q54G2.js +1096 -0
- package/dist/lib/chunk-BO76JDFZ.js +617 -0
- package/dist/lib/chunk-CYDNMEBC.js +339 -0
- package/dist/lib/chunk-EY7HE52Q.js +384 -0
- package/dist/lib/chunk-GHF2PSEW.js +8 -0
- package/dist/lib/chunk-GNI7YX6F.js +426 -0
- package/dist/lib/chunk-HK2Z6WP4.js +223 -0
- package/dist/lib/chunk-MF3YLQDS.js +1277 -0
- package/dist/lib/chunk-OO2CLOEE.js +88 -0
- package/dist/lib/chunk-ORSYNBMM.js +38 -0
- package/dist/lib/chunk-PZ42NAFA.js +242 -0
- package/dist/lib/chunk-VUD72RTY.js +39 -0
- package/dist/lib/glob.wasm +0 -0
- package/dist/lib/index.d.ts +5347 -0
- package/dist/lib/index.js +11990 -0
- package/dist/lib/openai-RDHZFJUT.js +15 -0
- package/dist/lib/seatbelt-FT5IY73W.js +6 -0
- package/dist/lib/tool-filter-VF7TZRE5.js +19 -0
- package/dist/main.js +204 -466
- package/dist/{openai-HXD52MZB.js → openai-6NWVJ5LI.js} +15 -15
- package/dist/{server-VMHWEO2Y.js → server-KQTLAXC6.js} +17 -17
- package/package.json +18 -6
- package/dist/anthropic-4ZVG7AMA.js +0 -4
- package/dist/chunk-6E6UA7MT.js +0 -126
- package/dist/chunk-DDBH5AEN.js +0 -386
- package/dist/chunk-ZZQE743W.js +0 -4
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnthropicClient,
|
|
3
|
+
buildAnthropicMessages,
|
|
4
|
+
fetchModelContextWindow,
|
|
5
|
+
markLastUserTailForCache,
|
|
6
|
+
markToolsForCache,
|
|
7
|
+
needsToolSearchBeta
|
|
8
|
+
} from "./chunk-7IZEK7X5.js";
|
|
9
|
+
import "./chunk-BN4Q54G2.js";
|
|
10
|
+
import "./chunk-CYDNMEBC.js";
|
|
11
|
+
import "./chunk-OO2CLOEE.js";
|
|
12
|
+
import "./chunk-PZ42NAFA.js";
|
|
13
|
+
import "./chunk-EY7HE52Q.js";
|
|
14
|
+
import "./chunk-GHF2PSEW.js";
|
|
15
|
+
export {
|
|
16
|
+
AnthropicClient,
|
|
17
|
+
buildAnthropicMessages,
|
|
18
|
+
fetchModelContextWindow,
|
|
19
|
+
markLastUserTailForCache,
|
|
20
|
+
markToolsForCache,
|
|
21
|
+
needsToolSearchBeta
|
|
22
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PathSandbox,
|
|
3
|
+
PermissionChecker,
|
|
4
|
+
RuleEngine,
|
|
5
|
+
evaluateRules,
|
|
6
|
+
extractContent,
|
|
7
|
+
isSafeCommand
|
|
8
|
+
} from "./chunk-GNI7YX6F.js";
|
|
9
|
+
import "./chunk-PZ42NAFA.js";
|
|
10
|
+
import "./chunk-EY7HE52Q.js";
|
|
11
|
+
import "./chunk-GHF2PSEW.js";
|
|
12
|
+
export {
|
|
13
|
+
PathSandbox,
|
|
14
|
+
PermissionChecker,
|
|
15
|
+
RuleEngine,
|
|
16
|
+
evaluateRules,
|
|
17
|
+
extractContent,
|
|
18
|
+
isSafeCommand
|
|
19
|
+
};
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeCompactThreshold
|
|
3
|
+
} from "./chunk-BN4Q54G2.js";
|
|
4
|
+
import {
|
|
5
|
+
getContextWindow,
|
|
6
|
+
getMaxOutputTokens,
|
|
7
|
+
resolveAPIKey
|
|
8
|
+
} from "./chunk-CYDNMEBC.js";
|
|
9
|
+
import {
|
|
10
|
+
AuthenticationError,
|
|
11
|
+
ContextTooLongError,
|
|
12
|
+
LLMError,
|
|
13
|
+
NetworkError,
|
|
14
|
+
RateLimitError,
|
|
15
|
+
ensureToolPairing
|
|
16
|
+
} from "./chunk-OO2CLOEE.js";
|
|
17
|
+
import {
|
|
18
|
+
MCP_TOOL_PREFIX,
|
|
19
|
+
isMcpToolLike
|
|
20
|
+
} from "./chunk-PZ42NAFA.js";
|
|
21
|
+
import {
|
|
22
|
+
asErrorString,
|
|
23
|
+
asRecord,
|
|
24
|
+
asString,
|
|
25
|
+
contentToText,
|
|
26
|
+
createChildLogger,
|
|
27
|
+
isRecord
|
|
28
|
+
} from "./chunk-EY7HE52Q.js";
|
|
29
|
+
|
|
30
|
+
// src/llm/anthropic.ts
|
|
31
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
32
|
+
import { safeParseAsync, z } from "zod";
|
|
33
|
+
|
|
34
|
+
// src/mcp/strategy.ts
|
|
35
|
+
var DEFAULT_EAGER_THRESHOLD_PERCENT = 10;
|
|
36
|
+
var CHARS_PER_TOKEN = 2.5;
|
|
37
|
+
var NATIVE_TOOL_USE_BETA = "advanced-tool-use-2025-11-20";
|
|
38
|
+
var OFFICIAL_HOSTS = /* @__PURE__ */ new Set(["api.anthropic.com"]);
|
|
39
|
+
var ENV_OVERRIDE = "SWIFTY_MCP_LOADING";
|
|
40
|
+
function isOfficialAnthropicEndpoint(baseUrl) {
|
|
41
|
+
if (!baseUrl) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
return OFFICIAL_HOSTS.has(new URL(baseUrl).hostname.toLowerCase());
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function estimateSchemaTokens(schemaChars) {
|
|
51
|
+
return Math.floor(schemaChars / CHARS_PER_TOKEN);
|
|
52
|
+
}
|
|
53
|
+
function decideMode(baseUrl, contextWindow, mcpSchemaChars, thresholdPercent = DEFAULT_EAGER_THRESHOLD_PERCENT) {
|
|
54
|
+
const override = (process.env[ENV_OVERRIDE] ?? "").trim().toLowerCase();
|
|
55
|
+
if (override === "eager" || override === "native" || override === "dispatch") {
|
|
56
|
+
return override;
|
|
57
|
+
}
|
|
58
|
+
if (mcpSchemaChars <= 0) {
|
|
59
|
+
return "eager";
|
|
60
|
+
}
|
|
61
|
+
const budget = contextWindow * thresholdPercent / 100;
|
|
62
|
+
if (estimateSchemaTokens(mcpSchemaChars) < budget) {
|
|
63
|
+
return "eager";
|
|
64
|
+
}
|
|
65
|
+
return isOfficialAnthropicEndpoint(baseUrl) ? "native" : "dispatch";
|
|
66
|
+
}
|
|
67
|
+
function measureSchemaChars(registry) {
|
|
68
|
+
let total = 0;
|
|
69
|
+
for (const tool of registry.listTools()) {
|
|
70
|
+
if (!tool.name.startsWith(MCP_TOOL_PREFIX)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
total += JSON.stringify(tool.schema()).length;
|
|
75
|
+
} catch {
|
|
76
|
+
total += tool.name.length + (tool.description?.length ?? 0);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return total;
|
|
80
|
+
}
|
|
81
|
+
function applyMode(registry, mode) {
|
|
82
|
+
registry.mcpLoadingMode = mode;
|
|
83
|
+
const eager = mode === "eager";
|
|
84
|
+
for (const tool of registry.listTools()) {
|
|
85
|
+
if (isMcpToolLike(tool) && typeof tool.setDeferLoading === "function") {
|
|
86
|
+
tool.setDeferLoading(!eager);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
registry.exposeToolSearch = !eager;
|
|
90
|
+
registry.exposeMcpCall = mode === "dispatch";
|
|
91
|
+
}
|
|
92
|
+
function decideAndApply(registry, baseUrl, contextWindow) {
|
|
93
|
+
const mode = decideMode(baseUrl, contextWindow, measureSchemaChars(registry));
|
|
94
|
+
applyMode(registry, mode);
|
|
95
|
+
return mode;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/llm/anthropic.ts
|
|
99
|
+
function markToolsForCache(tools) {
|
|
100
|
+
for (let i = tools.length - 1; i >= 0; i--) {
|
|
101
|
+
const t = tools[i];
|
|
102
|
+
if (t.defer_loading === true) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
t.cache_control = { type: "ephemeral" };
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function needsToolSearchBeta(toolSchemas) {
|
|
110
|
+
return toolSchemas.some((s) => s.defer_loading);
|
|
111
|
+
}
|
|
112
|
+
var log = createChildLogger({ module: "llm" });
|
|
113
|
+
var MODEL_FETCH_TIMEOUT_MS = 3e3;
|
|
114
|
+
var ModelContextWindowResSchema = z.object({
|
|
115
|
+
max_input_tokens: z.coerce.number()
|
|
116
|
+
});
|
|
117
|
+
async function fetchModelContextWindow(config) {
|
|
118
|
+
if (config.protocol !== "anthropic") {
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
const apiKey = resolveAPIKey(config);
|
|
122
|
+
const base = config.base_url.replace(/\/+$/, "");
|
|
123
|
+
const url = `${base}/v1/models/${encodeURIComponent(config.model)}`;
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
const timer = setTimeout(() => {
|
|
126
|
+
controller.abort();
|
|
127
|
+
}, MODEL_FETCH_TIMEOUT_MS);
|
|
128
|
+
try {
|
|
129
|
+
const res = await fetch(url, {
|
|
130
|
+
method: "GET",
|
|
131
|
+
headers: {
|
|
132
|
+
"anthropic-version": "2023-06-01",
|
|
133
|
+
...apiKey ? { "x-api-key": apiKey } : {}
|
|
134
|
+
},
|
|
135
|
+
signal: controller.signal
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok) {
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
const body = await res.json();
|
|
141
|
+
const { success, error, data } = await safeParseAsync(ModelContextWindowResSchema, body);
|
|
142
|
+
if (!success) {
|
|
143
|
+
log.warn({ message: error.message }, "model context window schema validation failed");
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
const maxInputTokens = data.max_input_tokens;
|
|
147
|
+
return Math.max(maxInputTokens, 0);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
log.error({ err }, "failed to fetch model context window");
|
|
150
|
+
return 0;
|
|
151
|
+
} finally {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function supportsAdaptiveThinking() {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
function userBlocksFor(content) {
|
|
159
|
+
if (typeof content === "string") {
|
|
160
|
+
return [{ type: "text", text: content }];
|
|
161
|
+
}
|
|
162
|
+
return content;
|
|
163
|
+
}
|
|
164
|
+
function buildAnthropicMessages(messages) {
|
|
165
|
+
const result = [];
|
|
166
|
+
for (const m of messages) {
|
|
167
|
+
if (m.role === "assistant") {
|
|
168
|
+
const blocks = [];
|
|
169
|
+
if (m.thinkingBlocks) {
|
|
170
|
+
for (const tb of m.thinkingBlocks) {
|
|
171
|
+
blocks.push({
|
|
172
|
+
type: "thinking",
|
|
173
|
+
thinking: tb.thinking,
|
|
174
|
+
signature: tb.signature
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const assistantText = typeof m.content === "string" ? m.content : contentToText(m.content);
|
|
179
|
+
if (assistantText) {
|
|
180
|
+
blocks.push({
|
|
181
|
+
type: "text",
|
|
182
|
+
text: assistantText
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (m.toolUses) {
|
|
186
|
+
for (const tu of m.toolUses) {
|
|
187
|
+
blocks.push({
|
|
188
|
+
type: "tool_use",
|
|
189
|
+
// tool use **request**
|
|
190
|
+
id: tu.toolUseId,
|
|
191
|
+
name: tu.toolName,
|
|
192
|
+
input: tu.arguments
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (blocks.length === 0) {
|
|
197
|
+
blocks.push({ type: "text", text: "" });
|
|
198
|
+
}
|
|
199
|
+
result.push({ role: "assistant", content: blocks });
|
|
200
|
+
} else if (m.toolResults && m.toolResults.length > 0) {
|
|
201
|
+
const blocks = [];
|
|
202
|
+
for (const tr of m.toolResults) {
|
|
203
|
+
blocks.push({
|
|
204
|
+
type: "tool_result",
|
|
205
|
+
// tool result
|
|
206
|
+
tool_use_id: tr.toolUseId,
|
|
207
|
+
is_error: tr.isError,
|
|
208
|
+
content: tr.contentBlocks?.length ? tr.contentBlocks : tr.content
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
result.push({ role: "user", content: blocks });
|
|
212
|
+
} else {
|
|
213
|
+
if (result.length === 0) {
|
|
214
|
+
result.push({
|
|
215
|
+
role: "user",
|
|
216
|
+
content: userBlocksFor(m.content)
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
let canMerge = false;
|
|
221
|
+
const prev = result[result.length - 1];
|
|
222
|
+
let content = prev.content;
|
|
223
|
+
if (prev.role === "user" && (typeof content === "string" || Array.isArray(content) && content.length > 0 && // content[0].type !== "tool_result"
|
|
224
|
+
(content[0].type === "text" || content[0].type === "image"))) {
|
|
225
|
+
canMerge = true;
|
|
226
|
+
}
|
|
227
|
+
if (canMerge) {
|
|
228
|
+
if (typeof content === "string") {
|
|
229
|
+
content = prev.content = content.trim().length > 0 ? [
|
|
230
|
+
{
|
|
231
|
+
type: "text",
|
|
232
|
+
text: content
|
|
233
|
+
}
|
|
234
|
+
] : [];
|
|
235
|
+
}
|
|
236
|
+
content.push(...userBlocksFor(m.content));
|
|
237
|
+
} else {
|
|
238
|
+
result.push({
|
|
239
|
+
role: "user",
|
|
240
|
+
content: userBlocksFor(m.content)
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
var AnthropicClient = class {
|
|
248
|
+
client;
|
|
249
|
+
model;
|
|
250
|
+
/**
|
|
251
|
+
* Whether supports/enable thinking, default false
|
|
252
|
+
*/
|
|
253
|
+
thinking;
|
|
254
|
+
systemPrompt;
|
|
255
|
+
maxOutputTokens;
|
|
256
|
+
/** Currently not used */
|
|
257
|
+
contextWindow;
|
|
258
|
+
constructor(config, systemPrompt) {
|
|
259
|
+
const apiKey = resolveAPIKey(config);
|
|
260
|
+
if (!apiKey) {
|
|
261
|
+
throw new AuthenticationError(
|
|
262
|
+
"Anthropic API key not found, set ANTHROPIC_API_KEY in .swifty/config.y(a)ml, or via ANTHROPIC_API_KEY env variable."
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
this.client = new Anthropic({
|
|
266
|
+
apiKey,
|
|
267
|
+
baseURL: config.base_url
|
|
268
|
+
});
|
|
269
|
+
this.model = config.model;
|
|
270
|
+
this.thinking = config.thinking ?? true;
|
|
271
|
+
this.systemPrompt = systemPrompt;
|
|
272
|
+
this.maxOutputTokens = getMaxOutputTokens(config);
|
|
273
|
+
this.contextWindow = getContextWindow(config);
|
|
274
|
+
}
|
|
275
|
+
setSystemPrompt(prompt) {
|
|
276
|
+
this.systemPrompt = prompt;
|
|
277
|
+
}
|
|
278
|
+
setMaxOutputTokens(maxTokens) {
|
|
279
|
+
this.maxOutputTokens = maxTokens;
|
|
280
|
+
}
|
|
281
|
+
async *stream(conversation, toolSchemas, abortSignal) {
|
|
282
|
+
const messages = buildAnthropicMessages(ensureToolPairing(conversation.getMessages()));
|
|
283
|
+
const sendToolSearchBeta = needsToolSearchBeta(toolSchemas);
|
|
284
|
+
const antToolSchemas = toolSchemas.map((s) => {
|
|
285
|
+
const inputSchema = s.input_schema;
|
|
286
|
+
const tool = {
|
|
287
|
+
name: s.name,
|
|
288
|
+
description: s.description,
|
|
289
|
+
input_schema: {
|
|
290
|
+
type: "object",
|
|
291
|
+
properties: inputSchema.properties,
|
|
292
|
+
required: inputSchema.required ?? []
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
if (s.defer_loading === true) {
|
|
296
|
+
tool.defer_loading = true;
|
|
297
|
+
}
|
|
298
|
+
return tool;
|
|
299
|
+
});
|
|
300
|
+
markToolsForCache(antToolSchemas);
|
|
301
|
+
markLastUserTailForCache(messages);
|
|
302
|
+
const params = {
|
|
303
|
+
model: this.model,
|
|
304
|
+
max_tokens: this.maxOutputTokens,
|
|
305
|
+
stream: true,
|
|
306
|
+
system: [
|
|
307
|
+
{
|
|
308
|
+
type: "text",
|
|
309
|
+
text: this.systemPrompt,
|
|
310
|
+
cache_control: {
|
|
311
|
+
type: "ephemeral"
|
|
312
|
+
// Prompt cache
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
],
|
|
316
|
+
messages,
|
|
317
|
+
...antToolSchemas.length > 0 ? { tools: antToolSchemas } : {}
|
|
318
|
+
};
|
|
319
|
+
if (this.thinking) {
|
|
320
|
+
if (supportsAdaptiveThinking()) {
|
|
321
|
+
params.thinking = {
|
|
322
|
+
type: "enabled",
|
|
323
|
+
budget_tokens: computeCompactThreshold(this.contextWindow, this.maxOutputTokens)
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
params.thinking = {
|
|
328
|
+
type: "enabled",
|
|
329
|
+
budget_tokens: computeCompactThreshold(this.contextWindow, this.maxOutputTokens)
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
let inputTokens = 0;
|
|
333
|
+
let outputTokens = 0;
|
|
334
|
+
let cacheReadInputTokens = 0;
|
|
335
|
+
let cacheCreationInputTokens = 0;
|
|
336
|
+
let stopReason = "end_turn";
|
|
337
|
+
let thinkingAccumulate = "";
|
|
338
|
+
let thinkingSignature = "";
|
|
339
|
+
let inThinking = false;
|
|
340
|
+
try {
|
|
341
|
+
const response = this.client.messages.stream(params, {
|
|
342
|
+
...abortSignal ? { signal: abortSignal } : {},
|
|
343
|
+
// If any tool uses defer_loading this beta header is required, otherwise the server does not recognize the field.
|
|
344
|
+
// Only the official endpoint reaches this code path (see mcp/strategy).
|
|
345
|
+
...sendToolSearchBeta ? { headers: { "anthropic-beta": NATIVE_TOOL_USE_BETA } } : {}
|
|
346
|
+
});
|
|
347
|
+
let currentToolName = "";
|
|
348
|
+
let currentToolId = "";
|
|
349
|
+
let jsonAccumulate = "";
|
|
350
|
+
for await (const event of response) {
|
|
351
|
+
switch (event.type) {
|
|
352
|
+
case "content_block_start": {
|
|
353
|
+
const block = event.content_block;
|
|
354
|
+
if (block.type === "thinking") {
|
|
355
|
+
inThinking = true;
|
|
356
|
+
thinkingAccumulate = "";
|
|
357
|
+
thinkingSignature = "";
|
|
358
|
+
} else if (block.type === "tool_use") {
|
|
359
|
+
currentToolId = block.id;
|
|
360
|
+
currentToolName = block.name;
|
|
361
|
+
jsonAccumulate = "";
|
|
362
|
+
yield {
|
|
363
|
+
type: "tool_call_start",
|
|
364
|
+
toolName: currentToolName,
|
|
365
|
+
toolId: currentToolId
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
// end case "content_block_start"
|
|
371
|
+
case "content_block_delta": {
|
|
372
|
+
const delta = event.delta;
|
|
373
|
+
if (delta.type === "thinking_delta") {
|
|
374
|
+
thinkingAccumulate += delta.thinking;
|
|
375
|
+
yield {
|
|
376
|
+
type: "thinking_delta",
|
|
377
|
+
text: delta.thinking
|
|
378
|
+
};
|
|
379
|
+
} else if (delta.type === "signature_delta") {
|
|
380
|
+
thinkingSignature = delta.signature;
|
|
381
|
+
} else if (delta.type === "text_delta") {
|
|
382
|
+
yield {
|
|
383
|
+
type: "text_delta",
|
|
384
|
+
text: delta.text
|
|
385
|
+
};
|
|
386
|
+
} else if (delta.type === "input_json_delta") {
|
|
387
|
+
jsonAccumulate += delta.partial_json;
|
|
388
|
+
yield {
|
|
389
|
+
type: "tool_call_delta",
|
|
390
|
+
text: delta.partial_json
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
// end case "content_block_delta"
|
|
396
|
+
case "content_block_stop": {
|
|
397
|
+
if (inThinking) {
|
|
398
|
+
yield {
|
|
399
|
+
type: "thinking_complete",
|
|
400
|
+
thinking: thinkingAccumulate,
|
|
401
|
+
signature: thinkingSignature
|
|
402
|
+
};
|
|
403
|
+
inThinking = false;
|
|
404
|
+
}
|
|
405
|
+
if (currentToolName) {
|
|
406
|
+
let args = {};
|
|
407
|
+
if (jsonAccumulate) {
|
|
408
|
+
try {
|
|
409
|
+
const parsed = JSON.parse(jsonAccumulate);
|
|
410
|
+
args = isRecord(parsed) ? asRecord(parsed) : {};
|
|
411
|
+
} catch (err) {
|
|
412
|
+
log.error({ err }, "llm operation failed");
|
|
413
|
+
args = {};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
yield {
|
|
417
|
+
type: "tool_call_complete",
|
|
418
|
+
toolId: currentToolId,
|
|
419
|
+
toolName: currentToolName,
|
|
420
|
+
arguments: args
|
|
421
|
+
};
|
|
422
|
+
currentToolName = "";
|
|
423
|
+
currentToolId = "";
|
|
424
|
+
jsonAccumulate = "";
|
|
425
|
+
}
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
// end case "content_block_stop"
|
|
429
|
+
case "message_delta": {
|
|
430
|
+
if (event.delta.stop_reason) {
|
|
431
|
+
stopReason = event.delta.stop_reason;
|
|
432
|
+
}
|
|
433
|
+
if (event.usage.output_tokens) {
|
|
434
|
+
outputTokens = event.usage.output_tokens;
|
|
435
|
+
if (event.usage.input_tokens) {
|
|
436
|
+
inputTokens = event.usage.input_tokens;
|
|
437
|
+
}
|
|
438
|
+
if (event.usage.cache_read_input_tokens) {
|
|
439
|
+
cacheReadInputTokens = event.usage.cache_read_input_tokens;
|
|
440
|
+
}
|
|
441
|
+
if (event.usage.cache_creation_input_tokens) {
|
|
442
|
+
cacheCreationInputTokens = event.usage.cache_creation_input_tokens;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
// end case "message_delta"
|
|
448
|
+
case "message_start": {
|
|
449
|
+
inputTokens = event.message.usage.input_tokens;
|
|
450
|
+
outputTokens = event.message.usage.output_tokens;
|
|
451
|
+
cacheReadInputTokens = event.message.usage.cache_read_input_tokens ?? 0;
|
|
452
|
+
cacheCreationInputTokens = event.message.usage.cache_creation_input_tokens ?? 0;
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
yield {
|
|
458
|
+
type: "stream_end",
|
|
459
|
+
stopReason,
|
|
460
|
+
usage: {
|
|
461
|
+
inputTokens,
|
|
462
|
+
outputTokens,
|
|
463
|
+
cacheReadInputTokens,
|
|
464
|
+
cacheCreationInputTokens
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
} catch (err) {
|
|
468
|
+
log.error({ err }, "llm operation failed");
|
|
469
|
+
throw classifyAnthropicError(err);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
function markLastUserTailForCache(messages) {
|
|
474
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
475
|
+
if (messages[i].role !== "user") {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
let content = messages[i].content;
|
|
479
|
+
if (typeof content === "string" && content.length === 0 || Array.isArray(content) && content.length === 0) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (typeof content === "string") {
|
|
483
|
+
content = messages[i].content = [
|
|
484
|
+
{
|
|
485
|
+
type: "text",
|
|
486
|
+
text: content
|
|
487
|
+
}
|
|
488
|
+
];
|
|
489
|
+
}
|
|
490
|
+
let last = content[content.length - 1];
|
|
491
|
+
for (let j = content.length - 1; j >= 0; j--) {
|
|
492
|
+
if (content[j].type !== "image") {
|
|
493
|
+
last = content[j];
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
Reflect.set(last, "cache_control", {
|
|
498
|
+
type: "ephemeral"
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function classifyAnthropicError(err) {
|
|
503
|
+
if (err instanceof Anthropic.APIError) {
|
|
504
|
+
if (err.status === 413 /* PromptTooLong */ || /prompts?\s+too\s+long/i.test(err.message)) {
|
|
505
|
+
return new ContextTooLongError(`Prompt too long: ${err.message}`);
|
|
506
|
+
}
|
|
507
|
+
if (err.status === 401 /* InvalidAPIKey */) {
|
|
508
|
+
return new AuthenticationError(`Invalid API key: ${err.message}`);
|
|
509
|
+
}
|
|
510
|
+
if (err.status === 429 /* RateLimitError */) {
|
|
511
|
+
const retryAfter = asRecord(err.headers)["retry-after"];
|
|
512
|
+
let message = "Rate Limited";
|
|
513
|
+
if (retryAfter) {
|
|
514
|
+
const s = Number.parseInt(asString(retryAfter));
|
|
515
|
+
if (Number.isNaN(s)) {
|
|
516
|
+
message += ", please wait.";
|
|
517
|
+
}
|
|
518
|
+
message += `, retry after ${asString(s)}s.`;
|
|
519
|
+
} else {
|
|
520
|
+
message += ", please wait.";
|
|
521
|
+
}
|
|
522
|
+
return new RateLimitError(message, retryAfter ? asString(retryAfter) : void 0);
|
|
523
|
+
}
|
|
524
|
+
return new LLMError(`Anthropic API error (${asString(err.status)}): ${err.message}`);
|
|
525
|
+
}
|
|
526
|
+
return new NetworkError(`Network error: ${asErrorString(err)}`);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
export {
|
|
530
|
+
DEFAULT_EAGER_THRESHOLD_PERCENT,
|
|
531
|
+
CHARS_PER_TOKEN,
|
|
532
|
+
NATIVE_TOOL_USE_BETA,
|
|
533
|
+
isOfficialAnthropicEndpoint,
|
|
534
|
+
estimateSchemaTokens,
|
|
535
|
+
decideMode,
|
|
536
|
+
measureSchemaChars,
|
|
537
|
+
applyMode,
|
|
538
|
+
decideAndApply,
|
|
539
|
+
markToolsForCache,
|
|
540
|
+
needsToolSearchBeta,
|
|
541
|
+
fetchModelContextWindow,
|
|
542
|
+
buildAnthropicMessages,
|
|
543
|
+
AnthropicClient,
|
|
544
|
+
markLastUserTailForCache
|
|
545
|
+
};
|