noumen 0.2.0 → 0.4.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/README.md +95 -16
- package/dist/a2a/index.d.ts +5 -5
- package/dist/a2a/index.js +3 -3
- package/dist/a2a/index.js.map +1 -1
- package/dist/acp/index.d.ts +5 -5
- package/dist/acp/index.js +4 -4
- package/dist/acp/index.js.map +1 -1
- package/dist/{agent-BrkbZyOT.d.ts → agent-1nFVUP9E.d.ts} +319 -15
- package/dist/{cache-DVqaCX8v.d.ts → cache-DsRqxx6v.d.ts} +1 -1
- package/dist/{chunk-BGG2E6JD.js → chunk-3HEYCV26.js} +1 -1
- package/dist/chunk-3SK5GCI6.js +75 -0
- package/dist/chunk-3SK5GCI6.js.map +1 -0
- package/dist/{chunk-NBDFQYUZ.js → chunk-4HW6LN6D.js} +4784 -2411
- package/dist/chunk-4HW6LN6D.js.map +1 -0
- package/dist/{chunk-7ZMN7XJE.js → chunk-5JN4SPI7.js} +6 -6
- package/dist/chunk-5JN4SPI7.js.map +1 -0
- package/dist/{chunk-CPFHEPW4.js → chunk-CS6WNDCF.js} +73 -41
- package/dist/chunk-CS6WNDCF.js.map +1 -0
- package/dist/chunk-EKOGVTBT.js +472 -0
- package/dist/chunk-EKOGVTBT.js.map +1 -0
- package/dist/{chunk-KY6ZPWHO.js → chunk-HEQQQGK5.js} +47 -28
- package/dist/chunk-HEQQQGK5.js.map +1 -0
- package/dist/{chunk-QTJ7VTJY.js → chunk-HL6JCRZJ.js} +1599 -481
- package/dist/chunk-HL6JCRZJ.js.map +1 -0
- package/dist/chunk-L3L3FG5T.js +16 -0
- package/dist/chunk-L3L3FG5T.js.map +1 -0
- package/dist/cli/index.js +36 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/{headless-Q7XHHZIW.js → headless-FFU2DESQ.js} +3 -4
- package/dist/headless-FFU2DESQ.js.map +1 -0
- package/dist/index.d.ts +218 -68
- package/dist/index.js +37 -23
- package/dist/lsp/index.d.ts +4 -4
- package/dist/mcp/index.d.ts +5 -5
- package/dist/mcp/index.js +2 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/{provider-factory-34MSWJZ3.js → provider-factory-KCLIF34X.js} +2 -2
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +5 -3
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts +2 -2
- package/dist/providers/bedrock.js +5 -3
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -1
- package/dist/providers/gemini.js +133 -95
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/ollama.d.ts +13 -0
- package/dist/{ollama-YNXAYP3R.js → providers/ollama.js} +6 -4
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +4 -1
- package/dist/providers/openai.js +2 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +2 -1
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/vertex.d.ts +4 -2
- package/dist/providers/vertex.js +6 -3
- package/dist/providers/vertex.js.map +1 -1
- package/dist/{resolve-XM52G7YE.js → resolve-4JA2BBDA.js} +2 -2
- package/dist/server/index.d.ts +35 -20
- package/dist/server/index.js +276 -207
- package/dist/server/index.js.map +1 -1
- package/dist/{server-Cg1yWGaV.d.ts → server-CHMxuWKq.d.ts} +1 -1
- package/dist/{types-DwdzmXfs.d.ts → types-CD0rUKKT.d.ts} +2 -0
- package/dist/{types-3c88cRKH.d.ts → types-LrU4LRmX.d.ts} +28 -0
- package/dist/{types-CwKKucOF.d.ts → types-RPKUTu1k.d.ts} +27 -2
- package/dist/uuid-RVN2T26F.js +8 -0
- package/dist/uuid-RVN2T26F.js.map +1 -0
- package/dist/zod-7YXKWYMC.js +12 -0
- package/dist/zod-7YXKWYMC.js.map +1 -0
- package/package.json +22 -13
- package/dist/chunk-2ZTGQLYK.js +0 -356
- package/dist/chunk-2ZTGQLYK.js.map +0 -1
- package/dist/chunk-7ZMN7XJE.js.map +0 -1
- package/dist/chunk-CPFHEPW4.js.map +0 -1
- package/dist/chunk-KY6ZPWHO.js.map +0 -1
- package/dist/chunk-NBDFQYUZ.js.map +0 -1
- package/dist/chunk-QTJ7VTJY.js.map +0 -1
- package/dist/headless-Q7XHHZIW.js.map +0 -1
- package/dist/ollama-YNXAYP3R.js.map +0 -1
- /package/dist/{chunk-BGG2E6JD.js.map → chunk-3HEYCV26.js.map} +0 -0
- /package/dist/{provider-factory-34MSWJZ3.js.map → provider-factory-KCLIF34X.js.map} +0 -0
- /package/dist/{resolve-XM52G7YE.js.map → resolve-4JA2BBDA.js.map} +0 -0
package/dist/chunk-2ZTGQLYK.js
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getMaxOutputTokensForModel,
|
|
3
|
-
getMessageCacheBreakpointIndex
|
|
4
|
-
} from "./chunk-KY6ZPWHO.js";
|
|
5
|
-
|
|
6
|
-
// src/providers/anthropic-shared.ts
|
|
7
|
-
function buildCacheControlBlock(config) {
|
|
8
|
-
const cc = { type: "ephemeral" };
|
|
9
|
-
if (config?.ttl) cc.ttl = config.ttl;
|
|
10
|
-
if (config?.scope) cc.scope = config.scope;
|
|
11
|
-
return cc;
|
|
12
|
-
}
|
|
13
|
-
function isCachingEnabled(config) {
|
|
14
|
-
return config?.enabled === true;
|
|
15
|
-
}
|
|
16
|
-
function contentPartsToAnthropic(parts) {
|
|
17
|
-
return parts.map((part) => {
|
|
18
|
-
if (part.type === "text") {
|
|
19
|
-
return { type: "text", text: part.text };
|
|
20
|
-
}
|
|
21
|
-
if (part.type === "image") {
|
|
22
|
-
return {
|
|
23
|
-
type: "image",
|
|
24
|
-
source: {
|
|
25
|
-
type: "base64",
|
|
26
|
-
media_type: part.media_type,
|
|
27
|
-
data: part.data
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
type: "image",
|
|
33
|
-
source: { type: "url", url: part.url }
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
function buildAnthropicTools(params, cacheConfig) {
|
|
38
|
-
if (!params.tools) return void 0;
|
|
39
|
-
const tools = params.tools.map((t) => ({
|
|
40
|
-
name: t.function.name,
|
|
41
|
-
description: t.function.description,
|
|
42
|
-
input_schema: t.function.parameters
|
|
43
|
-
}));
|
|
44
|
-
if (isCachingEnabled(cacheConfig) && tools.length > 0) {
|
|
45
|
-
const lastTool = tools[tools.length - 1];
|
|
46
|
-
lastTool.cache_control = buildCacheControlBlock(cacheConfig);
|
|
47
|
-
}
|
|
48
|
-
return tools;
|
|
49
|
-
}
|
|
50
|
-
function buildAnthropicSystemBlocks(systemPrompt, cacheConfig) {
|
|
51
|
-
if (!systemPrompt) return void 0;
|
|
52
|
-
if (!isCachingEnabled(cacheConfig)) return systemPrompt;
|
|
53
|
-
return [
|
|
54
|
-
{
|
|
55
|
-
type: "text",
|
|
56
|
-
text: systemPrompt,
|
|
57
|
-
cache_control: buildCacheControlBlock(cacheConfig)
|
|
58
|
-
}
|
|
59
|
-
];
|
|
60
|
-
}
|
|
61
|
-
function convertAnthropicMessages(systemPrompt, messages, cacheConfig, skipCacheWrite) {
|
|
62
|
-
const result = [];
|
|
63
|
-
const caching = isCachingEnabled(cacheConfig);
|
|
64
|
-
const cacheBreakpointIdx = caching ? getMessageCacheBreakpointIndex(messages, skipCacheWrite) : -1;
|
|
65
|
-
for (let mi = 0; mi < messages.length; mi++) {
|
|
66
|
-
const msg = messages[mi];
|
|
67
|
-
const addCache = mi === cacheBreakpointIdx;
|
|
68
|
-
if (msg.role === "system") continue;
|
|
69
|
-
if (msg.role === "user") {
|
|
70
|
-
const isMultipart = Array.isArray(msg.content);
|
|
71
|
-
if (addCache && caching) {
|
|
72
|
-
const blocks = isMultipart ? contentPartsToAnthropic(msg.content) : [{ type: "text", text: msg.content }];
|
|
73
|
-
const lastBlock = blocks[blocks.length - 1];
|
|
74
|
-
lastBlock.cache_control = buildCacheControlBlock(cacheConfig);
|
|
75
|
-
result.push({ role: "user", content: blocks });
|
|
76
|
-
} else if (isMultipart) {
|
|
77
|
-
result.push({
|
|
78
|
-
role: "user",
|
|
79
|
-
content: contentPartsToAnthropic(msg.content)
|
|
80
|
-
});
|
|
81
|
-
} else {
|
|
82
|
-
result.push({ role: "user", content: msg.content });
|
|
83
|
-
}
|
|
84
|
-
} else if (msg.role === "assistant") {
|
|
85
|
-
const content = [];
|
|
86
|
-
if (msg.content) {
|
|
87
|
-
content.push({ type: "text", text: msg.content });
|
|
88
|
-
}
|
|
89
|
-
if (msg.tool_calls) {
|
|
90
|
-
for (const tc of msg.tool_calls) {
|
|
91
|
-
let input = {};
|
|
92
|
-
try {
|
|
93
|
-
input = JSON.parse(tc.function.arguments);
|
|
94
|
-
} catch {
|
|
95
|
-
}
|
|
96
|
-
content.push({
|
|
97
|
-
type: "tool_use",
|
|
98
|
-
id: tc.id,
|
|
99
|
-
name: tc.function.name,
|
|
100
|
-
input
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (content.length === 0) {
|
|
105
|
-
content.push({ type: "text", text: "" });
|
|
106
|
-
}
|
|
107
|
-
if (addCache && caching && content.length > 0) {
|
|
108
|
-
const lastBlock = content[content.length - 1];
|
|
109
|
-
lastBlock.cache_control = buildCacheControlBlock(cacheConfig);
|
|
110
|
-
}
|
|
111
|
-
result.push({ role: "assistant", content });
|
|
112
|
-
} else if (msg.role === "tool") {
|
|
113
|
-
const isMultipart = Array.isArray(msg.content);
|
|
114
|
-
const toolContent = isMultipart ? contentPartsToAnthropic(msg.content) : msg.content;
|
|
115
|
-
const toolResultBlock = {
|
|
116
|
-
type: "tool_result",
|
|
117
|
-
tool_use_id: msg.tool_call_id,
|
|
118
|
-
content: toolContent
|
|
119
|
-
};
|
|
120
|
-
if (addCache && caching) {
|
|
121
|
-
toolResultBlock.cache_control = buildCacheControlBlock(cacheConfig);
|
|
122
|
-
}
|
|
123
|
-
const prev = result[result.length - 1];
|
|
124
|
-
if (prev && prev.role === "user" && Array.isArray(prev.content)) {
|
|
125
|
-
const blocks = prev.content;
|
|
126
|
-
if (blocks.length > 0 && blocks[0].type === "tool_result") {
|
|
127
|
-
blocks.push(toolResultBlock);
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
result.push({ role: "user", content: [toolResultBlock] });
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
system: buildAnthropicSystemBlocks(systemPrompt, cacheConfig),
|
|
136
|
-
messages: result
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function makeChunk(id, model, delta) {
|
|
140
|
-
return {
|
|
141
|
-
id,
|
|
142
|
-
model,
|
|
143
|
-
choices: [
|
|
144
|
-
{
|
|
145
|
-
index: 0,
|
|
146
|
-
delta,
|
|
147
|
-
finish_reason: null
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
async function* streamAnthropicChat(client, params, defaultModel, cacheConfig) {
|
|
153
|
-
const { system, messages: inputMessages } = convertAnthropicMessages(
|
|
154
|
-
params.system,
|
|
155
|
-
params.messages,
|
|
156
|
-
cacheConfig,
|
|
157
|
-
params.skipCacheWrite
|
|
158
|
-
);
|
|
159
|
-
const tools = buildAnthropicTools(params, cacheConfig);
|
|
160
|
-
const thinkingEnabled = params.thinking?.type === "enabled" && params.thinking.budgetTokens > 0;
|
|
161
|
-
const budgetTokens = thinkingEnabled ? params.thinking.budgetTokens : 0;
|
|
162
|
-
const model = params.model ?? defaultModel;
|
|
163
|
-
const modelMaxOutput = getMaxOutputTokensForModel(model);
|
|
164
|
-
const maxOutputTokens = thinkingEnabled ? Math.min(budgetTokens + (params.max_tokens ?? 8192), modelMaxOutput) : params.max_tokens ?? 8192;
|
|
165
|
-
const clampedBudget = thinkingEnabled ? Math.min(budgetTokens, maxOutputTokens - 1) : 0;
|
|
166
|
-
const streamParams = {
|
|
167
|
-
model,
|
|
168
|
-
max_tokens: maxOutputTokens,
|
|
169
|
-
system,
|
|
170
|
-
messages: inputMessages,
|
|
171
|
-
tools
|
|
172
|
-
};
|
|
173
|
-
if (!thinkingEnabled && params.temperature !== void 0) {
|
|
174
|
-
streamParams.temperature = params.temperature;
|
|
175
|
-
}
|
|
176
|
-
if (thinkingEnabled) {
|
|
177
|
-
streamParams.thinking = {
|
|
178
|
-
type: "enabled",
|
|
179
|
-
budget_tokens: clampedBudget
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
if (params.outputFormat?.type === "json_schema") {
|
|
183
|
-
streamParams.output_config = {
|
|
184
|
-
format: {
|
|
185
|
-
type: "json_schema",
|
|
186
|
-
schema: params.outputFormat.schema
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
const betas = streamParams.betas ?? [];
|
|
190
|
-
if (!betas.includes("structured-outputs-2025-12-15")) {
|
|
191
|
-
betas.push("structured-outputs-2025-12-15");
|
|
192
|
-
}
|
|
193
|
-
streamParams.betas = betas;
|
|
194
|
-
} else if (params.outputFormat?.type === "json_object") {
|
|
195
|
-
const hint = "\n\nYou MUST respond with valid JSON only. No markdown, no explanation \u2014 just a single JSON object.";
|
|
196
|
-
if (typeof streamParams.system === "string") {
|
|
197
|
-
streamParams.system = streamParams.system + hint;
|
|
198
|
-
} else if (Array.isArray(streamParams.system)) {
|
|
199
|
-
const blocks = streamParams.system;
|
|
200
|
-
if (blocks.length > 0) {
|
|
201
|
-
const last = blocks[blocks.length - 1];
|
|
202
|
-
if (last.type === "text" && typeof last.text === "string") {
|
|
203
|
-
last.text = last.text + hint;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
} else if (!streamParams.system) {
|
|
207
|
-
streamParams.system = hint.trim();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
const stream = client.messages.stream(streamParams);
|
|
211
|
-
let chunkIndex = 0;
|
|
212
|
-
const toolIndexMap = /* @__PURE__ */ new Map();
|
|
213
|
-
const blockIndexToToolId = /* @__PURE__ */ new Map();
|
|
214
|
-
const blockIndexToType = /* @__PURE__ */ new Map();
|
|
215
|
-
let nextToolIndex = 0;
|
|
216
|
-
let inputTokens = 0;
|
|
217
|
-
let outputTokens = 0;
|
|
218
|
-
let cacheReadTokens = 0;
|
|
219
|
-
let cacheCreationTokens = 0;
|
|
220
|
-
let stopReason;
|
|
221
|
-
for await (const event of stream) {
|
|
222
|
-
const ev = event;
|
|
223
|
-
const chunkId = `chatcmpl-${chunkIndex++}`;
|
|
224
|
-
if (ev.type === "message_start") {
|
|
225
|
-
const msg = ev.message ?? {};
|
|
226
|
-
const usage = msg.usage;
|
|
227
|
-
if (usage) {
|
|
228
|
-
inputTokens = usage.input_tokens ?? 0;
|
|
229
|
-
outputTokens = usage.output_tokens ?? 0;
|
|
230
|
-
cacheReadTokens = usage.cache_read_input_tokens ?? 0;
|
|
231
|
-
cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
|
|
232
|
-
}
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
if (ev.type === "message_delta") {
|
|
236
|
-
const delta = ev.delta;
|
|
237
|
-
if (delta?.stop_reason) {
|
|
238
|
-
stopReason = delta.stop_reason;
|
|
239
|
-
}
|
|
240
|
-
const usage = ev.usage;
|
|
241
|
-
if (usage?.output_tokens) {
|
|
242
|
-
outputTokens = usage.output_tokens;
|
|
243
|
-
}
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
if (ev.type === "content_block_start") {
|
|
247
|
-
const block = ev.content_block ?? {};
|
|
248
|
-
const blockIndex = ev.index;
|
|
249
|
-
if (blockIndex !== void 0) {
|
|
250
|
-
blockIndexToType.set(blockIndex, block.type);
|
|
251
|
-
}
|
|
252
|
-
if (block.type === "thinking") {
|
|
253
|
-
yield makeChunk(chunkId, model, { thinking_content: "" });
|
|
254
|
-
} else if (block.type === "text") {
|
|
255
|
-
yield makeChunk(chunkId, model, { content: "" });
|
|
256
|
-
} else if (block.type === "tool_use") {
|
|
257
|
-
const toolBlock = block;
|
|
258
|
-
const idx = nextToolIndex++;
|
|
259
|
-
toolIndexMap.set(toolBlock.id, idx);
|
|
260
|
-
if (blockIndex !== void 0) {
|
|
261
|
-
blockIndexToToolId.set(blockIndex, toolBlock.id);
|
|
262
|
-
}
|
|
263
|
-
yield makeChunk(chunkId, model, {
|
|
264
|
-
tool_calls: [
|
|
265
|
-
{
|
|
266
|
-
index: idx,
|
|
267
|
-
id: toolBlock.id,
|
|
268
|
-
type: "function",
|
|
269
|
-
function: { name: toolBlock.name, arguments: "" }
|
|
270
|
-
}
|
|
271
|
-
]
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
} else if (ev.type === "content_block_delta") {
|
|
275
|
-
const delta = ev.delta;
|
|
276
|
-
const deltaType = delta.type;
|
|
277
|
-
const blockIndex = ev.index;
|
|
278
|
-
if (deltaType === "thinking_delta") {
|
|
279
|
-
yield makeChunk(chunkId, model, {
|
|
280
|
-
thinking_content: delta.thinking
|
|
281
|
-
});
|
|
282
|
-
} else if (deltaType === "text_delta") {
|
|
283
|
-
yield makeChunk(chunkId, model, {
|
|
284
|
-
content: delta.text
|
|
285
|
-
});
|
|
286
|
-
} else if (deltaType === "signature_delta") {
|
|
287
|
-
if (blockIndex !== void 0 && blockIndexToType.get(blockIndex) === "thinking") {
|
|
288
|
-
yield makeChunk(chunkId, model, {
|
|
289
|
-
thinking_signature: delta.signature
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
} else if (deltaType === "input_json_delta") {
|
|
293
|
-
let toolId;
|
|
294
|
-
if (blockIndex !== void 0) {
|
|
295
|
-
toolId = blockIndexToToolId.get(blockIndex);
|
|
296
|
-
}
|
|
297
|
-
if (!toolId) {
|
|
298
|
-
toolId = Array.from(toolIndexMap.keys()).pop();
|
|
299
|
-
}
|
|
300
|
-
if (toolId) {
|
|
301
|
-
const idx = toolIndexMap.get(toolId);
|
|
302
|
-
yield makeChunk(chunkId, model, {
|
|
303
|
-
tool_calls: [
|
|
304
|
-
{
|
|
305
|
-
index: idx,
|
|
306
|
-
function: { arguments: delta.partial_json }
|
|
307
|
-
}
|
|
308
|
-
]
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
} else if (ev.type === "message_stop") {
|
|
313
|
-
let finishReason;
|
|
314
|
-
switch (stopReason) {
|
|
315
|
-
case "end_turn":
|
|
316
|
-
finishReason = "stop";
|
|
317
|
-
break;
|
|
318
|
-
case "tool_use":
|
|
319
|
-
finishReason = "tool_calls";
|
|
320
|
-
break;
|
|
321
|
-
case "max_tokens":
|
|
322
|
-
finishReason = "length";
|
|
323
|
-
break;
|
|
324
|
-
case "stop_sequence":
|
|
325
|
-
finishReason = "stop";
|
|
326
|
-
break;
|
|
327
|
-
default:
|
|
328
|
-
finishReason = toolIndexMap.size > 0 ? "tool_calls" : "stop";
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
yield {
|
|
332
|
-
id: chunkId,
|
|
333
|
-
model,
|
|
334
|
-
choices: [
|
|
335
|
-
{
|
|
336
|
-
index: 0,
|
|
337
|
-
delta: {},
|
|
338
|
-
finish_reason: finishReason
|
|
339
|
-
}
|
|
340
|
-
],
|
|
341
|
-
usage: {
|
|
342
|
-
prompt_tokens: inputTokens,
|
|
343
|
-
completion_tokens: outputTokens,
|
|
344
|
-
total_tokens: inputTokens + outputTokens,
|
|
345
|
-
cache_read_tokens: cacheReadTokens || void 0,
|
|
346
|
-
cache_creation_tokens: cacheCreationTokens || void 0
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
export {
|
|
354
|
-
streamAnthropicChat
|
|
355
|
-
};
|
|
356
|
-
//# sourceMappingURL=chunk-2ZTGQLYK.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/anthropic-shared.ts"],"sourcesContent":["/**\n * Shared Anthropic streaming, message conversion, and tool mapping logic.\n *\n * Used by AnthropicProvider, BedrockAnthropicProvider, and VertexAnthropicProvider.\n * Accepts a generic client shape so it works with all three SDKs without\n * importing any of them directly.\n */\n\nimport type { ChatParams, ChatStreamChunk } from \"./types.js\";\nimport type { ChatMessage, ContentPart } from \"../session/types.js\";\nimport type { CacheControlConfig } from \"./cache.js\";\nimport { getMessageCacheBreakpointIndex } from \"./cache.js\";\nimport { getMaxOutputTokensForModel } from \"../utils/context.js\";\n\ninterface AnthropicToolUseBlock {\n type: \"tool_use\";\n id: string;\n name: string;\n input: Record<string, unknown>;\n}\n\ntype CacheControlBlock = {\n type: \"ephemeral\";\n ttl?: \"1h\";\n scope?: \"global\" | \"org\";\n};\n\nexport interface AnthropicStreamClient {\n messages: {\n stream(params: Record<string, unknown>): AsyncIterable<Record<string, unknown>>;\n };\n}\n\nexport function buildCacheControlBlock(\n config: CacheControlConfig | undefined,\n): CacheControlBlock {\n const cc: CacheControlBlock = { type: \"ephemeral\" };\n if (config?.ttl) cc.ttl = config.ttl;\n if (config?.scope) cc.scope = config.scope;\n return cc;\n}\n\nfunction isCachingEnabled(config: CacheControlConfig | undefined): boolean {\n return config?.enabled === true;\n}\n\nexport function contentPartsToAnthropic(\n parts: ContentPart[],\n): Record<string, unknown>[] {\n return parts.map((part) => {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n if (part.type === \"image\") {\n return {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: part.media_type,\n data: part.data,\n },\n };\n }\n return {\n type: \"image\",\n source: { type: \"url\", url: part.url },\n };\n });\n}\n\nexport function buildAnthropicTools(\n params: ChatParams,\n cacheConfig?: CacheControlConfig,\n): Record<string, unknown>[] | undefined {\n if (!params.tools) return undefined;\n\n const tools = params.tools.map((t) => ({\n name: t.function.name,\n description: t.function.description,\n input_schema: t.function.parameters,\n }));\n\n if (isCachingEnabled(cacheConfig) && tools.length > 0) {\n const lastTool = tools[tools.length - 1] as Record<string, unknown>;\n lastTool.cache_control = buildCacheControlBlock(cacheConfig);\n }\n\n return tools;\n}\n\nexport function buildAnthropicSystemBlocks(\n systemPrompt: string | undefined,\n cacheConfig?: CacheControlConfig,\n): unknown {\n if (!systemPrompt) return undefined;\n if (!isCachingEnabled(cacheConfig)) return systemPrompt;\n\n return [\n {\n type: \"text\",\n text: systemPrompt,\n cache_control: buildCacheControlBlock(cacheConfig),\n },\n ];\n}\n\nexport function convertAnthropicMessages(\n systemPrompt: string | undefined,\n messages: ChatMessage[],\n cacheConfig?: CacheControlConfig,\n skipCacheWrite?: boolean,\n): {\n system: unknown;\n messages: Record<string, unknown>[];\n} {\n const result: Record<string, unknown>[] = [];\n const caching = isCachingEnabled(cacheConfig);\n const cacheBreakpointIdx = caching\n ? getMessageCacheBreakpointIndex(messages, skipCacheWrite)\n : -1;\n\n for (let mi = 0; mi < messages.length; mi++) {\n const msg = messages[mi];\n const addCache = mi === cacheBreakpointIdx;\n\n if (msg.role === \"system\") continue;\n\n if (msg.role === \"user\") {\n const isMultipart = Array.isArray(msg.content);\n if (addCache && caching) {\n const blocks = isMultipart\n ? contentPartsToAnthropic(msg.content as ContentPart[])\n : [{ type: \"text\", text: msg.content as string }];\n const lastBlock = blocks[blocks.length - 1] as Record<string, unknown>;\n lastBlock.cache_control = buildCacheControlBlock(cacheConfig);\n result.push({ role: \"user\", content: blocks });\n } else if (isMultipart) {\n result.push({\n role: \"user\",\n content: contentPartsToAnthropic(msg.content as ContentPart[]),\n });\n } else {\n result.push({ role: \"user\", content: msg.content as string });\n }\n } else if (msg.role === \"assistant\") {\n const content: Record<string, unknown>[] = [];\n if (msg.content) {\n content.push({ type: \"text\", text: msg.content });\n }\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n let input: Record<string, unknown> = {};\n try {\n input = JSON.parse(tc.function.arguments);\n } catch {\n // malformed JSON from truncated stream — send empty input\n }\n content.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input,\n });\n }\n }\n if (content.length === 0) {\n content.push({ type: \"text\", text: \"\" });\n }\n if (addCache && caching && content.length > 0) {\n const lastBlock = content[content.length - 1] as Record<string, unknown>;\n lastBlock.cache_control = buildCacheControlBlock(cacheConfig);\n }\n result.push({ role: \"assistant\", content });\n } else if (msg.role === \"tool\") {\n const isMultipart = Array.isArray(msg.content);\n const toolContent = isMultipart\n ? contentPartsToAnthropic(msg.content as ContentPart[])\n : (msg.content as string);\n\n const toolResultBlock: Record<string, unknown> = {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id,\n content: toolContent,\n };\n if (addCache && caching) {\n toolResultBlock.cache_control = buildCacheControlBlock(cacheConfig);\n }\n\n const prev = result[result.length - 1];\n if (prev && prev.role === \"user\" && Array.isArray(prev.content)) {\n const blocks = prev.content as Record<string, unknown>[];\n if (blocks.length > 0 && blocks[0].type === \"tool_result\") {\n blocks.push(toolResultBlock);\n continue;\n }\n }\n result.push({ role: \"user\", content: [toolResultBlock] });\n }\n }\n\n return {\n system: buildAnthropicSystemBlocks(systemPrompt, cacheConfig),\n messages: result,\n };\n}\n\nfunction makeChunk(\n id: string,\n model: string,\n delta: Record<string, unknown>,\n): ChatStreamChunk {\n return {\n id,\n model,\n choices: [\n {\n index: 0,\n delta: delta as ChatStreamChunk[\"choices\"][0][\"delta\"],\n finish_reason: null,\n },\n ],\n };\n}\n\n/**\n * Stream an Anthropic-compatible chat call and yield OpenAI-shaped ChatStreamChunks.\n * Works with Anthropic, AnthropicBedrock, and AnthropicVertex clients.\n */\nexport async function* streamAnthropicChat(\n client: AnthropicStreamClient,\n params: ChatParams,\n defaultModel: string,\n cacheConfig?: CacheControlConfig,\n): AsyncIterable<ChatStreamChunk> {\n const { system, messages: inputMessages } = convertAnthropicMessages(\n params.system,\n params.messages,\n cacheConfig,\n params.skipCacheWrite,\n );\n\n const tools = buildAnthropicTools(params, cacheConfig);\n\n const thinkingEnabled =\n params.thinking?.type === \"enabled\" &&\n (params.thinking as { budgetTokens: number }).budgetTokens > 0;\n const budgetTokens = thinkingEnabled\n ? (params.thinking as { type: \"enabled\"; budgetTokens: number }).budgetTokens\n : 0;\n\n const model = params.model ?? defaultModel;\n\n const modelMaxOutput = getMaxOutputTokensForModel(model);\n const maxOutputTokens = thinkingEnabled\n ? Math.min(budgetTokens + (params.max_tokens ?? 8192), modelMaxOutput)\n : (params.max_tokens ?? 8192);\n const clampedBudget = thinkingEnabled\n ? Math.min(budgetTokens, maxOutputTokens - 1)\n : 0;\n\n const streamParams: Record<string, unknown> = {\n model,\n max_tokens: maxOutputTokens,\n system,\n messages: inputMessages,\n tools,\n };\n\n if (!thinkingEnabled && params.temperature !== undefined) {\n streamParams.temperature = params.temperature;\n }\n\n if (thinkingEnabled) {\n streamParams.thinking = {\n type: \"enabled\",\n budget_tokens: clampedBudget,\n };\n }\n\n if (params.outputFormat?.type === \"json_schema\") {\n streamParams.output_config = {\n format: {\n type: \"json_schema\",\n schema: params.outputFormat.schema,\n },\n };\n const betas: string[] = (streamParams.betas as string[] | undefined) ?? [];\n if (!betas.includes(\"structured-outputs-2025-12-15\")) {\n betas.push(\"structured-outputs-2025-12-15\");\n }\n streamParams.betas = betas;\n } else if (params.outputFormat?.type === \"json_object\") {\n // Anthropic has no native json_object mode. Prepend a system-level hint\n // so the model knows to produce valid JSON.\n const hint = \"\\n\\nYou MUST respond with valid JSON only. No markdown, no explanation — just a single JSON object.\";\n if (typeof streamParams.system === \"string\") {\n streamParams.system = streamParams.system + hint;\n } else if (Array.isArray(streamParams.system)) {\n const blocks = streamParams.system as Array<Record<string, unknown>>;\n if (blocks.length > 0) {\n const last = blocks[blocks.length - 1];\n if (last.type === \"text\" && typeof last.text === \"string\") {\n last.text = last.text + hint;\n }\n }\n } else if (!streamParams.system) {\n streamParams.system = hint.trim();\n }\n }\n\n const stream = client.messages.stream(streamParams);\n\n let chunkIndex = 0;\n const toolIndexMap = new Map<string, number>();\n const blockIndexToToolId = new Map<number, string>();\n const blockIndexToType = new Map<number, string>();\n let nextToolIndex = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let cacheReadTokens = 0;\n let cacheCreationTokens = 0;\n let stopReason: string | undefined;\n\n for await (const event of stream) {\n const ev = event as Record<string, unknown>;\n const chunkId = `chatcmpl-${chunkIndex++}`;\n\n if (ev.type === \"message_start\") {\n const msg = (ev.message as Record<string, unknown>) ?? {};\n const usage = msg.usage as Record<string, unknown> | undefined;\n if (usage) {\n inputTokens = (usage.input_tokens as number) ?? 0;\n outputTokens = (usage.output_tokens as number) ?? 0;\n cacheReadTokens = (usage.cache_read_input_tokens as number) ?? 0;\n cacheCreationTokens = (usage.cache_creation_input_tokens as number) ?? 0;\n }\n continue;\n }\n\n if (ev.type === \"message_delta\") {\n const delta = (ev as Record<string, unknown>).delta as Record<string, unknown> | undefined;\n if (delta?.stop_reason) {\n stopReason = delta.stop_reason as string;\n }\n const usage = (ev as Record<string, unknown>).usage as\n | Record<string, unknown>\n | undefined;\n if (usage?.output_tokens) {\n outputTokens = usage.output_tokens as number;\n }\n continue;\n }\n\n if (ev.type === \"content_block_start\") {\n const block = (ev.content_block as Record<string, unknown>) ?? {};\n const blockIndex = ev.index as number | undefined;\n if (blockIndex !== undefined) {\n blockIndexToType.set(blockIndex, block.type as string);\n }\n\n if (block.type === \"thinking\") {\n yield makeChunk(chunkId, model, { thinking_content: \"\" });\n } else if (block.type === \"text\") {\n yield makeChunk(chunkId, model, { content: \"\" });\n } else if (block.type === \"tool_use\") {\n const toolBlock = block as unknown as AnthropicToolUseBlock;\n const idx = nextToolIndex++;\n toolIndexMap.set(toolBlock.id, idx);\n if (blockIndex !== undefined) {\n blockIndexToToolId.set(blockIndex, toolBlock.id);\n }\n yield makeChunk(chunkId, model, {\n tool_calls: [\n {\n index: idx,\n id: toolBlock.id,\n type: \"function\",\n function: { name: toolBlock.name, arguments: \"\" },\n },\n ],\n });\n }\n } else if (ev.type === \"content_block_delta\") {\n const delta = ev.delta as Record<string, unknown>;\n const deltaType = delta.type;\n const blockIndex = ev.index as number | undefined;\n\n if (deltaType === \"thinking_delta\") {\n yield makeChunk(chunkId, model, {\n thinking_content: delta.thinking as string,\n });\n } else if (deltaType === \"text_delta\") {\n yield makeChunk(chunkId, model, {\n content: delta.text as string,\n });\n } else if (deltaType === \"signature_delta\") {\n if (blockIndex !== undefined && blockIndexToType.get(blockIndex) === \"thinking\") {\n yield makeChunk(chunkId, model, {\n thinking_signature: delta.signature as string,\n });\n }\n } else if (deltaType === \"input_json_delta\") {\n let toolId: string | undefined;\n if (blockIndex !== undefined) {\n toolId = blockIndexToToolId.get(blockIndex);\n }\n if (!toolId) {\n toolId = Array.from(toolIndexMap.keys()).pop();\n }\n if (toolId) {\n const idx = toolIndexMap.get(toolId)!;\n yield makeChunk(chunkId, model, {\n tool_calls: [\n {\n index: idx,\n function: { arguments: delta.partial_json as string },\n },\n ],\n });\n }\n }\n } else if (ev.type === \"message_stop\") {\n let finishReason: string;\n switch (stopReason) {\n case \"end_turn\": finishReason = \"stop\"; break;\n case \"tool_use\": finishReason = \"tool_calls\"; break;\n case \"max_tokens\": finishReason = \"length\"; break;\n case \"stop_sequence\": finishReason = \"stop\"; break;\n default: finishReason = toolIndexMap.size > 0 ? \"tool_calls\" : \"stop\"; break;\n }\n yield {\n id: chunkId,\n model,\n choices: [\n {\n index: 0,\n delta: {},\n finish_reason: finishReason,\n },\n ],\n usage: {\n prompt_tokens: inputTokens,\n completion_tokens: outputTokens,\n total_tokens: inputTokens + outputTokens,\n cache_read_tokens: cacheReadTokens || undefined,\n cache_creation_tokens: cacheCreationTokens || undefined,\n },\n };\n }\n }\n}\n"],"mappings":";;;;;;AAiCO,SAAS,uBACd,QACmB;AACnB,QAAM,KAAwB,EAAE,MAAM,YAAY;AAClD,MAAI,QAAQ,IAAK,IAAG,MAAM,OAAO;AACjC,MAAI,QAAQ,MAAO,IAAG,QAAQ,OAAO;AACrC,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAiD;AACzE,SAAO,QAAQ,YAAY;AAC7B;AAEO,SAAS,wBACd,OAC2B;AAC3B,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,QAAQ;AACxB,aAAO,EAAE,MAAM,QAAQ,MAAM,KAAK,KAAK;AAAA,IACzC;AACA,QAAI,KAAK,SAAS,SAAS;AACzB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,YAAY,KAAK;AAAA,UACjB,MAAM,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,EAAE,MAAM,OAAO,KAAK,KAAK,IAAI;AAAA,IACvC;AAAA,EACF,CAAC;AACH;AAEO,SAAS,oBACd,QACA,aACuC;AACvC,MAAI,CAAC,OAAO,MAAO,QAAO;AAE1B,QAAM,QAAQ,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,IACrC,MAAM,EAAE,SAAS;AAAA,IACjB,aAAa,EAAE,SAAS;AAAA,IACxB,cAAc,EAAE,SAAS;AAAA,EAC3B,EAAE;AAEF,MAAI,iBAAiB,WAAW,KAAK,MAAM,SAAS,GAAG;AACrD,UAAM,WAAW,MAAM,MAAM,SAAS,CAAC;AACvC,aAAS,gBAAgB,uBAAuB,WAAW;AAAA,EAC7D;AAEA,SAAO;AACT;AAEO,SAAS,2BACd,cACA,aACS;AACT,MAAI,CAAC,aAAc,QAAO;AAC1B,MAAI,CAAC,iBAAiB,WAAW,EAAG,QAAO;AAE3C,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,eAAe,uBAAuB,WAAW;AAAA,IACnD;AAAA,EACF;AACF;AAEO,SAAS,yBACd,cACA,UACA,aACA,gBAIA;AACA,QAAM,SAAoC,CAAC;AAC3C,QAAM,UAAU,iBAAiB,WAAW;AAC5C,QAAM,qBAAqB,UACvB,+BAA+B,UAAU,cAAc,IACvD;AAEJ,WAAS,KAAK,GAAG,KAAK,SAAS,QAAQ,MAAM;AAC3C,UAAM,MAAM,SAAS,EAAE;AACvB,UAAM,WAAW,OAAO;AAExB,QAAI,IAAI,SAAS,SAAU;AAE3B,QAAI,IAAI,SAAS,QAAQ;AACvB,YAAM,cAAc,MAAM,QAAQ,IAAI,OAAO;AAC7C,UAAI,YAAY,SAAS;AACvB,cAAM,SAAS,cACX,wBAAwB,IAAI,OAAwB,IACpD,CAAC,EAAE,MAAM,QAAQ,MAAM,IAAI,QAAkB,CAAC;AAClD,cAAM,YAAY,OAAO,OAAO,SAAS,CAAC;AAC1C,kBAAU,gBAAgB,uBAAuB,WAAW;AAC5D,eAAO,KAAK,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,MAC/C,WAAW,aAAa;AACtB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,SAAS,wBAAwB,IAAI,OAAwB;AAAA,QAC/D,CAAC;AAAA,MACH,OAAO;AACL,eAAO,KAAK,EAAE,MAAM,QAAQ,SAAS,IAAI,QAAkB,CAAC;AAAA,MAC9D;AAAA,IACF,WAAW,IAAI,SAAS,aAAa;AACnC,YAAM,UAAqC,CAAC;AAC5C,UAAI,IAAI,SAAS;AACf,gBAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,IAAI,QAAQ,CAAC;AAAA,MAClD;AACA,UAAI,IAAI,YAAY;AAClB,mBAAW,MAAM,IAAI,YAAY;AAC/B,cAAI,QAAiC,CAAC;AACtC,cAAI;AACF,oBAAQ,KAAK,MAAM,GAAG,SAAS,SAAS;AAAA,UAC1C,QAAQ;AAAA,UAER;AACA,kBAAQ,KAAK;AAAA,YACX,MAAM;AAAA,YACN,IAAI,GAAG;AAAA,YACP,MAAM,GAAG,SAAS;AAAA,YAClB;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AACA,UAAI,QAAQ,WAAW,GAAG;AACxB,gBAAQ,KAAK,EAAE,MAAM,QAAQ,MAAM,GAAG,CAAC;AAAA,MACzC;AACA,UAAI,YAAY,WAAW,QAAQ,SAAS,GAAG;AAC7C,cAAM,YAAY,QAAQ,QAAQ,SAAS,CAAC;AAC5C,kBAAU,gBAAgB,uBAAuB,WAAW;AAAA,MAC9D;AACA,aAAO,KAAK,EAAE,MAAM,aAAa,QAAQ,CAAC;AAAA,IAC5C,WAAW,IAAI,SAAS,QAAQ;AAC9B,YAAM,cAAc,MAAM,QAAQ,IAAI,OAAO;AAC7C,YAAM,cAAc,cAChB,wBAAwB,IAAI,OAAwB,IACnD,IAAI;AAET,YAAM,kBAA2C;AAAA,QAC/C,MAAM;AAAA,QACN,aAAa,IAAI;AAAA,QACjB,SAAS;AAAA,MACX;AACA,UAAI,YAAY,SAAS;AACvB,wBAAgB,gBAAgB,uBAAuB,WAAW;AAAA,MACpE;AAEA,YAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,UAAI,QAAQ,KAAK,SAAS,UAAU,MAAM,QAAQ,KAAK,OAAO,GAAG;AAC/D,cAAM,SAAS,KAAK;AACpB,YAAI,OAAO,SAAS,KAAK,OAAO,CAAC,EAAE,SAAS,eAAe;AACzD,iBAAO,KAAK,eAAe;AAC3B;AAAA,QACF;AAAA,MACF;AACA,aAAO,KAAK,EAAE,MAAM,QAAQ,SAAS,CAAC,eAAe,EAAE,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,2BAA2B,cAAc,WAAW;AAAA,IAC5D,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,UACP,IACA,OACA,OACiB;AACjB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP;AAAA,QACE,OAAO;AAAA,QACP;AAAA,QACA,eAAe;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACF;AAMA,gBAAuB,oBACrB,QACA,QACA,cACA,aACgC;AAChC,QAAM,EAAE,QAAQ,UAAU,cAAc,IAAI;AAAA,IAC1C,OAAO;AAAA,IACP,OAAO;AAAA,IACP;AAAA,IACA,OAAO;AAAA,EACT;AAEA,QAAM,QAAQ,oBAAoB,QAAQ,WAAW;AAErD,QAAM,kBACJ,OAAO,UAAU,SAAS,aACzB,OAAO,SAAsC,eAAe;AAC/D,QAAM,eAAe,kBAChB,OAAO,SAAuD,eAC/D;AAEJ,QAAM,QAAQ,OAAO,SAAS;AAE9B,QAAM,iBAAiB,2BAA2B,KAAK;AACvD,QAAM,kBAAkB,kBACpB,KAAK,IAAI,gBAAgB,OAAO,cAAc,OAAO,cAAc,IAClE,OAAO,cAAc;AAC1B,QAAM,gBAAgB,kBAClB,KAAK,IAAI,cAAc,kBAAkB,CAAC,IAC1C;AAEJ,QAAM,eAAwC;AAAA,IAC5C;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,IACV;AAAA,EACF;AAEA,MAAI,CAAC,mBAAmB,OAAO,gBAAgB,QAAW;AACxD,iBAAa,cAAc,OAAO;AAAA,EACpC;AAEA,MAAI,iBAAiB;AACnB,iBAAa,WAAW;AAAA,MACtB,MAAM;AAAA,MACN,eAAe;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,OAAO,cAAc,SAAS,eAAe;AAC/C,iBAAa,gBAAgB;AAAA,MAC3B,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,QAAQ,OAAO,aAAa;AAAA,MAC9B;AAAA,IACF;AACA,UAAM,QAAmB,aAAa,SAAkC,CAAC;AACzE,QAAI,CAAC,MAAM,SAAS,+BAA+B,GAAG;AACpD,YAAM,KAAK,+BAA+B;AAAA,IAC5C;AACA,iBAAa,QAAQ;AAAA,EACvB,WAAW,OAAO,cAAc,SAAS,eAAe;AAGtD,UAAM,OAAO;AACb,QAAI,OAAO,aAAa,WAAW,UAAU;AAC3C,mBAAa,SAAS,aAAa,SAAS;AAAA,IAC9C,WAAW,MAAM,QAAQ,aAAa,MAAM,GAAG;AAC7C,YAAM,SAAS,aAAa;AAC5B,UAAI,OAAO,SAAS,GAAG;AACrB,cAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,YAAI,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,UAAU;AACzD,eAAK,OAAO,KAAK,OAAO;AAAA,QAC1B;AAAA,MACF;AAAA,IACF,WAAW,CAAC,aAAa,QAAQ;AAC/B,mBAAa,SAAS,KAAK,KAAK;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,SAAS,OAAO,YAAY;AAElD,MAAI,aAAa;AACjB,QAAM,eAAe,oBAAI,IAAoB;AAC7C,QAAM,qBAAqB,oBAAI,IAAoB;AACnD,QAAM,mBAAmB,oBAAI,IAAoB;AACjD,MAAI,gBAAgB;AACpB,MAAI,cAAc;AAClB,MAAI,eAAe;AACnB,MAAI,kBAAkB;AACtB,MAAI,sBAAsB;AAC1B,MAAI;AAEJ,mBAAiB,SAAS,QAAQ;AAChC,UAAM,KAAK;AACX,UAAM,UAAU,YAAY,YAAY;AAExC,QAAI,GAAG,SAAS,iBAAiB;AAC/B,YAAM,MAAO,GAAG,WAAuC,CAAC;AACxD,YAAM,QAAQ,IAAI;AAClB,UAAI,OAAO;AACT,sBAAe,MAAM,gBAA2B;AAChD,uBAAgB,MAAM,iBAA4B;AAClD,0BAAmB,MAAM,2BAAsC;AAC/D,8BAAuB,MAAM,+BAA0C;AAAA,MACzE;AACA;AAAA,IACF;AAEA,QAAI,GAAG,SAAS,iBAAiB;AAC/B,YAAM,QAAS,GAA+B;AAC9C,UAAI,OAAO,aAAa;AACtB,qBAAa,MAAM;AAAA,MACrB;AACA,YAAM,QAAS,GAA+B;AAG9C,UAAI,OAAO,eAAe;AACxB,uBAAe,MAAM;AAAA,MACvB;AACA;AAAA,IACF;AAEA,QAAI,GAAG,SAAS,uBAAuB;AACrC,YAAM,QAAS,GAAG,iBAA6C,CAAC;AAChE,YAAM,aAAa,GAAG;AACtB,UAAI,eAAe,QAAW;AAC5B,yBAAiB,IAAI,YAAY,MAAM,IAAc;AAAA,MACvD;AAEA,UAAI,MAAM,SAAS,YAAY;AAC7B,cAAM,UAAU,SAAS,OAAO,EAAE,kBAAkB,GAAG,CAAC;AAAA,MAC1D,WAAW,MAAM,SAAS,QAAQ;AAChC,cAAM,UAAU,SAAS,OAAO,EAAE,SAAS,GAAG,CAAC;AAAA,MACjD,WAAW,MAAM,SAAS,YAAY;AACpC,cAAM,YAAY;AAClB,cAAM,MAAM;AACZ,qBAAa,IAAI,UAAU,IAAI,GAAG;AAClC,YAAI,eAAe,QAAW;AAC5B,6BAAmB,IAAI,YAAY,UAAU,EAAE;AAAA,QACjD;AACA,cAAM,UAAU,SAAS,OAAO;AAAA,UAC9B,YAAY;AAAA,YACV;AAAA,cACE,OAAO;AAAA,cACP,IAAI,UAAU;AAAA,cACd,MAAM;AAAA,cACN,UAAU,EAAE,MAAM,UAAU,MAAM,WAAW,GAAG;AAAA,YAClD;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,WAAW,GAAG,SAAS,uBAAuB;AAC5C,YAAM,QAAQ,GAAG;AACjB,YAAM,YAAY,MAAM;AACxB,YAAM,aAAa,GAAG;AAEtB,UAAI,cAAc,kBAAkB;AAClC,cAAM,UAAU,SAAS,OAAO;AAAA,UAC9B,kBAAkB,MAAM;AAAA,QAC1B,CAAC;AAAA,MACH,WAAW,cAAc,cAAc;AACrC,cAAM,UAAU,SAAS,OAAO;AAAA,UAC9B,SAAS,MAAM;AAAA,QACjB,CAAC;AAAA,MACH,WAAW,cAAc,mBAAmB;AAC1C,YAAI,eAAe,UAAa,iBAAiB,IAAI,UAAU,MAAM,YAAY;AAC/E,gBAAM,UAAU,SAAS,OAAO;AAAA,YAC9B,oBAAoB,MAAM;AAAA,UAC5B,CAAC;AAAA,QACH;AAAA,MACF,WAAW,cAAc,oBAAoB;AAC3C,YAAI;AACJ,YAAI,eAAe,QAAW;AAC5B,mBAAS,mBAAmB,IAAI,UAAU;AAAA,QAC5C;AACA,YAAI,CAAC,QAAQ;AACX,mBAAS,MAAM,KAAK,aAAa,KAAK,CAAC,EAAE,IAAI;AAAA,QAC/C;AACA,YAAI,QAAQ;AACV,gBAAM,MAAM,aAAa,IAAI,MAAM;AACnC,gBAAM,UAAU,SAAS,OAAO;AAAA,YAC9B,YAAY;AAAA,cACV;AAAA,gBACE,OAAO;AAAA,gBACP,UAAU,EAAE,WAAW,MAAM,aAAuB;AAAA,cACtD;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,WAAW,GAAG,SAAS,gBAAgB;AACrC,UAAI;AACJ,cAAQ,YAAY;AAAA,QAClB,KAAK;AAAY,yBAAe;AAAQ;AAAA,QACxC,KAAK;AAAY,yBAAe;AAAc;AAAA,QAC9C,KAAK;AAAc,yBAAe;AAAU;AAAA,QAC5C,KAAK;AAAiB,yBAAe;AAAQ;AAAA,QAC7C;AAAS,yBAAe,aAAa,OAAO,IAAI,eAAe;AAAQ;AAAA,MACzE;AACA,YAAM;AAAA,QACJ,IAAI;AAAA,QACJ;AAAA,QACA,SAAS;AAAA,UACP;AAAA,YACE,OAAO;AAAA,YACP,OAAO,CAAC;AAAA,YACR,eAAe;AAAA,UACjB;AAAA,QACF;AAAA,QACA,OAAO;AAAA,UACL,eAAe;AAAA,UACf,mBAAmB;AAAA,UACnB,cAAc,cAAc;AAAA,UAC5B,mBAAmB,mBAAmB;AAAA,UACtC,uBAAuB,uBAAuB;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/resolve.ts"],"sourcesContent":["import type { AIProvider } from \"./types.js\";\n\nexport type ProviderName =\n | \"openai\"\n | \"anthropic\"\n | \"gemini\"\n | \"openrouter\"\n | \"bedrock\"\n | \"vertex\"\n | \"ollama\";\n\nconst ENV_KEY_MAP: Record<string, string> = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n gemini: \"GEMINI_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n};\n\nexport const DEFAULT_MODELS: Record<string, string> = {\n openai: \"gpt-5.4\",\n anthropic: \"claude-opus-4.6\",\n gemini: \"gemini-2.5-flash\",\n openrouter: \"anthropic/claude-opus-4.6\",\n bedrock: \"us.anthropic.claude-opus-4.6-v1:0\",\n vertex: \"claude-opus-4.6\",\n ollama: \"qwen2.5-coder:32b\",\n};\n\nexport const SUPPORTED_PROVIDERS: ProviderName[] = Object.keys(DEFAULT_MODELS) as ProviderName[];\n\nfunction getProviderEnvKey(name: string): string | undefined {\n const envVar = ENV_KEY_MAP[name];\n return envVar ? process.env[envVar] : undefined;\n}\n\nexport interface ResolveProviderOptions {\n apiKey?: string;\n model?: string;\n baseURL?: string;\n}\n\n/**\n * Resolve a provider from a name string or pass through an AIProvider instance.\n * API key resolution order:\n * 1. Explicit apiKey option\n * 2. Provider-specific env var (OPENAI_API_KEY, etc.)\n * 3. NOUMEN_API_KEY generic env var\n */\nexport async function resolveProvider(\n input: AIProvider | ProviderName,\n opts?: ResolveProviderOptions,\n): Promise<AIProvider> {\n if (typeof input !== \"string\") return input;\n\n const name = input;\n if (!SUPPORTED_PROVIDERS.includes(name)) {\n throw new Error(\n `Unknown provider \"${name}\". Supported: ${SUPPORTED_PROVIDERS.join(\", \")}`,\n );\n }\n\n const key =\n opts?.apiKey ??\n getProviderEnvKey(name) ??\n process.env.NOUMEN_API_KEY;\n\n switch (name) {\n case \"openai\": {\n if (!key) throw new Error(\"OpenAI requires an API key. Set OPENAI_API_KEY or pass apiKey.\");\n const { OpenAIProvider } = await import(\"./openai.js\");\n return new OpenAIProvider({ apiKey: key, model: opts?.model, baseURL: opts?.baseURL });\n }\n case \"anthropic\": {\n if (!key) throw new Error(\"Anthropic requires an API key. Set ANTHROPIC_API_KEY or pass apiKey.\");\n const { AnthropicProvider } = await import(\"./anthropic.js\");\n return new AnthropicProvider({ apiKey: key, model: opts?.model });\n }\n case \"gemini\": {\n if (!key) throw new Error(\"Gemini requires an API key. Set GEMINI_API_KEY or pass apiKey.\");\n const { GeminiProvider } = await import(\"./gemini.js\");\n return new GeminiProvider({ apiKey: key, model: opts?.model });\n }\n case \"openrouter\": {\n if (!key) throw new Error(\"OpenRouter requires an API key. Set OPENROUTER_API_KEY or pass apiKey.\");\n const { OpenRouterProvider } = await import(\"./openrouter.js\");\n return new OpenRouterProvider({ apiKey: key, model: opts?.model, appName: \"noumen\" });\n }\n case \"bedrock\": {\n const { BedrockAnthropicProvider } = await import(\"./bedrock.js\");\n return new BedrockAnthropicProvider({ model: opts?.model });\n }\n case \"vertex\": {\n const { VertexAnthropicProvider } = await import(\"./vertex.js\");\n return new VertexAnthropicProvider({ model: opts?.model });\n }\n case \"ollama\": {\n const { OllamaProvider } = await import(\"./ollama.js\");\n const base = opts?.baseURL ?? (process.env.OLLAMA_HOST\n ? `${process.env.OLLAMA_HOST.replace(/\\/+$/, \"\")}/v1`\n : undefined);\n return new OllamaProvider({ model: opts?.model, baseURL: base });\n }\n default:\n throw new Error(`Unhandled provider: ${name}`);\n }\n}\n\n/**\n * Auto-detect provider from available environment variables.\n */\nexport async function detectProvider(): Promise<ProviderName | undefined> {\n if (process.env.ANTHROPIC_API_KEY) return \"anthropic\";\n if (process.env.OPENAI_API_KEY) return \"openai\";\n if (process.env.GEMINI_API_KEY) return \"gemini\";\n if (process.env.OPENROUTER_API_KEY) return \"openrouter\";\n if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_PROFILE) return \"bedrock\";\n if (process.env.GOOGLE_APPLICATION_CREDENTIALS || process.env.GCLOUD_PROJECT) return \"vertex\";\n if (process.env.OLLAMA_HOST) return \"ollama\";\n\n try {\n const host = process.env.OLLAMA_HOST ?? \"http://localhost:11434\";\n const res = await fetch(`${host.replace(/\\/+$/, \"\")}/api/tags`, {\n signal: AbortSignal.timeout(500),\n });\n if (res.ok) return \"ollama\";\n } catch { /* not running */ }\n\n return undefined;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/openai.ts"],"sourcesContent":["import OpenAI from \"openai\";\nimport type {\n AIProvider,\n ChatParams,\n ChatStreamChunk,\n} from \"./types.js\";\nimport type { ChatMessage, ContentPart } from \"../session/types.js\";\n\nexport interface OpenAIProviderOptions {\n apiKey?: string;\n baseURL?: string;\n model?: string;\n defaultHeaders?: Record<string, string | undefined>;\n}\n\nconst O_SERIES_PATTERN = /^o[1-9]/;\n\nexport class OpenAIProvider implements AIProvider {\n private client: OpenAI;\n private defaultModel: string;\n\n constructor(opts: OpenAIProviderOptions) {\n this.client = new OpenAI({\n apiKey: opts.apiKey ?? \"not-needed\",\n baseURL: opts.baseURL,\n defaultHeaders: opts.defaultHeaders,\n });\n this.defaultModel = opts.model ?? \"gpt-5.4\";\n }\n\n async *chat(params: ChatParams): AsyncIterable<ChatStreamChunk> {\n const messages = this.buildMessages(params.system, params.messages);\n const model = params.model ?? this.defaultModel;\n const isOSeries = O_SERIES_PATTERN.test(model);\n\n const createParams: OpenAI.ChatCompletionCreateParamsStreaming = {\n model,\n messages: messages as unknown as OpenAI.ChatCompletionMessageParam[],\n tools: params.tools?.map((t) => ({\n type: \"function\" as const,\n function: t.function,\n })),\n stream: true,\n stream_options: { include_usage: true },\n };\n\n if (isOSeries && params.thinking?.type === \"enabled\") {\n (createParams as unknown as Record<string, unknown>).reasoning_effort = \"high\";\n } else {\n createParams.max_tokens = params.max_tokens;\n createParams.temperature = params.temperature;\n }\n\n if (params.outputFormat?.type === \"json_schema\") {\n (createParams as unknown as Record<string, unknown>).response_format = {\n type: \"json_schema\",\n json_schema: {\n name: params.outputFormat.name ?? \"response\",\n schema: params.outputFormat.schema,\n strict: params.outputFormat.strict ?? false,\n },\n };\n } else if (params.outputFormat?.type === \"json_object\") {\n (createParams as unknown as Record<string, unknown>).response_format = {\n type: \"json_object\",\n };\n }\n\n const stream = await this.client.chat.completions.create(createParams);\n\n for await (const chunk of stream) {\n const usage = chunk.usage;\n let mappedUsage: ChatStreamChunk[\"usage\"] | undefined;\n if (usage) {\n const u = usage as unknown as Record<string, unknown>;\n const promptDetails = u.prompt_tokens_details as Record<string, unknown> | undefined;\n const completionDetails = u.completion_tokens_details as Record<string, unknown> | undefined;\n mappedUsage = {\n prompt_tokens: usage.prompt_tokens,\n completion_tokens: usage.completion_tokens,\n total_tokens: usage.total_tokens,\n cache_read_tokens: promptDetails?.cached_tokens as number | undefined,\n thinking_tokens: completionDetails?.reasoning_tokens as number | undefined,\n };\n }\n\n yield {\n id: chunk.id,\n model: chunk.model,\n choices: chunk.choices.map((c) => ({\n index: c.index,\n delta: {\n role: c.delta.role as \"assistant\" | undefined,\n content: c.delta.content,\n tool_calls: c.delta.tool_calls?.map((tc) => ({\n index: tc.index,\n id: tc.id,\n type: tc.type as \"function\" | undefined,\n function: tc.function\n ? {\n name: tc.function.name,\n arguments: tc.function.arguments,\n }\n : undefined,\n })),\n },\n finish_reason: c.finish_reason,\n })),\n usage: mappedUsage,\n };\n }\n }\n\n private static contentPartsToOpenAI(\n parts: ContentPart[],\n ): Array<Record<string, unknown>> {\n return parts.map((part) => {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n if (part.type === \"image\") {\n return {\n type: \"image_url\",\n image_url: { url: `data:${part.media_type};base64,${part.data}` },\n };\n }\n // image_url\n return { type: \"image_url\", image_url: { url: part.url } };\n });\n }\n\n private buildMessages(\n system: string | undefined,\n messages: ChatMessage[],\n ): Array<Record<string, unknown>> {\n const result: Array<Record<string, unknown>> = [];\n if (system) {\n result.push({ role: \"system\", content: system });\n }\n for (const msg of messages) {\n if (msg.role === \"tool\") {\n const content = Array.isArray(msg.content)\n ? OpenAIProvider.contentPartsToOpenAI(msg.content as ContentPart[])\n : msg.content;\n result.push({\n role: \"tool\",\n tool_call_id: msg.tool_call_id,\n content,\n });\n } else if (msg.role === \"assistant\") {\n const entry: Record<string, unknown> = {\n role: \"assistant\",\n content: msg.content,\n };\n if (msg.tool_calls) {\n entry.tool_calls = msg.tool_calls;\n }\n result.push(entry);\n } else if (msg.role === \"user\") {\n const content = Array.isArray(msg.content)\n ? OpenAIProvider.contentPartsToOpenAI(msg.content as ContentPart[])\n : msg.content;\n result.push({ role: \"user\", content });\n } else {\n result.push({ role: msg.role, content: msg.content });\n }\n }\n return result;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/cache.ts","../src/utils/context.ts"],"sourcesContent":["/**\n * Provider-agnostic prompt caching utilities.\n *\n * Stable tool ordering prevents cache invalidation when the tool set is\n * unchanged. The breakpoint index helper determines which message gets a\n * single cache_control marker per request (matching claude-code's strategy).\n */\n\nimport type { ToolDefinition } from \"./types.js\";\nimport type { ChatMessage } from \"../session/types.js\";\n\nexport type CacheScope = \"global\" | \"org\";\n\nexport interface CacheControlConfig {\n enabled: boolean;\n /** TTL for cached content. When set, produces `ttl: '1h'` in cache_control. */\n ttl?: \"1h\";\n /** Scope for shared cache across sessions/orgs. */\n scope?: CacheScope;\n}\n\n/**\n * Sort tool definitions deterministically for prompt cache stability.\n *\n * Strategy (matching claude-code's assembleToolPool): built-in tools form a\n * contiguous prefix sorted by name, followed by MCP/external tools sorted by\n * name. Tools with `mcpInfo` on the original Tool object are treated as MCP;\n * everything else is built-in. Since ToolDefinition doesn't carry mcpInfo,\n * callers can pass an optional set of MCP tool names to partition correctly.\n */\nexport function sortToolDefinitionsForCache(\n tools: ToolDefinition[],\n mcpToolNames?: ReadonlySet<string>,\n): ToolDefinition[] {\n const byName = (a: ToolDefinition, b: ToolDefinition) =>\n a.function.name.localeCompare(b.function.name);\n\n if (!mcpToolNames || mcpToolNames.size === 0) {\n return [...tools].sort(byName);\n }\n\n const builtIn: ToolDefinition[] = [];\n const mcp: ToolDefinition[] = [];\n\n for (const t of tools) {\n if (mcpToolNames.has(t.function.name)) {\n mcp.push(t);\n } else {\n builtIn.push(t);\n }\n }\n\n return [...builtIn.sort(byName), ...mcp.sort(byName)];\n}\n\n/**\n * Determine which message index should receive the cache_control breakpoint.\n *\n * Exactly one message per request is marked. Normally the last message;\n * for forked agents with skipCacheWrite the second-to-last so the fork\n * doesn't write its own tail into the cache.\n */\nexport function getMessageCacheBreakpointIndex(\n messages: ChatMessage[],\n skipCacheWrite?: boolean,\n): number {\n if (messages.length === 0) return -1;\n return skipCacheWrite && messages.length >= 2\n ? messages.length - 2\n : messages.length - 1;\n}\n","/**\n * Model context window sizes and effective window calculations.\n */\n\nconst MODEL_CONTEXT_WINDOWS: Record<string, number> = {\n // Anthropic (evergreen prefixes — also match dated variants via startsWith)\n \"claude-sonnet-4\": 200_000,\n \"claude-opus-4\": 200_000,\n \"claude-haiku-3-5\": 200_000,\n \"claude-3-5-sonnet\": 200_000,\n \"claude-3-5-haiku\": 200_000,\n // OpenAI\n \"gpt-4o\": 128_000,\n \"gpt-4o-mini\": 128_000,\n \"gpt-4-turbo\": 128_000,\n \"gpt-4\": 8_192,\n \"o1\": 200_000,\n \"o1-mini\": 128_000,\n \"o1-preview\": 128_000,\n \"o3\": 200_000,\n \"o3-mini\": 200_000,\n \"o4-mini\": 200_000,\n // Google\n \"gemini-2.5-pro\": 1_048_576,\n \"gemini-2.5-flash\": 1_048_576,\n \"gemini-2.0-flash\": 1_048_576,\n \"gemini-1.5-pro\": 2_097_152,\n \"gemini-1.5-flash\": 1_048_576,\n};\n\nconst MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {\n \"claude-sonnet-4\": 16_384,\n \"claude-opus-4\": 16_384,\n \"claude-haiku-3-5\": 8_192,\n \"claude-3-5-sonnet\": 8_192,\n \"claude-3-5-haiku\": 8_192,\n \"gpt-4o\": 16_384,\n \"gpt-4o-mini\": 16_384,\n \"gpt-4-turbo\": 4_096,\n \"o1\": 100_000,\n \"o3\": 100_000,\n \"o3-mini\": 100_000,\n \"o4-mini\": 100_000,\n \"gemini-2.5-pro\": 65_536,\n \"gemini-2.5-flash\": 65_536,\n \"gemini-2.0-flash\": 8_192,\n};\n\nconst DEFAULT_MAX_OUTPUT_TOKENS = 16_384;\nconst DEFAULT_CONTEXT_WINDOW = 128_000;\nconst AUTOCOMPACT_BUFFER_TOKENS = 13_000;\nconst MAX_OUTPUT_RESERVE = 20_000;\n\nlet customWindows: Record<string, number> = {};\n\n/**\n * Register custom context window sizes for models not in the built-in table.\n */\nexport function registerContextWindows(\n windows: Record<string, number>,\n): void {\n customWindows = { ...customWindows, ...windows };\n}\n\n/**\n * Get the context window size for a model. Checks custom overrides first,\n * then built-in table, then prefix-matches, then falls back to default.\n */\nexport function getContextWindowForModel(model: string): number {\n if (customWindows[model] !== undefined) return customWindows[model];\n if (MODEL_CONTEXT_WINDOWS[model] !== undefined)\n return MODEL_CONTEXT_WINDOWS[model];\n\n for (const [prefix, size] of Object.entries(MODEL_CONTEXT_WINDOWS)) {\n if (model.startsWith(prefix)) return size;\n }\n for (const [prefix, size] of Object.entries(customWindows)) {\n if (model.startsWith(prefix)) return size;\n }\n\n return DEFAULT_CONTEXT_WINDOW;\n}\n\n/**\n * Effective context window = total window minus space reserved for the\n * model's output during a compaction/summary request.\n */\nexport function getEffectiveContextWindow(\n model: string,\n maxOutputTokens?: number,\n): number {\n const window = getContextWindowForModel(model);\n const reserve = Math.min(maxOutputTokens ?? MAX_OUTPUT_RESERVE, MAX_OUTPUT_RESERVE);\n return window - reserve;\n}\n\n/**\n * Auto-compact threshold: effective window minus a buffer to ensure we\n * compact before we're at the hard limit.\n */\nexport function getAutoCompactThreshold(\n model: string,\n maxOutputTokens?: number,\n): number {\n return getEffectiveContextWindow(model, maxOutputTokens) - AUTOCOMPACT_BUFFER_TOKENS;\n}\n\n/**\n * Get the maximum output tokens a model supports. Used to clamp\n * max_tokens when extended thinking budgets are added.\n */\nexport function getMaxOutputTokensForModel(model: string): number {\n if (MODEL_MAX_OUTPUT_TOKENS[model] !== undefined)\n return MODEL_MAX_OUTPUT_TOKENS[model];\n for (const [prefix, size] of Object.entries(MODEL_MAX_OUTPUT_TOKENS)) {\n if (model.startsWith(prefix)) return size;\n }\n return DEFAULT_MAX_OUTPUT_TOKENS;\n}\n"],"mappings":"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","names":[]}
|