@byfriends/kosong 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +11 -0
- package/dist/anthropic-Dm_GqFgS.d.mts +69 -0
- package/dist/capability-registry-CMBuEYcf.mjs +161 -0
- package/dist/chat-completions-stream-BuMu_xr9.mjs +62 -0
- package/dist/errors-DweKbIOf.d.mts +42 -0
- package/dist/errors-WFxxzL1B.mjs +80 -0
- package/dist/google-genai-hX0X6CF3.d.mts +98 -0
- package/dist/index.d.mts +161 -0
- package/dist/index.mjs +287 -0
- package/dist/openai-common-08qin3UI.mjs +278 -0
- package/dist/openai-common-B6cK2ig3.d.mts +105 -0
- package/dist/openai-compat-CMrIk-ib.d.mts +132 -0
- package/dist/openai-compat-CWbwO4b7.mjs +801 -0
- package/dist/openai-legacy-B6CVfLlr.d.mts +71 -0
- package/dist/openai-responses-BxOwxtd3.d.mts +65 -0
- package/dist/provider-DiJKWMsQ.d.mts +371 -0
- package/dist/providers/anthropic.d.mts +2 -0
- package/dist/providers/anthropic.mjs +720 -0
- package/dist/providers/google-genai.d.mts +2 -0
- package/dist/providers/google-genai.mjs +562 -0
- package/dist/providers/openai-common.d.mts +2 -0
- package/dist/providers/openai-common.mjs +2 -0
- package/dist/providers/openai-compat.d.mts +2 -0
- package/dist/providers/openai-compat.mjs +2 -0
- package/dist/providers/openai-legacy.d.mts +2 -0
- package/dist/providers/openai-legacy.mjs +248 -0
- package/dist/providers/openai-responses.d.mts +2 -0
- package/dist/providers/openai-responses.mjs +623 -0
- package/dist/request-auth-DCWSyCKI.mjs +63 -0
- package/package.json +89 -0
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
import { a as APITimeoutError, o as ChatProviderError, s as normalizeAPIStatusError, t as APIConnectionError$1 } from "../errors-WFxxzL1B.mjs";
|
|
2
|
+
import { n as requireProviderApiKey, r as resolveAuthBackedClient, t as mergeRequestHeaders } from "../request-auth-DCWSyCKI.mjs";
|
|
3
|
+
import { t as getAnthropicModelCapability } from "../capability-registry-CMBuEYcf.mjs";
|
|
4
|
+
import Anthropic, { APIConnectionError, APIConnectionTimeoutError, APIError, AnthropicError } from "@anthropic-ai/sdk";
|
|
5
|
+
//#region src/providers/anthropic.ts
|
|
6
|
+
/**
|
|
7
|
+
* Normalize an Anthropic `stop_reason` string to the unified
|
|
8
|
+
* {@link FinishReason} enum.
|
|
9
|
+
*
|
|
10
|
+
* Source: `message.stop_reason` (non-stream) or the last `message_delta`
|
|
11
|
+
* event's `delta.stop_reason` (stream).
|
|
12
|
+
*/
|
|
13
|
+
function normalizeAnthropicStopReason(raw) {
|
|
14
|
+
if (raw === null || raw === void 0) return {
|
|
15
|
+
finishReason: null,
|
|
16
|
+
rawFinishReason: null
|
|
17
|
+
};
|
|
18
|
+
switch (raw) {
|
|
19
|
+
case "end_turn":
|
|
20
|
+
case "stop_sequence": return {
|
|
21
|
+
finishReason: "completed",
|
|
22
|
+
rawFinishReason: raw
|
|
23
|
+
};
|
|
24
|
+
case "max_tokens": return {
|
|
25
|
+
finishReason: "truncated",
|
|
26
|
+
rawFinishReason: raw
|
|
27
|
+
};
|
|
28
|
+
case "tool_use": return {
|
|
29
|
+
finishReason: "tool_calls",
|
|
30
|
+
rawFinishReason: raw
|
|
31
|
+
};
|
|
32
|
+
case "pause_turn": return {
|
|
33
|
+
finishReason: "paused",
|
|
34
|
+
rawFinishReason: raw
|
|
35
|
+
};
|
|
36
|
+
case "refusal": return {
|
|
37
|
+
finishReason: "filtered",
|
|
38
|
+
rawFinishReason: raw
|
|
39
|
+
};
|
|
40
|
+
default: return {
|
|
41
|
+
finishReason: "other",
|
|
42
|
+
rawFinishReason: raw
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
|
|
47
|
+
const FAMILY_VERSION_RE = /(?:opus|sonnet|haiku)[.-](\d+)[.-](\d{1,2})(?!\d)/;
|
|
48
|
+
const OPUS_VERSION_RE = /opus[.-](\d+)[.-](\d{1,2})(?!\d)/;
|
|
49
|
+
const ADAPTIVE_MIN_VERSION = {
|
|
50
|
+
major: 4,
|
|
51
|
+
minor: 6
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Per-version default output ceilings sourced from Anthropic's Messages
|
|
55
|
+
* API model cards (platform.claude.com/docs/en/about-claude/models/overview).
|
|
56
|
+
* Values are the documented synchronous Messages-API maximum — we send
|
|
57
|
+
* the full ceiling because Claude 4 + interleaved-thinking shares this
|
|
58
|
+
* budget with encrypted reasoning, so anything below the documented cap
|
|
59
|
+
* can silently truncate mid-`tool_use`.
|
|
60
|
+
*
|
|
61
|
+
* Keys are `<family>-<major>[-<minor>]`. Lookups try the most specific
|
|
62
|
+
* key first, then fall back to the family/major-only entry, so an
|
|
63
|
+
* unrecognized minor version (e.g. a future `opus-4-10`) gets the
|
|
64
|
+
* family's baseline rather than the generic fallback.
|
|
65
|
+
*/
|
|
66
|
+
const CEILING_BY_FAMILY_VERSION = {
|
|
67
|
+
"opus-4-7": 128e3,
|
|
68
|
+
"opus-4-6": 128e3,
|
|
69
|
+
"opus-4-5": 64e3,
|
|
70
|
+
"opus-4-1": 32e3,
|
|
71
|
+
"opus-4-0": 32e3,
|
|
72
|
+
"opus-4": 32e3,
|
|
73
|
+
"sonnet-4-6": 64e3,
|
|
74
|
+
"sonnet-4-5": 64e3,
|
|
75
|
+
"sonnet-4-0": 64e3,
|
|
76
|
+
"sonnet-4": 64e3,
|
|
77
|
+
"haiku-4-5": 64e3,
|
|
78
|
+
"haiku-4": 64e3,
|
|
79
|
+
"opus-3-5": 8192,
|
|
80
|
+
"sonnet-3-5": 8192,
|
|
81
|
+
"sonnet-3-7": 8192,
|
|
82
|
+
"haiku-3-5": 8192,
|
|
83
|
+
"opus-3": 4096,
|
|
84
|
+
"sonnet-3": 4096,
|
|
85
|
+
"haiku-3": 4096
|
|
86
|
+
};
|
|
87
|
+
const FALLBACK_MAX_TOKENS = 32e3;
|
|
88
|
+
const FAMILY_FIRST_RE = /(opus|sonnet|haiku)[-._](\d{1,2})(?!\d)(?:[-._](\d{1,2})(?!\d))?/;
|
|
89
|
+
const VERSION_FIRST_RE = /(\d{1,2})[-._](\d{1,2})[-._](opus|sonnet|haiku)/;
|
|
90
|
+
const BARE_FAMILY_RE = /(\d{1,2})[-._](opus|sonnet|haiku)/;
|
|
91
|
+
/**
|
|
92
|
+
* Extract Claude family + version from a model id.
|
|
93
|
+
*
|
|
94
|
+
* Designed to survive the naming variants we see across vendors:
|
|
95
|
+
* vendor prefixes (`anthropic.`, `aws/`, `openrouter/`,
|
|
96
|
+
* `online-`), suffixes (date stamps like `-20251001`, build tags
|
|
97
|
+
* like `-construct`, `-v1:0`), and `.` vs `-` separators between
|
|
98
|
+
* the family and version components.
|
|
99
|
+
*
|
|
100
|
+
* Returns `null` when the id contains no Claude marker or no
|
|
101
|
+
* recognizable family/version, in which case the resolver should fall
|
|
102
|
+
* back to the override or {@link FALLBACK_MAX_TOKENS}.
|
|
103
|
+
*/
|
|
104
|
+
function parseClaudeVersion(model) {
|
|
105
|
+
const normalized = model.toLowerCase();
|
|
106
|
+
if (!normalized.includes("claude")) return null;
|
|
107
|
+
const familyFirst = FAMILY_FIRST_RE.exec(normalized);
|
|
108
|
+
if (familyFirst !== null) return {
|
|
109
|
+
family: familyFirst[1],
|
|
110
|
+
major: Number.parseInt(familyFirst[2], 10),
|
|
111
|
+
minor: familyFirst[3] !== void 0 ? Number.parseInt(familyFirst[3], 10) : null
|
|
112
|
+
};
|
|
113
|
+
const versionFirst = VERSION_FIRST_RE.exec(normalized);
|
|
114
|
+
if (versionFirst !== null) return {
|
|
115
|
+
major: Number.parseInt(versionFirst[1], 10),
|
|
116
|
+
minor: Number.parseInt(versionFirst[2], 10),
|
|
117
|
+
family: versionFirst[3]
|
|
118
|
+
};
|
|
119
|
+
const bare = BARE_FAMILY_RE.exec(normalized);
|
|
120
|
+
if (bare !== null) return {
|
|
121
|
+
major: Number.parseInt(bare[1], 10),
|
|
122
|
+
minor: null,
|
|
123
|
+
family: bare[2]
|
|
124
|
+
};
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
function lookupClaudeCeiling(version) {
|
|
128
|
+
const { family, major, minor } = version;
|
|
129
|
+
if (minor !== null) {
|
|
130
|
+
const exact = CEILING_BY_FAMILY_VERSION[`${family}-${major}-${minor}`];
|
|
131
|
+
if (exact !== void 0) return exact;
|
|
132
|
+
}
|
|
133
|
+
return CEILING_BY_FAMILY_VERSION[`${family}-${major}`];
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Resolve the default `max_tokens` for an Anthropic request.
|
|
137
|
+
*
|
|
138
|
+
* Precedence:
|
|
139
|
+
* 1. Caller-provided `override` (e.g. `models.<alias>.maxOutputSize`
|
|
140
|
+
* from the harness config) — honored when present so users can
|
|
141
|
+
* intentionally lower the budget (handy for forcing truncation
|
|
142
|
+
* in tests) or raise it on a model we don't yet know about.
|
|
143
|
+
* 2. When the model id parses to a known Claude family + version,
|
|
144
|
+
* the override is clamped to the documented Messages-API ceiling
|
|
145
|
+
* so we never send a value the server would reject.
|
|
146
|
+
* 3. With no override and no recognized version, fall back to
|
|
147
|
+
* {@link FALLBACK_MAX_TOKENS}.
|
|
148
|
+
*/
|
|
149
|
+
function resolveDefaultMaxTokens(model, override) {
|
|
150
|
+
const parsed = parseClaudeVersion(model);
|
|
151
|
+
const ceiling = parsed === null ? void 0 : lookupClaudeCeiling(parsed);
|
|
152
|
+
if (ceiling === void 0) return override ?? FALLBACK_MAX_TOKENS;
|
|
153
|
+
return override === void 0 ? ceiling : Math.min(override, ceiling);
|
|
154
|
+
}
|
|
155
|
+
function parseVersion(match) {
|
|
156
|
+
const majorRaw = match[1];
|
|
157
|
+
const minorRaw = match[2];
|
|
158
|
+
if (majorRaw === void 0 || minorRaw === void 0) throw new Error("Model version regex did not capture major and minor versions.");
|
|
159
|
+
return {
|
|
160
|
+
major: Number.parseInt(majorRaw, 10),
|
|
161
|
+
minor: Number.parseInt(minorRaw, 10)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function versionAtLeast(version, minimum) {
|
|
165
|
+
return version.major > minimum.major || version.major === minimum.major && version.minor >= minimum.minor;
|
|
166
|
+
}
|
|
167
|
+
function supportsAdaptiveThinking(model) {
|
|
168
|
+
const normalized = model.toLowerCase();
|
|
169
|
+
const match = FAMILY_VERSION_RE.exec(normalized);
|
|
170
|
+
if (match === null) return false;
|
|
171
|
+
return versionAtLeast(parseVersion(match), ADAPTIVE_MIN_VERSION);
|
|
172
|
+
}
|
|
173
|
+
function isOpus47(model) {
|
|
174
|
+
const match = OPUS_VERSION_RE.exec(model.toLowerCase());
|
|
175
|
+
if (match === null) return false;
|
|
176
|
+
const version = parseVersion(match);
|
|
177
|
+
return version.major === 4 && version.minor === 7;
|
|
178
|
+
}
|
|
179
|
+
function supportsEffortParam(model) {
|
|
180
|
+
if (supportsAdaptiveThinking(model)) return true;
|
|
181
|
+
const normalized = model.toLowerCase();
|
|
182
|
+
return normalized.includes("opus-4-5") || normalized.includes("opus-4.5");
|
|
183
|
+
}
|
|
184
|
+
function clampEffort(effort, model) {
|
|
185
|
+
if (effort === "off") return effort;
|
|
186
|
+
if (effort === "xhigh" && !isOpus47(model)) return "high";
|
|
187
|
+
if (effort === "max" && !supportsAdaptiveThinking(model)) return "high";
|
|
188
|
+
return effort;
|
|
189
|
+
}
|
|
190
|
+
function budgetTokensForEffort(effort) {
|
|
191
|
+
switch (effort) {
|
|
192
|
+
case "low": return 1024;
|
|
193
|
+
case "medium": return 4096;
|
|
194
|
+
case "high": return 32e3;
|
|
195
|
+
case "off":
|
|
196
|
+
case "xhigh":
|
|
197
|
+
case "max": throw new Error(`Unsupported budget-based thinking effort: ${effort}`);
|
|
198
|
+
}
|
|
199
|
+
throw new Error(`Unknown thinking effort: ${String(effort)}`);
|
|
200
|
+
}
|
|
201
|
+
const CACHE_CONTROL = { type: "ephemeral" };
|
|
202
|
+
/**
|
|
203
|
+
* Content block types that support cache_control injection.
|
|
204
|
+
*/
|
|
205
|
+
const CACHEABLE_TYPES = new Set([
|
|
206
|
+
"text",
|
|
207
|
+
"image",
|
|
208
|
+
"document",
|
|
209
|
+
"search_result",
|
|
210
|
+
"tool_use",
|
|
211
|
+
"tool_result",
|
|
212
|
+
"server_tool_use",
|
|
213
|
+
"web_search_tool_result"
|
|
214
|
+
]);
|
|
215
|
+
function injectCacheControlOnLastBlock(messages) {
|
|
216
|
+
const lastMessage = messages.at(-1);
|
|
217
|
+
if (lastMessage === void 0) return;
|
|
218
|
+
const content = lastMessage.content;
|
|
219
|
+
if (!Array.isArray(content) || content.length === 0) return;
|
|
220
|
+
const lastBlock = content.at(-1);
|
|
221
|
+
if (lastBlock === void 0) return;
|
|
222
|
+
if (CACHEABLE_TYPES.has(lastBlock.type)) lastBlock.cache_control = CACHE_CONTROL;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Check whether a MessageParam is a user message whose content consists
|
|
226
|
+
* entirely of `tool_result` blocks.
|
|
227
|
+
*
|
|
228
|
+
* Used to detect adjacent tool-result-only messages that must be merged
|
|
229
|
+
* before hitting the Anthropic wire. Per the Messages API parallel-tool-use
|
|
230
|
+
* spec, all `tool_result` blocks answering parallel `tool_use` calls must
|
|
231
|
+
* live in a single user message — splitting them across consecutive user
|
|
232
|
+
* messages fails on strict Anthropic-compatible backends (HTTP 400) and
|
|
233
|
+
* silently degrades parallel tool use on api.anthropic.com.
|
|
234
|
+
*/
|
|
235
|
+
function isToolResultOnly(message) {
|
|
236
|
+
if (message.role !== "user") return false;
|
|
237
|
+
const content = message.content;
|
|
238
|
+
if (!Array.isArray(content) || content.length === 0) return false;
|
|
239
|
+
return content.every((block) => block.type === "tool_result");
|
|
240
|
+
}
|
|
241
|
+
const SUPPORTED_B64_MEDIA_TYPES = new Set([
|
|
242
|
+
"image/png",
|
|
243
|
+
"image/jpeg",
|
|
244
|
+
"image/gif",
|
|
245
|
+
"image/webp"
|
|
246
|
+
]);
|
|
247
|
+
function imageUrlPartToAnthropic(url) {
|
|
248
|
+
if (url.startsWith("data:")) {
|
|
249
|
+
const parts = url.slice(5).split(";base64,", 2);
|
|
250
|
+
if (parts.length !== 2 || parts[0] === void 0 || parts[1] === void 0) throw new ChatProviderError(`Invalid data URL for image: ${url}`);
|
|
251
|
+
const mediaType = parts[0];
|
|
252
|
+
const data = parts[1];
|
|
253
|
+
if (!SUPPORTED_B64_MEDIA_TYPES.has(mediaType)) throw new ChatProviderError(`Unsupported media type for base64 image: ${mediaType}, url: ${url}`);
|
|
254
|
+
return {
|
|
255
|
+
type: "image",
|
|
256
|
+
source: {
|
|
257
|
+
type: "base64",
|
|
258
|
+
data,
|
|
259
|
+
media_type: mediaType
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
type: "image",
|
|
265
|
+
source: {
|
|
266
|
+
type: "url",
|
|
267
|
+
url
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function convertTool(tool) {
|
|
272
|
+
return {
|
|
273
|
+
name: tool.name,
|
|
274
|
+
description: tool.description,
|
|
275
|
+
input_schema: tool.parameters
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function toolResultToBlock(toolCallId, content) {
|
|
279
|
+
const blocks = [];
|
|
280
|
+
for (const part of content) if (part.type === "text") {
|
|
281
|
+
if (part.text) blocks.push({
|
|
282
|
+
type: "text",
|
|
283
|
+
text: part.text
|
|
284
|
+
});
|
|
285
|
+
} else if (part.type === "image_url") blocks.push(imageUrlPartToAnthropic(part.imageUrl.url));
|
|
286
|
+
return {
|
|
287
|
+
type: "tool_result",
|
|
288
|
+
tool_use_id: toolCallId,
|
|
289
|
+
content: blocks
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function convertMessage(message) {
|
|
293
|
+
const role = message.role;
|
|
294
|
+
if (role === "system") return {
|
|
295
|
+
role: "user",
|
|
296
|
+
content: [{
|
|
297
|
+
type: "text",
|
|
298
|
+
text: `<system>${message.content.filter((p) => p.type === "text").map((p) => p.text).join("\n")}</system>`
|
|
299
|
+
}]
|
|
300
|
+
};
|
|
301
|
+
if (role === "tool") {
|
|
302
|
+
if (message.toolCallId === void 0) throw new ChatProviderError("Tool message missing `toolCallId`.");
|
|
303
|
+
return {
|
|
304
|
+
role: "user",
|
|
305
|
+
content: [toolResultToBlock(message.toolCallId, message.content)]
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const blocks = [];
|
|
309
|
+
for (const part of message.content) if (part.type === "text") blocks.push({
|
|
310
|
+
type: "text",
|
|
311
|
+
text: part.text
|
|
312
|
+
});
|
|
313
|
+
else if (part.type === "image_url") blocks.push(imageUrlPartToAnthropic(part.imageUrl.url));
|
|
314
|
+
else if (part.type === "think") {
|
|
315
|
+
if (part.encrypted === void 0) continue;
|
|
316
|
+
blocks.push({
|
|
317
|
+
type: "thinking",
|
|
318
|
+
thinking: part.think,
|
|
319
|
+
signature: part.encrypted
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
if (message.toolCalls.length > 0) for (const tc of message.toolCalls) {
|
|
323
|
+
let toolInput = {};
|
|
324
|
+
if (tc.arguments) try {
|
|
325
|
+
const parsed = JSON.parse(tc.arguments);
|
|
326
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) toolInput = parsed;
|
|
327
|
+
else throw new ChatProviderError("Tool call arguments must be a JSON object.");
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (error instanceof ChatProviderError) throw error;
|
|
330
|
+
throw new ChatProviderError("Tool call arguments must be valid JSON.");
|
|
331
|
+
}
|
|
332
|
+
blocks.push({
|
|
333
|
+
type: "tool_use",
|
|
334
|
+
id: tc.id,
|
|
335
|
+
name: tc.name,
|
|
336
|
+
input: toolInput
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
role,
|
|
341
|
+
content: blocks
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function convertAnthropicError(error) {
|
|
345
|
+
if (error instanceof APIConnectionTimeoutError) return new APITimeoutError(error.message);
|
|
346
|
+
if (error instanceof APIConnectionError) return new APIConnectionError$1(error.message);
|
|
347
|
+
if (error instanceof APIError && typeof error.status === "number") {
|
|
348
|
+
const reqId = error.requestID ?? null;
|
|
349
|
+
return normalizeAPIStatusError(error.status, error.message, reqId);
|
|
350
|
+
}
|
|
351
|
+
if (error instanceof AnthropicError) return new ChatProviderError(`Anthropic error: ${error.message}`);
|
|
352
|
+
if (error instanceof Error) return new ChatProviderError(`Error: ${error.message}`);
|
|
353
|
+
return new ChatProviderError(`Error: ${String(error)}`);
|
|
354
|
+
}
|
|
355
|
+
var AnthropicStreamedMessage = class {
|
|
356
|
+
_id = null;
|
|
357
|
+
_usage = {
|
|
358
|
+
inputOther: 0,
|
|
359
|
+
output: 0,
|
|
360
|
+
inputCacheRead: 0,
|
|
361
|
+
inputCacheCreation: 0
|
|
362
|
+
};
|
|
363
|
+
_finishReason = null;
|
|
364
|
+
_rawFinishReason = null;
|
|
365
|
+
_iter;
|
|
366
|
+
constructor(response, isStream) {
|
|
367
|
+
if (isStream) this._iter = this._convertStreamResponse(response);
|
|
368
|
+
else this._iter = this._convertNonStreamResponse(response);
|
|
369
|
+
}
|
|
370
|
+
get id() {
|
|
371
|
+
return this._id;
|
|
372
|
+
}
|
|
373
|
+
get usage() {
|
|
374
|
+
return this._usage;
|
|
375
|
+
}
|
|
376
|
+
get finishReason() {
|
|
377
|
+
return this._finishReason;
|
|
378
|
+
}
|
|
379
|
+
get rawFinishReason() {
|
|
380
|
+
return this._rawFinishReason;
|
|
381
|
+
}
|
|
382
|
+
async *[Symbol.asyncIterator]() {
|
|
383
|
+
yield* this._iter;
|
|
384
|
+
}
|
|
385
|
+
_captureStopReason(raw) {
|
|
386
|
+
const normalized = normalizeAnthropicStopReason(raw);
|
|
387
|
+
this._finishReason = normalized.finishReason;
|
|
388
|
+
this._rawFinishReason = normalized.rawFinishReason;
|
|
389
|
+
}
|
|
390
|
+
_extractUsage(usage) {
|
|
391
|
+
this._usage = {
|
|
392
|
+
inputOther: usage.input_tokens ?? 0,
|
|
393
|
+
output: usage.output_tokens ?? 0,
|
|
394
|
+
inputCacheRead: usage.cache_read_input_tokens ?? 0,
|
|
395
|
+
inputCacheCreation: usage.cache_creation_input_tokens ?? 0
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
async *_convertNonStreamResponse(response) {
|
|
399
|
+
this._id = response.id;
|
|
400
|
+
this._extractUsage(response.usage);
|
|
401
|
+
this._captureStopReason(response.stop_reason);
|
|
402
|
+
for (const block of response.content) switch (block.type) {
|
|
403
|
+
case "text":
|
|
404
|
+
if (block.text !== void 0) yield {
|
|
405
|
+
type: "text",
|
|
406
|
+
text: block.text
|
|
407
|
+
};
|
|
408
|
+
break;
|
|
409
|
+
case "thinking":
|
|
410
|
+
yield block.signature !== void 0 ? {
|
|
411
|
+
type: "think",
|
|
412
|
+
think: block.thinking ?? "",
|
|
413
|
+
encrypted: block.signature
|
|
414
|
+
} : {
|
|
415
|
+
type: "think",
|
|
416
|
+
think: block.thinking ?? ""
|
|
417
|
+
};
|
|
418
|
+
break;
|
|
419
|
+
case "redacted_thinking":
|
|
420
|
+
yield block.data !== void 0 ? {
|
|
421
|
+
type: "think",
|
|
422
|
+
think: "",
|
|
423
|
+
encrypted: block.data
|
|
424
|
+
} : {
|
|
425
|
+
type: "think",
|
|
426
|
+
think: ""
|
|
427
|
+
};
|
|
428
|
+
break;
|
|
429
|
+
case "tool_use":
|
|
430
|
+
yield {
|
|
431
|
+
type: "function",
|
|
432
|
+
id: block.id ?? crypto.randomUUID(),
|
|
433
|
+
name: block.name ?? "",
|
|
434
|
+
arguments: block.input !== void 0 ? JSON.stringify(block.input) : null
|
|
435
|
+
};
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
async *_convertStreamResponse(response) {
|
|
440
|
+
const toolUseBlockIndexes = /* @__PURE__ */ new Set();
|
|
441
|
+
try {
|
|
442
|
+
for await (const event of response) {
|
|
443
|
+
const evt = event;
|
|
444
|
+
const eventType = evt["type"];
|
|
445
|
+
if (eventType === "message_start") {
|
|
446
|
+
const startEvt = evt;
|
|
447
|
+
this._id = startEvt.message.id;
|
|
448
|
+
this._extractUsage(startEvt.message.usage);
|
|
449
|
+
} else if (eventType === "content_block_start") {
|
|
450
|
+
const blockEvt = evt;
|
|
451
|
+
const block = blockEvt.content_block;
|
|
452
|
+
const blockIndex = blockEvt.index;
|
|
453
|
+
switch (block.type) {
|
|
454
|
+
case "text":
|
|
455
|
+
yield {
|
|
456
|
+
type: "text",
|
|
457
|
+
text: block.text
|
|
458
|
+
};
|
|
459
|
+
break;
|
|
460
|
+
case "thinking":
|
|
461
|
+
yield {
|
|
462
|
+
type: "think",
|
|
463
|
+
think: block.thinking
|
|
464
|
+
};
|
|
465
|
+
break;
|
|
466
|
+
case "redacted_thinking":
|
|
467
|
+
yield {
|
|
468
|
+
type: "think",
|
|
469
|
+
think: "",
|
|
470
|
+
encrypted: block.data
|
|
471
|
+
};
|
|
472
|
+
break;
|
|
473
|
+
case "tool_use":
|
|
474
|
+
toolUseBlockIndexes.add(blockIndex);
|
|
475
|
+
yield {
|
|
476
|
+
type: "function",
|
|
477
|
+
id: block.id,
|
|
478
|
+
name: block.name,
|
|
479
|
+
arguments: "",
|
|
480
|
+
_streamIndex: blockIndex
|
|
481
|
+
};
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
} else if (eventType === "content_block_delta") {
|
|
485
|
+
const deltaEvt = evt;
|
|
486
|
+
const delta = deltaEvt.delta;
|
|
487
|
+
const blockIndex = deltaEvt.index;
|
|
488
|
+
switch (delta.type) {
|
|
489
|
+
case "text_delta":
|
|
490
|
+
yield {
|
|
491
|
+
type: "text",
|
|
492
|
+
text: delta.text
|
|
493
|
+
};
|
|
494
|
+
break;
|
|
495
|
+
case "thinking_delta":
|
|
496
|
+
yield {
|
|
497
|
+
type: "think",
|
|
498
|
+
think: delta.thinking
|
|
499
|
+
};
|
|
500
|
+
break;
|
|
501
|
+
case "input_json_delta":
|
|
502
|
+
yield {
|
|
503
|
+
type: "tool_call_part",
|
|
504
|
+
argumentsPart: delta.partial_json,
|
|
505
|
+
index: blockIndex
|
|
506
|
+
};
|
|
507
|
+
break;
|
|
508
|
+
case "signature_delta":
|
|
509
|
+
yield {
|
|
510
|
+
type: "think",
|
|
511
|
+
think: "",
|
|
512
|
+
encrypted: delta.signature
|
|
513
|
+
};
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
} else if (eventType === "content_block_stop") {} else if (eventType === "message_delta") {
|
|
517
|
+
const deltaUsage = evt.usage;
|
|
518
|
+
if (deltaUsage !== void 0) {
|
|
519
|
+
if (typeof deltaUsage["output_tokens"] === "number") this._usage.output = deltaUsage["output_tokens"];
|
|
520
|
+
if (typeof deltaUsage["cache_read_input_tokens"] === "number") this._usage.inputCacheRead = deltaUsage["cache_read_input_tokens"];
|
|
521
|
+
if (typeof deltaUsage["cache_creation_input_tokens"] === "number") this._usage.inputCacheCreation = deltaUsage["cache_creation_input_tokens"];
|
|
522
|
+
if (typeof deltaUsage["input_tokens"] === "number") this._usage.inputOther = deltaUsage["input_tokens"];
|
|
523
|
+
}
|
|
524
|
+
const messageDeltaPayload = evt.delta;
|
|
525
|
+
if (messageDeltaPayload !== void 0 && "stop_reason" in messageDeltaPayload) this._captureStopReason(messageDeltaPayload["stop_reason"]);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
} catch (error) {
|
|
529
|
+
throw convertAnthropicError(error);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
var AnthropicChatProvider = class {
|
|
534
|
+
name = "anthropic";
|
|
535
|
+
_model;
|
|
536
|
+
_stream;
|
|
537
|
+
_client;
|
|
538
|
+
_generationKwargs;
|
|
539
|
+
_metadata;
|
|
540
|
+
_apiKey;
|
|
541
|
+
_baseUrl;
|
|
542
|
+
_defaultHeaders;
|
|
543
|
+
_clientFactory;
|
|
544
|
+
constructor(options) {
|
|
545
|
+
this._model = options.model;
|
|
546
|
+
this._stream = options.stream ?? true;
|
|
547
|
+
this._metadata = options.metadata;
|
|
548
|
+
const apiKey = options.apiKey ?? process.env["ANTHROPIC_API_KEY"];
|
|
549
|
+
this._apiKey = apiKey === void 0 || apiKey.length === 0 ? void 0 : apiKey;
|
|
550
|
+
this._baseUrl = options.baseUrl;
|
|
551
|
+
this._defaultHeaders = options.defaultHeaders;
|
|
552
|
+
this._clientFactory = options.clientFactory;
|
|
553
|
+
this._client = this._apiKey === void 0 ? void 0 : this._buildClient(this._apiKey);
|
|
554
|
+
this._generationKwargs = {
|
|
555
|
+
max_tokens: resolveDefaultMaxTokens(options.model, options.defaultMaxTokens),
|
|
556
|
+
betaFeatures: options.betaFeatures ?? [INTERLEAVED_THINKING_BETA]
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
get modelName() {
|
|
560
|
+
return this._model;
|
|
561
|
+
}
|
|
562
|
+
get thinkingEffort() {
|
|
563
|
+
const thinkingConfig = this._generationKwargs.thinking;
|
|
564
|
+
if (thinkingConfig === void 0 || thinkingConfig === null) return null;
|
|
565
|
+
if (thinkingConfig.type === "disabled") return "off";
|
|
566
|
+
if (thinkingConfig.type === "adaptive") {
|
|
567
|
+
const effort = this._generationKwargs.output_config?.effort;
|
|
568
|
+
if (effort === void 0 || effort === null) return "high";
|
|
569
|
+
switch (effort) {
|
|
570
|
+
case "low":
|
|
571
|
+
case "medium":
|
|
572
|
+
case "high":
|
|
573
|
+
case "xhigh":
|
|
574
|
+
case "max": return effort;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const budget = thinkingConfig.budget_tokens ?? 0;
|
|
578
|
+
if (budget <= 1024) return "low";
|
|
579
|
+
if (budget <= 4096) return "medium";
|
|
580
|
+
return "high";
|
|
581
|
+
}
|
|
582
|
+
get modelParameters() {
|
|
583
|
+
return {
|
|
584
|
+
model: this._model,
|
|
585
|
+
...this._generationKwargs
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
getCapability(model) {
|
|
589
|
+
return getAnthropicModelCapability(model ?? this._model);
|
|
590
|
+
}
|
|
591
|
+
async generate(systemPrompt, tools, history, options) {
|
|
592
|
+
const system = systemPrompt ? [{
|
|
593
|
+
type: "text",
|
|
594
|
+
text: systemPrompt,
|
|
595
|
+
cache_control: CACHE_CONTROL
|
|
596
|
+
}] : void 0;
|
|
597
|
+
const messages = [];
|
|
598
|
+
for (const msg of history) {
|
|
599
|
+
const converted = convertMessage(msg);
|
|
600
|
+
const last = messages.at(-1);
|
|
601
|
+
if (last !== void 0 && isToolResultOnly(last) && isToolResultOnly(converted)) last.content = [...last.content, ...converted.content];
|
|
602
|
+
else messages.push(converted);
|
|
603
|
+
}
|
|
604
|
+
injectCacheControlOnLastBlock(messages);
|
|
605
|
+
const kwargs = {};
|
|
606
|
+
if (this._generationKwargs.max_tokens !== void 0) kwargs["max_tokens"] = this._generationKwargs.max_tokens;
|
|
607
|
+
if (this._generationKwargs.temperature !== void 0) kwargs["temperature"] = this._generationKwargs.temperature;
|
|
608
|
+
if (this._generationKwargs.top_k !== void 0) kwargs["top_k"] = this._generationKwargs.top_k;
|
|
609
|
+
if (this._generationKwargs.top_p !== void 0) kwargs["top_p"] = this._generationKwargs.top_p;
|
|
610
|
+
if (this._generationKwargs.thinking !== void 0) kwargs["thinking"] = this._generationKwargs.thinking;
|
|
611
|
+
if (this._generationKwargs.output_config !== void 0) kwargs["output_config"] = this._generationKwargs.output_config;
|
|
612
|
+
const betas = this._generationKwargs.betaFeatures ?? [];
|
|
613
|
+
const extraHeaders = {};
|
|
614
|
+
if (betas.length > 0) extraHeaders["anthropic-beta"] = betas.join(",");
|
|
615
|
+
const anthropicTools = tools.map((t) => convertTool(t));
|
|
616
|
+
if (anthropicTools.length > 0) {
|
|
617
|
+
const lastTool = anthropicTools.at(-1);
|
|
618
|
+
if (lastTool !== void 0) lastTool.cache_control = CACHE_CONTROL;
|
|
619
|
+
}
|
|
620
|
+
const createParams = {
|
|
621
|
+
model: this._model,
|
|
622
|
+
messages,
|
|
623
|
+
...kwargs
|
|
624
|
+
};
|
|
625
|
+
if (system !== void 0) createParams["system"] = system;
|
|
626
|
+
if (anthropicTools.length > 0) createParams["tools"] = anthropicTools;
|
|
627
|
+
if (this._metadata !== void 0) createParams["metadata"] = this._metadata;
|
|
628
|
+
const requestOptions = {};
|
|
629
|
+
const headers = mergeRequestHeaders(extraHeaders, options?.auth?.headers);
|
|
630
|
+
if (headers !== void 0) requestOptions["headers"] = headers;
|
|
631
|
+
if (options?.signal) requestOptions["signal"] = options.signal;
|
|
632
|
+
const finalRequestOptions = Object.keys(requestOptions).length > 0 ? requestOptions : void 0;
|
|
633
|
+
const client = this._createClient(options?.auth);
|
|
634
|
+
if (this._stream) try {
|
|
635
|
+
return new AnthropicStreamedMessage(await client.messages.create({
|
|
636
|
+
...createParams,
|
|
637
|
+
stream: true
|
|
638
|
+
}, finalRequestOptions), true);
|
|
639
|
+
} catch (error) {
|
|
640
|
+
throw convertAnthropicError(error);
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
return new AnthropicStreamedMessage(await client.messages.create({
|
|
644
|
+
...createParams,
|
|
645
|
+
stream: false
|
|
646
|
+
}, finalRequestOptions), false);
|
|
647
|
+
} catch (error) {
|
|
648
|
+
throw convertAnthropicError(error);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
_createClient(auth) {
|
|
652
|
+
return resolveAuthBackedClient({
|
|
653
|
+
cachedClient: this._client,
|
|
654
|
+
clientFactory: this._clientFactory
|
|
655
|
+
}, auth, (a) => this._buildClient(requireProviderApiKey("AnthropicChatProvider", a, this._apiKey)));
|
|
656
|
+
}
|
|
657
|
+
_buildClient(apiKey) {
|
|
658
|
+
return new Anthropic({
|
|
659
|
+
apiKey,
|
|
660
|
+
baseURL: this._baseUrl,
|
|
661
|
+
defaultHeaders: this._defaultHeaders
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
withThinking(effort) {
|
|
665
|
+
if (effort === "off") {
|
|
666
|
+
let newBetas = [...this._generationKwargs.betaFeatures ?? []];
|
|
667
|
+
if (supportsAdaptiveThinking(this._model)) newBetas = newBetas.filter((b) => b !== INTERLEAVED_THINKING_BETA);
|
|
668
|
+
const clone = this._withGenerationKwargs({
|
|
669
|
+
thinking: { type: "disabled" },
|
|
670
|
+
betaFeatures: newBetas
|
|
671
|
+
});
|
|
672
|
+
delete clone._generationKwargs.output_config;
|
|
673
|
+
return clone;
|
|
674
|
+
}
|
|
675
|
+
const effectiveEffort = clampEffort(effort, this._model);
|
|
676
|
+
if (effectiveEffort === "off") throw new Error("Non-off thinking effort unexpectedly clamped to off.");
|
|
677
|
+
let newBetas = [...this._generationKwargs.betaFeatures ?? []];
|
|
678
|
+
if (supportsAdaptiveThinking(this._model)) {
|
|
679
|
+
newBetas = newBetas.filter((b) => b !== INTERLEAVED_THINKING_BETA);
|
|
680
|
+
return this._withGenerationKwargs({
|
|
681
|
+
thinking: {
|
|
682
|
+
type: "adaptive",
|
|
683
|
+
display: "summarized"
|
|
684
|
+
},
|
|
685
|
+
output_config: { effort: effectiveEffort },
|
|
686
|
+
betaFeatures: newBetas
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
const kwargs = {
|
|
690
|
+
thinking: {
|
|
691
|
+
type: "enabled",
|
|
692
|
+
budget_tokens: budgetTokensForEffort(effectiveEffort)
|
|
693
|
+
},
|
|
694
|
+
betaFeatures: newBetas
|
|
695
|
+
};
|
|
696
|
+
if (supportsEffortParam(this._model)) kwargs.output_config = { effort: effectiveEffort };
|
|
697
|
+
else kwargs.output_config = void 0;
|
|
698
|
+
const clone = this._withGenerationKwargs(kwargs);
|
|
699
|
+
if (!supportsEffortParam(this._model)) delete clone._generationKwargs.output_config;
|
|
700
|
+
return clone;
|
|
701
|
+
}
|
|
702
|
+
withGenerationKwargs(kwargs) {
|
|
703
|
+
return this._withGenerationKwargs(kwargs);
|
|
704
|
+
}
|
|
705
|
+
_withGenerationKwargs(kwargs) {
|
|
706
|
+
const clone = this._clone();
|
|
707
|
+
clone._generationKwargs = {
|
|
708
|
+
...clone._generationKwargs,
|
|
709
|
+
...kwargs
|
|
710
|
+
};
|
|
711
|
+
return clone;
|
|
712
|
+
}
|
|
713
|
+
_clone() {
|
|
714
|
+
const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
715
|
+
clone._generationKwargs = { ...this._generationKwargs };
|
|
716
|
+
return clone;
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
//#endregion
|
|
720
|
+
export { AnthropicChatProvider, convertAnthropicError, resolveDefaultMaxTokens };
|