claudish 9.4.0 → 9.6.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/dist/index.js +1455 -635
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -715,7 +715,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
715
715
|
});
|
|
716
716
|
|
|
717
717
|
// src/version.ts
|
|
718
|
-
var VERSION = "9.
|
|
718
|
+
var VERSION = "9.6.0";
|
|
719
719
|
|
|
720
720
|
// src/logger.ts
|
|
721
721
|
import { appendFile, existsSync as existsSync2, mkdirSync, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -815,14 +815,17 @@ function redactLogLine(message, timestamp) {
|
|
|
815
815
|
return `[${timestamp}] ${message}
|
|
816
816
|
`;
|
|
817
817
|
}
|
|
818
|
+
function logFileStamp() {
|
|
819
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").split("T").join("_").slice(0, -5);
|
|
820
|
+
return `${timestamp}_${process.pid}`;
|
|
821
|
+
}
|
|
818
822
|
function initLogger(debugMode, level = "info", noLogs = false) {
|
|
819
823
|
if (!noLogs) {
|
|
820
824
|
const logsDir = join2(homedir2(), ".claudish", "logs");
|
|
821
825
|
if (!existsSync2(logsDir)) {
|
|
822
826
|
mkdirSync(logsDir, { recursive: true });
|
|
823
827
|
}
|
|
824
|
-
|
|
825
|
-
alwaysOnLogPath = join2(logsDir, `claudish_${timestamp}.log`);
|
|
828
|
+
alwaysOnLogPath = join2(logsDir, `claudish_${logFileStamp()}.log`);
|
|
826
829
|
writeFileSync2(alwaysOnLogPath, `Claudish Session Log - ${new Date().toISOString()}
|
|
827
830
|
Mode: structural (content redacted)
|
|
828
831
|
${"=".repeat(60)}
|
|
@@ -837,8 +840,7 @@ ${"=".repeat(60)}
|
|
|
837
840
|
if (!existsSync2(logsDir)) {
|
|
838
841
|
mkdirSync(logsDir, { recursive: true });
|
|
839
842
|
}
|
|
840
|
-
|
|
841
|
-
logFilePath = join2(logsDir, `claudish_${timestamp}.log`);
|
|
843
|
+
logFilePath = join2(logsDir, `claudish_${logFileStamp()}.log`);
|
|
842
844
|
writeFileSync2(logFilePath, `Claudish Debug Log - ${new Date().toISOString()}
|
|
843
845
|
Log Level: ${level}
|
|
844
846
|
${"=".repeat(80)}
|
|
@@ -18167,6 +18169,12 @@ var init_stdio2 = __esm(() => {
|
|
|
18167
18169
|
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
18168
18170
|
import { homedir as homedir7 } from "os";
|
|
18169
18171
|
import { dirname as dirname4, join as join7 } from "path";
|
|
18172
|
+
function reasoningStatusOf(entry) {
|
|
18173
|
+
if (entry.reasoningStatus === "known" || entry.reasoningStatus === "unknown") {
|
|
18174
|
+
return entry.reasoningStatus;
|
|
18175
|
+
}
|
|
18176
|
+
return entry.reasoning !== undefined ? "known" : "unknown";
|
|
18177
|
+
}
|
|
18170
18178
|
function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
|
|
18171
18179
|
if (!existsSync5(path))
|
|
18172
18180
|
return null;
|
|
@@ -18183,12 +18191,14 @@ function readAllModelsCache(path = ALL_MODELS_CACHE_PATH) {
|
|
|
18183
18191
|
const models = Array.isArray(data.models) ? data.models : [];
|
|
18184
18192
|
const entries = Array.isArray(data.entries) ? data.entries : [];
|
|
18185
18193
|
const plans = Array.isArray(data.plans) ? data.plans : undefined;
|
|
18194
|
+
const catalogRevision = typeof data.catalogRevision === "string" ? data.catalogRevision : undefined;
|
|
18186
18195
|
return {
|
|
18187
18196
|
version: 2,
|
|
18188
18197
|
lastUpdated,
|
|
18189
18198
|
entries,
|
|
18190
18199
|
models,
|
|
18191
|
-
...plans !== undefined ? { plans } : {}
|
|
18200
|
+
...plans !== undefined ? { plans } : {},
|
|
18201
|
+
...catalogRevision !== undefined ? { catalogRevision } : {}
|
|
18192
18202
|
};
|
|
18193
18203
|
}
|
|
18194
18204
|
function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
|
|
@@ -18198,7 +18208,8 @@ function writeAllModelsCache(data, path = ALL_MODELS_CACHE_PATH) {
|
|
|
18198
18208
|
lastUpdated: data.lastUpdated ?? new Date().toISOString(),
|
|
18199
18209
|
entries: data.entries ?? existing?.entries ?? [],
|
|
18200
18210
|
models: data.models ?? existing?.models ?? [],
|
|
18201
|
-
...data.plans !== undefined || existing?.plans !== undefined ? { plans: data.plans ?? existing?.plans ?? [] } : {}
|
|
18211
|
+
...data.plans !== undefined || existing?.plans !== undefined ? { plans: data.plans ?? existing?.plans ?? [] } : {},
|
|
18212
|
+
...data.catalogRevision !== undefined ? { catalogRevision: data.catalogRevision } : {}
|
|
18202
18213
|
};
|
|
18203
18214
|
mkdirSync5(dirname4(path), { recursive: true });
|
|
18204
18215
|
writeFileSync5(path, JSON.stringify(merged), "utf-8");
|
|
@@ -18285,6 +18296,10 @@ function lookupModel(modelId, cachePath) {
|
|
|
18285
18296
|
function lookupModelReasoning(modelId, cachePath) {
|
|
18286
18297
|
return findCacheEntry(modelId, cachePath)?.reasoning;
|
|
18287
18298
|
}
|
|
18299
|
+
function lookupModelReasoningStatus(modelId, cachePath) {
|
|
18300
|
+
const entry = findCacheEntry(modelId, cachePath);
|
|
18301
|
+
return entry ? reasoningStatusOf(entry) : undefined;
|
|
18302
|
+
}
|
|
18288
18303
|
function lookupModelTokenParam(modelId, cachePath) {
|
|
18289
18304
|
return findCacheEntry(modelId, cachePath)?.tokenParam;
|
|
18290
18305
|
}
|
|
@@ -18398,6 +18413,26 @@ function findCacheEntry(modelId, cachePath) {
|
|
|
18398
18413
|
return entry;
|
|
18399
18414
|
}
|
|
18400
18415
|
}
|
|
18416
|
+
const canonical = resolvePlanDescribedModelId(modelId, cache.plans);
|
|
18417
|
+
if (!canonical)
|
|
18418
|
+
return;
|
|
18419
|
+
const canonicalLower = canonical.toLowerCase();
|
|
18420
|
+
for (const entry of cache.entries) {
|
|
18421
|
+
if (entry.modelId.toLowerCase() === canonicalLower)
|
|
18422
|
+
return entry;
|
|
18423
|
+
if (entry.aliases?.some((a) => a.toLowerCase() === canonicalLower))
|
|
18424
|
+
return entry;
|
|
18425
|
+
}
|
|
18426
|
+
return;
|
|
18427
|
+
}
|
|
18428
|
+
function resolvePlanDescribedModelId(wireId, plans) {
|
|
18429
|
+
if (!plans)
|
|
18430
|
+
return;
|
|
18431
|
+
for (const plan of plans) {
|
|
18432
|
+
const described = plan.modelDescriptions?.[wireId];
|
|
18433
|
+
if (described?.status === "described" && described.modelId)
|
|
18434
|
+
return described.modelId;
|
|
18435
|
+
}
|
|
18401
18436
|
return;
|
|
18402
18437
|
}
|
|
18403
18438
|
function classifyCatalogHit(entry, q) {
|
|
@@ -19045,6 +19080,28 @@ var init_remote_provider_types = __esm(() => {
|
|
|
19045
19080
|
};
|
|
19046
19081
|
});
|
|
19047
19082
|
|
|
19083
|
+
// src/adapters/optional-param-rejection.ts
|
|
19084
|
+
function escapeName(name) {
|
|
19085
|
+
return name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
19086
|
+
}
|
|
19087
|
+
function namesParameterField(errorText, param) {
|
|
19088
|
+
const p = escapeName(param);
|
|
19089
|
+
const tail = "(?![A-Za-z0-9_])";
|
|
19090
|
+
const quoted = new RegExp(`['"\`]${p}['"\`]`);
|
|
19091
|
+
const introduced = new RegExp("(?:parameter|argument|field|property|key|input)s?\\b" + "(?:[^A-Za-z0-9_]|\\b(?:supplied|provided|given|named|name|is|was|of|in|the|for|body|request|json)\\b){0,6}" + `${p}${tail}`, "i");
|
|
19092
|
+
const leading = new RegExp(`(?:^|["'])\\s*${p}\\s*:`);
|
|
19093
|
+
return quoted.test(errorText) || introduced.test(errorText) || leading.test(errorText);
|
|
19094
|
+
}
|
|
19095
|
+
function rejectedOptionalParams(errorText, candidates) {
|
|
19096
|
+
if (!errorText || !PARAMETER_COMPLAINT.test(errorText))
|
|
19097
|
+
return [];
|
|
19098
|
+
return candidates.filter((param) => namesParameterField(errorText, param));
|
|
19099
|
+
}
|
|
19100
|
+
var PARAMETER_COMPLAINT;
|
|
19101
|
+
var init_optional_param_rejection = __esm(() => {
|
|
19102
|
+
PARAMETER_COMPLAINT = /(unknown|unrecognized|unexpected|unsupported|not supported|does not support|invalid|not permitted|not allowed|extra (fields|inputs))/i;
|
|
19103
|
+
});
|
|
19104
|
+
|
|
19048
19105
|
// src/adapters/tool-name-utils.ts
|
|
19049
19106
|
function hashToolName(name) {
|
|
19050
19107
|
let h1 = 3735928559;
|
|
@@ -19061,20 +19118,46 @@ function hashToolName(name) {
|
|
|
19061
19118
|
const combined = 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
19062
19119
|
return combined.toString(16).padStart(8, "0").slice(0, 8);
|
|
19063
19120
|
}
|
|
19064
|
-
function
|
|
19065
|
-
|
|
19066
|
-
|
|
19067
|
-
|
|
19068
|
-
const
|
|
19069
|
-
const
|
|
19070
|
-
|
|
19071
|
-
|
|
19072
|
-
|
|
19073
|
-
|
|
19074
|
-
|
|
19121
|
+
function newToolNameBindings() {
|
|
19122
|
+
return { byEncoded: new Map, byOriginal: new Map };
|
|
19123
|
+
}
|
|
19124
|
+
function hashedForm(transformed, original, limit, salt) {
|
|
19125
|
+
const seed = salt === 0 ? original : `${original}#${salt}`;
|
|
19126
|
+
const prefix = transformed.slice(0, Math.max(0, limit - 9));
|
|
19127
|
+
return `${prefix}_${hashToolName(seed)}`;
|
|
19128
|
+
}
|
|
19129
|
+
function encodeToolName(original, limit, bindings) {
|
|
19130
|
+
const already = bindings.byOriginal.get(original);
|
|
19131
|
+
if (already !== undefined)
|
|
19132
|
+
return already;
|
|
19133
|
+
const transformed = original.replace(UNWIRABLE_CHARACTER, "_");
|
|
19134
|
+
const takenByAnother = (name) => {
|
|
19135
|
+
const owner = bindings.byEncoded.get(name);
|
|
19136
|
+
return owner !== undefined && owner !== original;
|
|
19137
|
+
};
|
|
19138
|
+
let encoded = transformed.length <= limit ? transformed : hashedForm(transformed, original, limit, 0);
|
|
19139
|
+
if (takenByAnother(encoded)) {
|
|
19140
|
+
let salt = 0;
|
|
19141
|
+
do {
|
|
19142
|
+
encoded = hashedForm(transformed, original, limit, salt++);
|
|
19143
|
+
} while (takenByAnother(encoded));
|
|
19144
|
+
}
|
|
19145
|
+
bindings.byEncoded.set(encoded, original);
|
|
19146
|
+
bindings.byOriginal.set(original, encoded);
|
|
19147
|
+
if (encoded !== original) {
|
|
19148
|
+
log(`[ToolName] Encoded: "${original}" -> "${encoded}" (${original.length} -> ${encoded.length})`);
|
|
19149
|
+
}
|
|
19150
|
+
return encoded;
|
|
19151
|
+
}
|
|
19152
|
+
function wireDecodesToolNames(format) {
|
|
19153
|
+
return format !== undefined && TOOL_NAME_DECODING_WIRES.includes(format);
|
|
19154
|
+
}
|
|
19155
|
+
var TOOL_NAME_SOURCE = "[A-Za-z_][A-Za-z0-9_.-]{0,63}", TOOL_NAME_SHAPE, UNWIRABLE_CHARACTER, TOOL_NAME_DECODING_WIRES;
|
|
19075
19156
|
var init_tool_name_utils = __esm(() => {
|
|
19076
19157
|
init_logger();
|
|
19077
19158
|
TOOL_NAME_SHAPE = new RegExp(`^${TOOL_NAME_SOURCE}$`);
|
|
19159
|
+
UNWIRABLE_CHARACTER = /[^A-Za-z0-9_-]/g;
|
|
19160
|
+
TOOL_NAME_DECODING_WIRES = ["openai-sse", "openai-responses-sse"];
|
|
19078
19161
|
});
|
|
19079
19162
|
|
|
19080
19163
|
// src/handlers/shared/format/openai-messages.ts
|
|
@@ -19106,13 +19189,154 @@ ${msg}`;
|
|
|
19106
19189
|
processAssistantMessage(msg, messages, simpleFormat);
|
|
19107
19190
|
}
|
|
19108
19191
|
}
|
|
19109
|
-
return messages;
|
|
19192
|
+
return normalizeMessageSequence(messages);
|
|
19110
19193
|
}
|
|
19111
|
-
function
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
|
|
19194
|
+
function mergeUserContent(a, b) {
|
|
19195
|
+
if (typeof a === "string" && typeof b === "string") {
|
|
19196
|
+
if (!a)
|
|
19197
|
+
return b;
|
|
19198
|
+
if (!b)
|
|
19199
|
+
return a;
|
|
19200
|
+
return `${a}
|
|
19201
|
+
|
|
19202
|
+
${b}`;
|
|
19203
|
+
}
|
|
19204
|
+
const toParts = (c) => {
|
|
19205
|
+
if (typeof c === "string")
|
|
19206
|
+
return c ? [{ type: "text", text: c }] : [];
|
|
19207
|
+
if (Array.isArray(c))
|
|
19208
|
+
return c;
|
|
19209
|
+
return;
|
|
19210
|
+
};
|
|
19211
|
+
const pa = toParts(a);
|
|
19212
|
+
const pb = toParts(b);
|
|
19213
|
+
if (!pa || !pb)
|
|
19214
|
+
return;
|
|
19215
|
+
return [...pa, ...pb];
|
|
19216
|
+
}
|
|
19217
|
+
function pushUserMessage(out, msg) {
|
|
19218
|
+
const prev = out[out.length - 1];
|
|
19219
|
+
if (prev?.role === "user") {
|
|
19220
|
+
const merged = mergeUserContent(prev.content, msg.content);
|
|
19221
|
+
if (merged !== undefined) {
|
|
19222
|
+
prev.content = merged;
|
|
19223
|
+
return;
|
|
19224
|
+
}
|
|
19225
|
+
}
|
|
19226
|
+
out.push(msg);
|
|
19227
|
+
}
|
|
19228
|
+
function normalizeMessageSequence(messages) {
|
|
19229
|
+
const out = [];
|
|
19230
|
+
let openRound = null;
|
|
19231
|
+
let collected = [];
|
|
19232
|
+
const flushRound = (isFinal = false) => {
|
|
19233
|
+
if (!openRound)
|
|
19234
|
+
return;
|
|
19235
|
+
if (isFinal && collected.length === 0) {
|
|
19236
|
+
openRound = null;
|
|
19237
|
+
return;
|
|
19238
|
+
}
|
|
19239
|
+
out.push(...alignToolRound(openRound, collected));
|
|
19240
|
+
openRound = null;
|
|
19241
|
+
collected = [];
|
|
19115
19242
|
};
|
|
19243
|
+
for (const msg of messages) {
|
|
19244
|
+
if (msg.role === "tool") {
|
|
19245
|
+
if (openRound) {
|
|
19246
|
+
collected.push(msg);
|
|
19247
|
+
continue;
|
|
19248
|
+
}
|
|
19249
|
+
log(`[OpenAIMessages] error \u2014 tool result ${msg.tool_call_id} answers no open tool round; ` + "re-emitted as a user message");
|
|
19250
|
+
const text = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
19251
|
+
pushUserMessage(out, { role: "user", content: `[Tool Result]: ${text}` });
|
|
19252
|
+
continue;
|
|
19253
|
+
}
|
|
19254
|
+
flushRound();
|
|
19255
|
+
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
|
|
19256
|
+
out.push(msg);
|
|
19257
|
+
openRound = msg;
|
|
19258
|
+
continue;
|
|
19259
|
+
}
|
|
19260
|
+
if (msg.role === "user")
|
|
19261
|
+
pushUserMessage(out, msg);
|
|
19262
|
+
else
|
|
19263
|
+
out.push(msg);
|
|
19264
|
+
}
|
|
19265
|
+
flushRound(true);
|
|
19266
|
+
return out;
|
|
19267
|
+
}
|
|
19268
|
+
function missingResultText(name) {
|
|
19269
|
+
const call = name ? `\`${name}\` call` : "call";
|
|
19270
|
+
return `[No tool result was provided for this ${call} \u2014 it was interrupted, cancelled, ` + "or dropped from the conversation history.]";
|
|
19271
|
+
}
|
|
19272
|
+
function alignToolRound(assistant, collected) {
|
|
19273
|
+
const calls = Array.isArray(assistant.tool_calls) ? assistant.tool_calls : [];
|
|
19274
|
+
const byId = new Map;
|
|
19275
|
+
for (const m of collected) {
|
|
19276
|
+
const list = byId.get(m.tool_call_id);
|
|
19277
|
+
if (list)
|
|
19278
|
+
list.push(m);
|
|
19279
|
+
else
|
|
19280
|
+
byId.set(m.tool_call_id, [m]);
|
|
19281
|
+
}
|
|
19282
|
+
const ordered = [];
|
|
19283
|
+
let synthesized = 0;
|
|
19284
|
+
for (const call of calls) {
|
|
19285
|
+
const matched = byId.get(call.id);
|
|
19286
|
+
if (matched) {
|
|
19287
|
+
ordered.push(...matched);
|
|
19288
|
+
byId.delete(call.id);
|
|
19289
|
+
continue;
|
|
19290
|
+
}
|
|
19291
|
+
synthesized++;
|
|
19292
|
+
log(`[OpenAIMessages] error \u2014 tool call ${call.id} (${call.function?.name || "?"}) has no ` + "result; a synthetic tool message names the omission");
|
|
19293
|
+
ordered.push({
|
|
19294
|
+
role: "tool",
|
|
19295
|
+
content: missingResultText(call.function?.name || ""),
|
|
19296
|
+
tool_call_id: call.id
|
|
19297
|
+
});
|
|
19298
|
+
}
|
|
19299
|
+
let dropped = 0;
|
|
19300
|
+
for (const [id, list] of byId) {
|
|
19301
|
+
dropped += list.length;
|
|
19302
|
+
log(`[OpenAIMessages] error \u2014 tool result ${id} matches no call in the round it follows; dropped`);
|
|
19303
|
+
}
|
|
19304
|
+
const reordered = ordered.some((m, i) => m !== collected[i]);
|
|
19305
|
+
if (reordered && !synthesized && !dropped) {
|
|
19306
|
+
log(`[OpenAIMessages] Reordered ${ordered.length} tool results to their tool_calls order`);
|
|
19307
|
+
}
|
|
19308
|
+
return ordered;
|
|
19309
|
+
}
|
|
19310
|
+
function imageBlockToUrlPart(block) {
|
|
19311
|
+
const source = block?.source;
|
|
19312
|
+
if (!source || typeof source !== "object") {
|
|
19313
|
+
log("[OpenAIMessages] Dropping image block: error \u2014 no source object");
|
|
19314
|
+
return null;
|
|
19315
|
+
}
|
|
19316
|
+
const url = typeof source.url === "string" ? source.url : "";
|
|
19317
|
+
const data = typeof source.data === "string" ? source.data : "";
|
|
19318
|
+
const kind = source.type || (url ? "url" : data ? "base64" : "");
|
|
19319
|
+
if (kind === "url") {
|
|
19320
|
+
if (!url) {
|
|
19321
|
+
log("[OpenAIMessages] Dropping image block: error \u2014 url source carries no url");
|
|
19322
|
+
return null;
|
|
19323
|
+
}
|
|
19324
|
+
return { type: "image_url", image_url: { url } };
|
|
19325
|
+
}
|
|
19326
|
+
if (kind === "base64") {
|
|
19327
|
+
if (!data) {
|
|
19328
|
+
log("[OpenAIMessages] Dropping image block: error \u2014 base64 source carries no data");
|
|
19329
|
+
return null;
|
|
19330
|
+
}
|
|
19331
|
+
const mediaType = typeof source.media_type === "string" ? source.media_type : "";
|
|
19332
|
+
if (!mediaType) {
|
|
19333
|
+
log("[OpenAIMessages] Dropping image block: error \u2014 base64 source carries no media_type");
|
|
19334
|
+
return null;
|
|
19335
|
+
}
|
|
19336
|
+
return { type: "image_url", image_url: { url: `data:${mediaType};base64,${data}` } };
|
|
19337
|
+
}
|
|
19338
|
+
log(`[OpenAIMessages] Dropping image block: error \u2014 unsupported source type ${kind || "(none)"}`);
|
|
19339
|
+
return null;
|
|
19116
19340
|
}
|
|
19117
19341
|
function processUserMessage(msg, messages, simpleFormat = false) {
|
|
19118
19342
|
if (Array.isArray(msg.content)) {
|
|
@@ -19129,7 +19353,9 @@ function processUserMessage(msg, messages, simpleFormat = false) {
|
|
|
19129
19353
|
}
|
|
19130
19354
|
} else if (block.type === "image") {
|
|
19131
19355
|
if (!simpleFormat) {
|
|
19132
|
-
|
|
19356
|
+
const part = imageBlockToUrlPart(block);
|
|
19357
|
+
if (part)
|
|
19358
|
+
contentParts.push(part);
|
|
19133
19359
|
}
|
|
19134
19360
|
} else if (block.type === "tool_result") {
|
|
19135
19361
|
if (seen.has(block.tool_use_id))
|
|
@@ -19141,12 +19367,21 @@ function processUserMessage(msg, messages, simpleFormat = false) {
|
|
|
19141
19367
|
} else if (Array.isArray(block.content)) {
|
|
19142
19368
|
const texts = [];
|
|
19143
19369
|
const others = [];
|
|
19370
|
+
let forwardedImages = 0;
|
|
19371
|
+
let droppedImages = 0;
|
|
19144
19372
|
for (const inner of block.content) {
|
|
19145
19373
|
if (inner.type === "text") {
|
|
19146
19374
|
texts.push(inner.text);
|
|
19147
19375
|
} else if (inner.type === "image" && inner.source) {
|
|
19148
|
-
if (!simpleFormat)
|
|
19149
|
-
|
|
19376
|
+
if (!simpleFormat) {
|
|
19377
|
+
const part = imageBlockToUrlPart(inner);
|
|
19378
|
+
if (part) {
|
|
19379
|
+
toolResultImages.push(part);
|
|
19380
|
+
forwardedImages++;
|
|
19381
|
+
} else {
|
|
19382
|
+
droppedImages++;
|
|
19383
|
+
}
|
|
19384
|
+
}
|
|
19150
19385
|
} else {
|
|
19151
19386
|
others.push(inner);
|
|
19152
19387
|
}
|
|
@@ -19157,7 +19392,12 @@ function processUserMessage(msg, messages, simpleFormat = false) {
|
|
|
19157
19392
|
resultText += (resultText ? `
|
|
19158
19393
|
` : "") + JSON.stringify(others);
|
|
19159
19394
|
if (!resultText) {
|
|
19160
|
-
|
|
19395
|
+
if (forwardedImages)
|
|
19396
|
+
resultText = "[image returned; see following message]";
|
|
19397
|
+
else if (droppedImages)
|
|
19398
|
+
resultText = "[image returned, but its source could not be forwarded]";
|
|
19399
|
+
else
|
|
19400
|
+
resultText = "";
|
|
19161
19401
|
}
|
|
19162
19402
|
} else {
|
|
19163
19403
|
resultText = JSON.stringify(block.content);
|
|
@@ -19243,6 +19483,9 @@ function processAssistantMessage(msg, messages, simpleFormat = false) {
|
|
|
19243
19483
|
messages.push({ role: "assistant", content: msg.content });
|
|
19244
19484
|
}
|
|
19245
19485
|
}
|
|
19486
|
+
var init_openai_messages = __esm(() => {
|
|
19487
|
+
init_logger();
|
|
19488
|
+
});
|
|
19246
19489
|
|
|
19247
19490
|
// src/transform.ts
|
|
19248
19491
|
function removeUriFormat(schema) {
|
|
@@ -19452,6 +19695,45 @@ function summarizeToolParameters(schema) {
|
|
|
19452
19695
|
}
|
|
19453
19696
|
return summarized;
|
|
19454
19697
|
}
|
|
19698
|
+
function mapToolChoiceToOpenAI(choice, encodeName) {
|
|
19699
|
+
if (!choice)
|
|
19700
|
+
return;
|
|
19701
|
+
const { type, name } = choice;
|
|
19702
|
+
if (type === "tool" && name) {
|
|
19703
|
+
return { type: "function", function: { name: encodeName ? encodeName(name) : name } };
|
|
19704
|
+
}
|
|
19705
|
+
if (type === "any")
|
|
19706
|
+
return "required";
|
|
19707
|
+
if (type === "auto" || type === "none")
|
|
19708
|
+
return type;
|
|
19709
|
+
return;
|
|
19710
|
+
}
|
|
19711
|
+
function mapToolChoiceToResponsesAPI(choice, encodeName) {
|
|
19712
|
+
const mapped = mapToolChoiceToOpenAI(choice, encodeName);
|
|
19713
|
+
if (mapped === undefined || typeof mapped === "string")
|
|
19714
|
+
return mapped;
|
|
19715
|
+
return { type: "function", name: mapped.function.name };
|
|
19716
|
+
}
|
|
19717
|
+
function mapToolChoiceToGemini(choice, encodeName) {
|
|
19718
|
+
if (!choice)
|
|
19719
|
+
return;
|
|
19720
|
+
const { type, name } = choice;
|
|
19721
|
+
if (type === "tool" && name) {
|
|
19722
|
+
return {
|
|
19723
|
+
functionCallingConfig: {
|
|
19724
|
+
mode: "ANY",
|
|
19725
|
+
allowedFunctionNames: [encodeName ? encodeName(name) : name]
|
|
19726
|
+
}
|
|
19727
|
+
};
|
|
19728
|
+
}
|
|
19729
|
+
if (type === "any")
|
|
19730
|
+
return { functionCallingConfig: { mode: "ANY" } };
|
|
19731
|
+
if (type === "auto")
|
|
19732
|
+
return { functionCallingConfig: { mode: "AUTO" } };
|
|
19733
|
+
if (type === "none")
|
|
19734
|
+
return { functionCallingConfig: { mode: "NONE" } };
|
|
19735
|
+
return;
|
|
19736
|
+
}
|
|
19455
19737
|
var PORTABLE_ESCAPE_LETTERS, NAMED_SCHEMA_MAPS;
|
|
19456
19738
|
var init_openai_tools = __esm(() => {
|
|
19457
19739
|
init_logger();
|
|
@@ -19490,11 +19772,38 @@ function matchesModelFamily(modelId, family) {
|
|
|
19490
19772
|
function isEffortLevel(value) {
|
|
19491
19773
|
return typeof value === "string" && EFFORT_ORDER.includes(value);
|
|
19492
19774
|
}
|
|
19775
|
+
function outputCeilingOf(originalRequest, payload) {
|
|
19776
|
+
const candidates = [
|
|
19777
|
+
originalRequest?.max_tokens,
|
|
19778
|
+
payload?.max_tokens,
|
|
19779
|
+
payload?.max_completion_tokens,
|
|
19780
|
+
payload?.max_output_tokens
|
|
19781
|
+
];
|
|
19782
|
+
for (const value of candidates) {
|
|
19783
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0)
|
|
19784
|
+
return value;
|
|
19785
|
+
}
|
|
19786
|
+
return;
|
|
19787
|
+
}
|
|
19788
|
+
function clampThinkingBudget(budget, ceiling) {
|
|
19789
|
+
if (budget === undefined)
|
|
19790
|
+
return;
|
|
19791
|
+
if (ceiling === undefined)
|
|
19792
|
+
return budget;
|
|
19793
|
+
const allowed = ceiling - ANSWER_TOKEN_RESERVE;
|
|
19794
|
+
if (allowed < MIN_THINKING_BUDGET)
|
|
19795
|
+
return "no-room";
|
|
19796
|
+
return Math.min(budget, allowed);
|
|
19797
|
+
}
|
|
19493
19798
|
|
|
19494
19799
|
class BaseAPIFormat {
|
|
19495
19800
|
modelId;
|
|
19496
19801
|
wireFormat;
|
|
19497
|
-
|
|
19802
|
+
responseWireFormat;
|
|
19803
|
+
setResponseWireFormat(format) {
|
|
19804
|
+
this.responseWireFormat = format;
|
|
19805
|
+
}
|
|
19806
|
+
toolNameBindings = newToolNameBindings();
|
|
19498
19807
|
constructor(modelId, wireFormat) {
|
|
19499
19808
|
this.modelId = modelId;
|
|
19500
19809
|
this.wireFormat = wireFormat;
|
|
@@ -19505,20 +19814,52 @@ class BaseAPIFormat {
|
|
|
19505
19814
|
getWireFormat() {
|
|
19506
19815
|
return this.wireFormat;
|
|
19507
19816
|
}
|
|
19817
|
+
recoverFromRejection(payload, errorText) {
|
|
19818
|
+
return this.recoverFromSamplingParamRejection(payload, errorText);
|
|
19819
|
+
}
|
|
19820
|
+
recoverFromSamplingParamRejection(payload, errorText) {
|
|
19821
|
+
if (!payload)
|
|
19822
|
+
return null;
|
|
19823
|
+
const present = OPTIONAL_SAMPLING_PARAMS.filter((p) => payload[p] !== undefined);
|
|
19824
|
+
if (present.length === 0)
|
|
19825
|
+
return null;
|
|
19826
|
+
const rejected = rejectedOptionalParams(errorText, present);
|
|
19827
|
+
if (rejected.length === 0)
|
|
19828
|
+
return null;
|
|
19829
|
+
const next = { ...payload };
|
|
19830
|
+
for (const p of rejected)
|
|
19831
|
+
delete next[p];
|
|
19832
|
+
return { payload: next, note: `dropped ${rejected.join(", ")} for ${this.modelId}` };
|
|
19833
|
+
}
|
|
19834
|
+
applyOpenAISamplingParams(payload, claudeRequest) {
|
|
19835
|
+
if (!payload || !claudeRequest)
|
|
19836
|
+
return;
|
|
19837
|
+
const sequences = claudeRequest.stop_sequences;
|
|
19838
|
+
if (Array.isArray(sequences)) {
|
|
19839
|
+
const usable = sequences.filter((s) => typeof s === "string" && s.length > 0);
|
|
19840
|
+
if (usable.length > 0)
|
|
19841
|
+
payload.stop = usable;
|
|
19842
|
+
}
|
|
19843
|
+
if (claudeRequest.top_p !== undefined && claudeRequest.top_p !== null) {
|
|
19844
|
+
payload.top_p = claudeRequest.top_p;
|
|
19845
|
+
}
|
|
19846
|
+
}
|
|
19508
19847
|
getToolNameLimit() {
|
|
19509
|
-
|
|
19848
|
+
const wire = this.responseWireFormat ?? this.wireFormat ?? this.getStreamFormat();
|
|
19849
|
+
return wireDecodesToolNames(wire) ? OPENAI_TOOL_NAME_LIMIT : null;
|
|
19510
19850
|
}
|
|
19511
19851
|
getMaxToolCount() {
|
|
19512
19852
|
return null;
|
|
19513
19853
|
}
|
|
19514
19854
|
getToolNameMap() {
|
|
19515
|
-
return this.
|
|
19855
|
+
return this.toolNameBindings.byEncoded;
|
|
19516
19856
|
}
|
|
19517
19857
|
restoreToolName(name) {
|
|
19518
|
-
return this.
|
|
19858
|
+
return this.toolNameBindings.byEncoded.get(name) || name;
|
|
19519
19859
|
}
|
|
19520
19860
|
prepareRequest(request, originalRequest) {
|
|
19521
19861
|
const prepared = this.prepareRequestCommon(request, originalRequest) ?? request;
|
|
19862
|
+
this.encodeToolNames(prepared);
|
|
19522
19863
|
if (!this.isAnthropicWire()) {
|
|
19523
19864
|
return this.applyNativeReasoning(prepared, originalRequest) ?? prepared;
|
|
19524
19865
|
}
|
|
@@ -19543,6 +19884,11 @@ class BaseAPIFormat {
|
|
|
19543
19884
|
}
|
|
19544
19885
|
}
|
|
19545
19886
|
applyAnthropicWireReasoning(request, originalRequest) {
|
|
19887
|
+
const status = this.lookupReasoningStatus();
|
|
19888
|
+
if (status !== "known") {
|
|
19889
|
+
log(`[${this.getName()}] ${this.modelId} reasoning control ${status === "unknown" ? "unknown" : "absent from the catalog"} -> no reasoning parameter emitted`);
|
|
19890
|
+
return request;
|
|
19891
|
+
}
|
|
19546
19892
|
const reasoning = this.lookupReasoningCapability();
|
|
19547
19893
|
if (reasoning?.supported === false) {
|
|
19548
19894
|
request.thinking = { type: "disabled" };
|
|
@@ -19553,36 +19899,63 @@ class BaseAPIFormat {
|
|
|
19553
19899
|
if (!effort)
|
|
19554
19900
|
return request;
|
|
19555
19901
|
if (effort === "none" || effort === "minimal") {
|
|
19902
|
+
if (reasoning?.mandatory) {
|
|
19903
|
+
return this.enableAnthropicEffort(request, effort, reasoning, "mandatory reasoning");
|
|
19904
|
+
}
|
|
19556
19905
|
request.thinking = { type: "disabled" };
|
|
19557
19906
|
log(`[${this.getName()}] effort ${effort} -> thinking.type: disabled for ${this.modelId}`);
|
|
19558
19907
|
return request;
|
|
19559
19908
|
}
|
|
19560
|
-
|
|
19561
|
-
|
|
19909
|
+
const control = reasoning?.control;
|
|
19910
|
+
const advertisesEfforts = (reasoning?.efforts?.length ?? 0) > 0;
|
|
19911
|
+
if (control === "effort" || control === undefined && advertisesEfforts) {
|
|
19912
|
+
return this.enableAnthropicEffort(request, effort, reasoning, "catalog: effort-controlled");
|
|
19562
19913
|
}
|
|
19563
|
-
|
|
19564
|
-
|
|
19565
|
-
const level = this.clampToAdvertisedEffort(effort, advertised);
|
|
19566
|
-
request.thinking = { type: "enabled" };
|
|
19567
|
-
if (level) {
|
|
19568
|
-
request.output_config = { ...request.output_config ?? {}, effort: level };
|
|
19569
|
-
}
|
|
19570
|
-
log(`[${this.getName()}] effort ${effort} -> thinking: enabled, output_config.effort: ${level ?? "(none advertised)"} for ${this.modelId} (advertised: ${advertised.efforts?.join("/")})`);
|
|
19571
|
-
return request;
|
|
19914
|
+
if (control === "budget" || control === undefined && reasoning?.supportsBudgetTokens) {
|
|
19915
|
+
return this.enableAnthropicBudget(request, effort, originalRequest, reasoning);
|
|
19572
19916
|
}
|
|
19573
|
-
|
|
19574
|
-
|
|
19575
|
-
|
|
19576
|
-
|
|
19917
|
+
request.thinking = { type: "enabled" };
|
|
19918
|
+
this.stripAnthropicEffortField(request);
|
|
19919
|
+
log(`[${this.getName()}] effort ${effort} -> thinking: enabled (no depth knob; catalog control=${control ?? "unspecified"}) for ${this.modelId}`);
|
|
19920
|
+
return request;
|
|
19921
|
+
}
|
|
19922
|
+
enableAnthropicEffort(request, effort, reasoning, why) {
|
|
19923
|
+
const level = reasoning ? this.clampToAdvertisedEffort(effort, reasoning) : undefined;
|
|
19924
|
+
request.thinking = { type: "enabled" };
|
|
19925
|
+
if (level) {
|
|
19926
|
+
request.output_config = { ...request.output_config ?? {}, effort: level };
|
|
19577
19927
|
}
|
|
19578
|
-
|
|
19928
|
+
log(`[${this.getName()}] effort ${effort} -> thinking: enabled, output_config.effort: ${level ?? "(none advertised)"} for ${this.modelId} (${why}; advertised: ${reasoning?.efforts?.join("/") ?? "none"})`);
|
|
19929
|
+
return request;
|
|
19579
19930
|
}
|
|
19580
|
-
|
|
19581
|
-
const
|
|
19931
|
+
enableAnthropicBudget(request, effort, originalRequest, reasoning) {
|
|
19932
|
+
const requested = this.effortToThinkingTokenBudget(effort);
|
|
19933
|
+
const ceiling = outputCeilingOf(originalRequest, request);
|
|
19934
|
+
const budget = clampThinkingBudget(requested, ceiling);
|
|
19935
|
+
if (budget === "no-room") {
|
|
19936
|
+
if (reasoning?.mandatory) {
|
|
19937
|
+
request.thinking = { type: "enabled" };
|
|
19938
|
+
} else {
|
|
19939
|
+
request.thinking = { type: "disabled" };
|
|
19940
|
+
}
|
|
19941
|
+
this.stripAnthropicEffortField(request);
|
|
19942
|
+
log(`[${this.getName()}] effort ${effort} -> budget ${requested ?? "(model max)"} does not fit under max_tokens ${ceiling}; sent thinking.type: ${request.thinking.type} for ${this.modelId}`);
|
|
19943
|
+
return request;
|
|
19944
|
+
}
|
|
19582
19945
|
request.thinking = budget === undefined ? { type: "enabled" } : { type: "enabled", budget_tokens: budget };
|
|
19583
|
-
|
|
19946
|
+
this.stripAnthropicEffortField(request);
|
|
19947
|
+
log(`[${this.getName()}] effort ${effort} -> thinking: enabled, budget_tokens: ${budget ?? "(model max)"}${budget !== undefined && budget !== requested ? ` (clamped from ${requested} under max_tokens ${ceiling})` : ""} for ${this.modelId} (catalog: budget-controlled)`);
|
|
19584
19948
|
return request;
|
|
19585
19949
|
}
|
|
19950
|
+
stripAnthropicEffortField(request) {
|
|
19951
|
+
if (!request?.output_config || typeof request.output_config !== "object")
|
|
19952
|
+
return;
|
|
19953
|
+
if (request.output_config.effort === undefined)
|
|
19954
|
+
return;
|
|
19955
|
+
delete request.output_config.effort;
|
|
19956
|
+
if (Object.keys(request.output_config).length === 0)
|
|
19957
|
+
delete request.output_config;
|
|
19958
|
+
}
|
|
19586
19959
|
clampToAdvertisedEffort(requested, reasoning) {
|
|
19587
19960
|
if (this.pinnedEffort)
|
|
19588
19961
|
return this.pinnedEffort;
|
|
@@ -19611,6 +19984,13 @@ class BaseAPIFormat {
|
|
|
19611
19984
|
return;
|
|
19612
19985
|
}
|
|
19613
19986
|
}
|
|
19987
|
+
lookupReasoningStatus() {
|
|
19988
|
+
try {
|
|
19989
|
+
return lookupModelReasoningStatus(this.modelId);
|
|
19990
|
+
} catch {
|
|
19991
|
+
return;
|
|
19992
|
+
}
|
|
19993
|
+
}
|
|
19614
19994
|
effortToThinkingTokenBudget(effort) {
|
|
19615
19995
|
switch (effort) {
|
|
19616
19996
|
case "low":
|
|
@@ -19656,7 +20036,7 @@ class BaseAPIFormat {
|
|
|
19656
20036
|
return;
|
|
19657
20037
|
}
|
|
19658
20038
|
reset() {
|
|
19659
|
-
this.
|
|
20039
|
+
this.toolNameBindings = newToolNameBindings();
|
|
19660
20040
|
}
|
|
19661
20041
|
convertMessages(claudeRequest, filterIdentityFn) {
|
|
19662
20042
|
return convertMessagesToOpenAI(claudeRequest, this.modelId, filterIdentityFn);
|
|
@@ -19672,6 +20052,10 @@ class BaseAPIFormat {
|
|
|
19672
20052
|
};
|
|
19673
20053
|
if (tools.length > 0) {
|
|
19674
20054
|
payload.tools = tools;
|
|
20055
|
+
const toolChoice = mapToolChoiceToOpenAI(claudeRequest.tool_choice);
|
|
20056
|
+
if (toolChoice !== undefined) {
|
|
20057
|
+
payload.tool_choice = toolChoice;
|
|
20058
|
+
}
|
|
19675
20059
|
}
|
|
19676
20060
|
if (claudeRequest.max_tokens) {
|
|
19677
20061
|
payload.max_tokens = claudeRequest.max_tokens;
|
|
@@ -19679,6 +20063,7 @@ class BaseAPIFormat {
|
|
|
19679
20063
|
if (claudeRequest.temperature !== undefined) {
|
|
19680
20064
|
payload.temperature = claudeRequest.temperature;
|
|
19681
20065
|
}
|
|
20066
|
+
this.applyOpenAISamplingParams(payload, claudeRequest);
|
|
19682
20067
|
return payload;
|
|
19683
20068
|
}
|
|
19684
20069
|
getStreamFormat() {
|
|
@@ -19696,50 +20081,56 @@ class BaseAPIFormat {
|
|
|
19696
20081
|
shouldFilterThinking() {
|
|
19697
20082
|
return this.isAnthropicWire();
|
|
19698
20083
|
}
|
|
19699
|
-
|
|
20084
|
+
encodeToolNames(request) {
|
|
19700
20085
|
const limit = this.getToolNameLimit();
|
|
19701
|
-
if (!limit || !request
|
|
20086
|
+
if (!limit || !request)
|
|
19702
20087
|
return;
|
|
19703
|
-
|
|
19704
|
-
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
if (tool
|
|
19709
|
-
tool.
|
|
19710
|
-
} else if (tool.name) {
|
|
19711
|
-
tool.name = truncated;
|
|
20088
|
+
const encode = (name) => encodeToolName(name, limit, this.toolNameBindings);
|
|
20089
|
+
if (Array.isArray(request.tools)) {
|
|
20090
|
+
for (const tool of request.tools) {
|
|
20091
|
+
if (tool?.function?.name) {
|
|
20092
|
+
tool.function.name = encode(tool.function.name);
|
|
20093
|
+
} else if (tool?.name) {
|
|
20094
|
+
tool.name = encode(tool.name);
|
|
19712
20095
|
}
|
|
19713
20096
|
}
|
|
19714
20097
|
}
|
|
19715
|
-
|
|
19716
|
-
|
|
19717
|
-
|
|
19718
|
-
|
|
19719
|
-
return;
|
|
19720
|
-
for (const msg of messages) {
|
|
19721
|
-
if (msg.role === "assistant" && Array.isArray(msg.tool_calls)) {
|
|
20098
|
+
if (Array.isArray(request.messages)) {
|
|
20099
|
+
for (const msg of request.messages) {
|
|
20100
|
+
if (msg?.role !== "assistant" || !Array.isArray(msg.tool_calls))
|
|
20101
|
+
continue;
|
|
19722
20102
|
for (const tc of msg.tool_calls) {
|
|
19723
|
-
|
|
19724
|
-
|
|
19725
|
-
const truncated = truncateToolName(name, limit);
|
|
19726
|
-
tc.function.name = truncated;
|
|
19727
|
-
if (!this.toolNameMap.has(truncated)) {
|
|
19728
|
-
this.toolNameMap.set(truncated, name);
|
|
19729
|
-
}
|
|
19730
|
-
}
|
|
20103
|
+
if (tc?.function?.name)
|
|
20104
|
+
tc.function.name = encode(tc.function.name);
|
|
19731
20105
|
}
|
|
19732
20106
|
}
|
|
19733
20107
|
}
|
|
20108
|
+
if (Array.isArray(request.input)) {
|
|
20109
|
+
for (const item of request.input) {
|
|
20110
|
+
if (item?.type === "function_call" && item.name)
|
|
20111
|
+
item.name = encode(item.name);
|
|
20112
|
+
}
|
|
20113
|
+
}
|
|
20114
|
+
const choice = request.tool_choice;
|
|
20115
|
+
if (choice && typeof choice === "object") {
|
|
20116
|
+
if (choice.function?.name) {
|
|
20117
|
+
choice.function.name = encode(choice.function.name);
|
|
20118
|
+
} else if (choice.name) {
|
|
20119
|
+
choice.name = encode(choice.name);
|
|
20120
|
+
}
|
|
20121
|
+
}
|
|
19734
20122
|
}
|
|
19735
20123
|
}
|
|
19736
|
-
var EFFORT_ORDER, EFFORT_LEVELS, NON_ANTHROPIC_REASONING_FIELDS, DefaultAPIFormat;
|
|
20124
|
+
var OPTIONAL_SAMPLING_PARAMS, OPENAI_TOOL_NAME_LIMIT = 64, EFFORT_ORDER, EFFORT_LEVELS, NON_ANTHROPIC_REASONING_FIELDS, MIN_THINKING_BUDGET = 1024, ANSWER_TOKEN_RESERVE = 1024, DefaultAPIFormat;
|
|
19737
20125
|
var init_base_api_format = __esm(() => {
|
|
19738
20126
|
init_remote_provider_types();
|
|
19739
20127
|
init_logger();
|
|
19740
20128
|
init_model_catalog();
|
|
20129
|
+
init_optional_param_rejection();
|
|
19741
20130
|
init_tool_name_utils();
|
|
20131
|
+
init_openai_messages();
|
|
19742
20132
|
init_openai_tools();
|
|
20133
|
+
OPTIONAL_SAMPLING_PARAMS = ["stop", "top_p"];
|
|
19743
20134
|
EFFORT_ORDER = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
19744
20135
|
EFFORT_LEVELS = EFFORT_ORDER;
|
|
19745
20136
|
NON_ANTHROPIC_REASONING_FIELDS = [
|
|
@@ -19922,6 +20313,7 @@ function normalizeCodexModel(modelId) {
|
|
|
19922
20313
|
}
|
|
19923
20314
|
var CodexAPIFormat;
|
|
19924
20315
|
var init_codex_api_format = __esm(() => {
|
|
20316
|
+
init_openai_tools();
|
|
19925
20317
|
init_logger();
|
|
19926
20318
|
init_base_api_format();
|
|
19927
20319
|
init_reasoning_cache();
|
|
@@ -19987,6 +20379,10 @@ var init_codex_api_format = __esm(() => {
|
|
|
19987
20379
|
}
|
|
19988
20380
|
return tool;
|
|
19989
20381
|
});
|
|
20382
|
+
const toolChoice = mapToolChoiceToResponsesAPI(claudeRequest.tool_choice);
|
|
20383
|
+
if (toolChoice !== undefined) {
|
|
20384
|
+
payload.tool_choice = toolChoice;
|
|
20385
|
+
}
|
|
19990
20386
|
}
|
|
19991
20387
|
return payload;
|
|
19992
20388
|
}
|
|
@@ -20568,31 +20964,308 @@ function filterIdentity(content) {
|
|
|
20568
20964
|
`);
|
|
20569
20965
|
}
|
|
20570
20966
|
|
|
20967
|
+
// src/handlers/shared/schema-validate.ts
|
|
20968
|
+
function missingRequired(schema, args) {
|
|
20969
|
+
const required = schema?.required;
|
|
20970
|
+
if (!Array.isArray(required) || required.length === 0)
|
|
20971
|
+
return [];
|
|
20972
|
+
const supplied = args ?? {};
|
|
20973
|
+
return required.filter((key) => {
|
|
20974
|
+
if (typeof key !== "string")
|
|
20975
|
+
return false;
|
|
20976
|
+
if (!Object.hasOwn(supplied, key))
|
|
20977
|
+
return true;
|
|
20978
|
+
const value = supplied[key];
|
|
20979
|
+
return value === undefined || value === null;
|
|
20980
|
+
});
|
|
20981
|
+
}
|
|
20982
|
+
function isAbsent(args, key) {
|
|
20983
|
+
if (!Object.hasOwn(args, key))
|
|
20984
|
+
return true;
|
|
20985
|
+
const value = args[key];
|
|
20986
|
+
return value === undefined || value === null;
|
|
20987
|
+
}
|
|
20988
|
+
function declaredTypes(node) {
|
|
20989
|
+
if (!node)
|
|
20990
|
+
return [];
|
|
20991
|
+
const t = node.type;
|
|
20992
|
+
if (typeof t === "string")
|
|
20993
|
+
return [t];
|
|
20994
|
+
if (Array.isArray(t))
|
|
20995
|
+
return t.filter((x) => typeof x === "string");
|
|
20996
|
+
if (Array.isArray(node.enum) && node.enum.length > 0) {
|
|
20997
|
+
const kinds = new Set;
|
|
20998
|
+
for (const member of node.enum) {
|
|
20999
|
+
if (member === null || member === undefined)
|
|
21000
|
+
continue;
|
|
21001
|
+
if (Array.isArray(member))
|
|
21002
|
+
kinds.add("array");
|
|
21003
|
+
else if (typeof member === "object")
|
|
21004
|
+
kinds.add("object");
|
|
21005
|
+
else if (typeof member === "number")
|
|
21006
|
+
kinds.add(Number.isInteger(member) ? "integer" : "number");
|
|
21007
|
+
else if (typeof member === "boolean")
|
|
21008
|
+
kinds.add("boolean");
|
|
21009
|
+
else if (typeof member === "string")
|
|
21010
|
+
kinds.add("string");
|
|
21011
|
+
}
|
|
21012
|
+
return [...kinds];
|
|
21013
|
+
}
|
|
21014
|
+
return [];
|
|
21015
|
+
}
|
|
21016
|
+
function coerceOne(value, type, node) {
|
|
21017
|
+
switch (type) {
|
|
21018
|
+
case "string":
|
|
21019
|
+
return typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" ? String(value) : undefined;
|
|
21020
|
+
case "number":
|
|
21021
|
+
case "integer": {
|
|
21022
|
+
if (typeof value === "number") {
|
|
21023
|
+
return type === "integer" && !Number.isInteger(value) ? undefined : value;
|
|
21024
|
+
}
|
|
21025
|
+
if (typeof value !== "string" || !FULL_NUMBER.test(value.trim()))
|
|
21026
|
+
return;
|
|
21027
|
+
const n = Number(value.trim());
|
|
21028
|
+
if (!Number.isFinite(n))
|
|
21029
|
+
return;
|
|
21030
|
+
return type === "integer" && !Number.isInteger(n) ? undefined : n;
|
|
21031
|
+
}
|
|
21032
|
+
case "boolean": {
|
|
21033
|
+
if (typeof value === "boolean")
|
|
21034
|
+
return value;
|
|
21035
|
+
if (typeof value !== "string")
|
|
21036
|
+
return;
|
|
21037
|
+
const s = value.trim().toLowerCase();
|
|
21038
|
+
return s === "true" ? true : s === "false" ? false : undefined;
|
|
21039
|
+
}
|
|
21040
|
+
case "array": {
|
|
21041
|
+
const arr = Array.isArray(value) ? value : parseJsonOfKind(value, "array");
|
|
21042
|
+
if (!Array.isArray(arr))
|
|
21043
|
+
return;
|
|
21044
|
+
const items = node?.items;
|
|
21045
|
+
if (!items)
|
|
21046
|
+
return arr;
|
|
21047
|
+
return arr.map((el) => {
|
|
21048
|
+
const target = declaredTypes(items);
|
|
21049
|
+
for (const t of target) {
|
|
21050
|
+
const c = coerceOne(el, t, items);
|
|
21051
|
+
if (c !== undefined)
|
|
21052
|
+
return c;
|
|
21053
|
+
}
|
|
21054
|
+
return el;
|
|
21055
|
+
});
|
|
21056
|
+
}
|
|
21057
|
+
case "object":
|
|
21058
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : parseJsonOfKind(value, "object");
|
|
21059
|
+
default:
|
|
21060
|
+
return;
|
|
21061
|
+
}
|
|
21062
|
+
}
|
|
21063
|
+
function parseJsonOfKind(value, kind) {
|
|
21064
|
+
if (typeof value !== "string")
|
|
21065
|
+
return;
|
|
21066
|
+
try {
|
|
21067
|
+
const parsed = JSON.parse(value);
|
|
21068
|
+
if (kind === "array")
|
|
21069
|
+
return Array.isArray(parsed) ? parsed : undefined;
|
|
21070
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
21071
|
+
} catch {
|
|
21072
|
+
return;
|
|
21073
|
+
}
|
|
21074
|
+
}
|
|
21075
|
+
function coerceToSchema(schema, args) {
|
|
21076
|
+
const properties = schema?.properties;
|
|
21077
|
+
if (!properties)
|
|
21078
|
+
return { args, coerced: [] };
|
|
21079
|
+
const out = { ...args };
|
|
21080
|
+
const coerced = [];
|
|
21081
|
+
for (const [key, value] of Object.entries(args)) {
|
|
21082
|
+
const node = properties[key];
|
|
21083
|
+
if (!node || value === undefined || value === null)
|
|
21084
|
+
continue;
|
|
21085
|
+
const types = declaredTypes(node);
|
|
21086
|
+
if (types.length === 0)
|
|
21087
|
+
continue;
|
|
21088
|
+
if (types.some((t) => matchesType(value, t)))
|
|
21089
|
+
continue;
|
|
21090
|
+
for (const t of types) {
|
|
21091
|
+
const c = coerceOne(value, t, node);
|
|
21092
|
+
if (c !== undefined) {
|
|
21093
|
+
out[key] = c;
|
|
21094
|
+
coerced.push(key);
|
|
21095
|
+
break;
|
|
21096
|
+
}
|
|
21097
|
+
}
|
|
21098
|
+
}
|
|
21099
|
+
return { args: coerced.length > 0 ? out : args, coerced };
|
|
21100
|
+
}
|
|
21101
|
+
function matchesType(value, type) {
|
|
21102
|
+
switch (type) {
|
|
21103
|
+
case "string":
|
|
21104
|
+
return typeof value === "string";
|
|
21105
|
+
case "number":
|
|
21106
|
+
return typeof value === "number";
|
|
21107
|
+
case "integer":
|
|
21108
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
21109
|
+
case "boolean":
|
|
21110
|
+
return typeof value === "boolean";
|
|
21111
|
+
case "array":
|
|
21112
|
+
return Array.isArray(value);
|
|
21113
|
+
case "object":
|
|
21114
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
21115
|
+
case "null":
|
|
21116
|
+
return value === null;
|
|
21117
|
+
default:
|
|
21118
|
+
return false;
|
|
21119
|
+
}
|
|
21120
|
+
}
|
|
21121
|
+
function applySchemaDefaults(schema, args) {
|
|
21122
|
+
const properties = schema?.properties;
|
|
21123
|
+
const required = schema?.required;
|
|
21124
|
+
if (!properties || !Array.isArray(required))
|
|
21125
|
+
return { args, applied: [] };
|
|
21126
|
+
const out = { ...args };
|
|
21127
|
+
const applied = [];
|
|
21128
|
+
for (const key of required) {
|
|
21129
|
+
if (typeof key !== "string" || !isAbsent(out, key))
|
|
21130
|
+
continue;
|
|
21131
|
+
const node = properties[key];
|
|
21132
|
+
if (!node || !Object.hasOwn(node, "default") || node.default === undefined)
|
|
21133
|
+
continue;
|
|
21134
|
+
out[key] = node.default;
|
|
21135
|
+
applied.push(key);
|
|
21136
|
+
}
|
|
21137
|
+
return { args: applied.length > 0 ? out : args, applied };
|
|
21138
|
+
}
|
|
21139
|
+
function renameToDeclaredKeys(schema, args) {
|
|
21140
|
+
const properties = schema?.properties;
|
|
21141
|
+
const required = schema?.required;
|
|
21142
|
+
if (!properties || !Array.isArray(required))
|
|
21143
|
+
return { args, renamed: [] };
|
|
21144
|
+
const out = { ...args };
|
|
21145
|
+
const renamed = [];
|
|
21146
|
+
for (const target of required) {
|
|
21147
|
+
if (typeof target !== "string")
|
|
21148
|
+
continue;
|
|
21149
|
+
if (!properties[target] || !isAbsent(out, target))
|
|
21150
|
+
continue;
|
|
21151
|
+
for (const source of KEY_SYNONYMS[target] ?? []) {
|
|
21152
|
+
if (properties[source])
|
|
21153
|
+
continue;
|
|
21154
|
+
if (isAbsent(out, source))
|
|
21155
|
+
continue;
|
|
21156
|
+
out[target] = out[source];
|
|
21157
|
+
delete out[source];
|
|
21158
|
+
renamed.push(`${source}\u2192${target}`);
|
|
21159
|
+
break;
|
|
21160
|
+
}
|
|
21161
|
+
}
|
|
21162
|
+
return { args: renamed.length > 0 ? out : args, renamed };
|
|
21163
|
+
}
|
|
21164
|
+
var FULL_NUMBER, KEY_SYNONYMS;
|
|
21165
|
+
var init_schema_validate = __esm(() => {
|
|
21166
|
+
FULL_NUMBER = /^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;
|
|
21167
|
+
KEY_SYNONYMS = {
|
|
21168
|
+
command: ["cmd", "shell", "script"],
|
|
21169
|
+
file_path: ["path", "file", "filename"],
|
|
21170
|
+
content: ["text", "data", "body"],
|
|
21171
|
+
pattern: ["query", "search", "regex", "glob"],
|
|
21172
|
+
query: ["search", "keyword"],
|
|
21173
|
+
prompt: ["query", "task"]
|
|
21174
|
+
};
|
|
21175
|
+
});
|
|
21176
|
+
|
|
20571
21177
|
// src/handlers/shared/tool-call-recovery.ts
|
|
20572
21178
|
function hasExtractableFunctionTag(text) {
|
|
20573
21179
|
return FUNCTION_TAG_PRESENT.test(text);
|
|
20574
21180
|
}
|
|
20575
|
-
function keepOnlyRealTools(extracted, knownToolNames) {
|
|
21181
|
+
function keepOnlyRealTools(extracted, knownToolNames, decodeToolName) {
|
|
20576
21182
|
const kept = [];
|
|
20577
21183
|
for (const call of extracted) {
|
|
20578
21184
|
if (!TOOL_NAME_SHAPE.test(call.name)) {
|
|
20579
21185
|
log(`[ToolRecovery] Dropped extracted call: name is not an identifier: ${JSON.stringify(call.name.slice(0, 120))}`);
|
|
20580
21186
|
continue;
|
|
20581
21187
|
}
|
|
21188
|
+
const decoded = decodeToolName ? decodeToolName(call.name) : call.name;
|
|
21189
|
+
const named = decoded === call.name ? call : { ...call, name: decoded };
|
|
20582
21190
|
if (!knownToolNames || knownToolNames.length === 0) {
|
|
20583
|
-
kept.push(
|
|
21191
|
+
kept.push(named);
|
|
20584
21192
|
continue;
|
|
20585
21193
|
}
|
|
20586
|
-
const canonical = knownToolNames.find((t) => t.toLowerCase() ===
|
|
21194
|
+
const canonical = knownToolNames.find((t) => t.toLowerCase() === named.name.toLowerCase());
|
|
20587
21195
|
if (!canonical) {
|
|
20588
|
-
log(`[ToolRecovery] Dropped extracted call for unadvertised tool: ${
|
|
21196
|
+
log(`[ToolRecovery] Dropped extracted call for unadvertised tool: ${named.name}`);
|
|
20589
21197
|
continue;
|
|
20590
21198
|
}
|
|
20591
|
-
kept.push(canonical ===
|
|
21199
|
+
kept.push(canonical === named.name ? named : { ...named, name: canonical });
|
|
20592
21200
|
}
|
|
20593
21201
|
return kept;
|
|
20594
21202
|
}
|
|
20595
|
-
function
|
|
21203
|
+
function parseFunctionTagEnvelope(text) {
|
|
21204
|
+
const body = text.trim();
|
|
21205
|
+
if (body.length === 0)
|
|
21206
|
+
return null;
|
|
21207
|
+
if (!body.startsWith("<function="))
|
|
21208
|
+
return null;
|
|
21209
|
+
const calls = [];
|
|
21210
|
+
let cursor = 0;
|
|
21211
|
+
while (cursor < body.length) {
|
|
21212
|
+
FUNCTION_TAG_AT_CURSOR.lastIndex = cursor;
|
|
21213
|
+
const open = FUNCTION_TAG_AT_CURSOR.exec(body);
|
|
21214
|
+
if (!open)
|
|
21215
|
+
return null;
|
|
21216
|
+
cursor = FUNCTION_TAG_AT_CURSOR.lastIndex;
|
|
21217
|
+
const args = {};
|
|
21218
|
+
while (cursor < body.length) {
|
|
21219
|
+
const rest = body.slice(cursor);
|
|
21220
|
+
const closeLen = /^\s*<\/function>/.exec(rest)?.[0].length;
|
|
21221
|
+
if (closeLen !== undefined) {
|
|
21222
|
+
cursor += closeLen;
|
|
21223
|
+
break;
|
|
21224
|
+
}
|
|
21225
|
+
if (/^\s*<function=/.test(rest))
|
|
21226
|
+
break;
|
|
21227
|
+
const lead = /^\s*/.exec(rest)?.[0].length ?? 0;
|
|
21228
|
+
PARAMETER_TAG_AT_CURSOR.lastIndex = cursor + lead;
|
|
21229
|
+
const param = PARAMETER_TAG_AT_CURSOR.exec(body);
|
|
21230
|
+
if (!param)
|
|
21231
|
+
return null;
|
|
21232
|
+
let valueStart = PARAMETER_TAG_AT_CURSOR.lastIndex;
|
|
21233
|
+
if (body[valueStart] === `
|
|
21234
|
+
`)
|
|
21235
|
+
valueStart += 1;
|
|
21236
|
+
const tail = body.slice(valueStart);
|
|
21237
|
+
const end = /<\/parameter>|<\/function>|<parameter=|<function=/.exec(tail);
|
|
21238
|
+
const raw = end ? tail.slice(0, end.index) : tail;
|
|
21239
|
+
args[param[1]] = raw.trim();
|
|
21240
|
+
cursor = valueStart + raw.length;
|
|
21241
|
+
const consumedClose = /^<\/parameter>/.exec(body.slice(cursor))?.[0].length;
|
|
21242
|
+
if (consumedClose !== undefined)
|
|
21243
|
+
cursor += consumedClose;
|
|
21244
|
+
}
|
|
21245
|
+
calls.push({ name: open[1], arguments: args, source: "xml_text" });
|
|
21246
|
+
const gap = /^\s*/.exec(body.slice(cursor))?.[0].length ?? 0;
|
|
21247
|
+
cursor += gap;
|
|
21248
|
+
}
|
|
21249
|
+
return calls.length > 0 ? calls : null;
|
|
21250
|
+
}
|
|
21251
|
+
function normalizeAgainstSchema(call, toolSchemas) {
|
|
21252
|
+
const schema = toolSchemas?.find((t) => t.name === call.name)?.input_schema;
|
|
21253
|
+
if (!schema)
|
|
21254
|
+
return call;
|
|
21255
|
+
const renamed = renameToDeclaredKeys(schema, call.arguments);
|
|
21256
|
+
const defaulted = applySchemaDefaults(schema, renamed.args);
|
|
21257
|
+
const coerced = coerceToSchema(schema, defaulted.args);
|
|
21258
|
+
if (renamed.renamed.length + defaulted.applied.length + coerced.coerced.length === 0) {
|
|
21259
|
+
return call;
|
|
21260
|
+
}
|
|
21261
|
+
log(`[ToolRecovery] Normalized ${call.name} against its schema: ` + `renamed=[${renamed.renamed}] defaults=[${defaulted.applied}] coerced=[${coerced.coerced}]`);
|
|
21262
|
+
return { ...call, arguments: coerced.args };
|
|
21263
|
+
}
|
|
21264
|
+
function extractToolCallsFromText(text, knownToolNames, toolSchemas, decodeToolName) {
|
|
21265
|
+
const envelope = parseFunctionTagEnvelope(text);
|
|
21266
|
+
if (envelope) {
|
|
21267
|
+
return keepOnlyRealTools(envelope, knownToolNames, decodeToolName).map((call) => normalizeAgainstSchema(call, toolSchemas));
|
|
21268
|
+
}
|
|
20596
21269
|
const extracted = [];
|
|
20597
21270
|
const qwenPattern = new RegExp(FUNCTION_TAG_SOURCE, "gi");
|
|
20598
21271
|
let match;
|
|
@@ -20719,73 +21392,6 @@ function extractToolCallsFromText(text, knownToolNames) {
|
|
|
20719
21392
|
while ((kvMatch = kvPattern.exec(paramText)) !== null) {
|
|
20720
21393
|
args[kvMatch[1]] = kvMatch[2];
|
|
20721
21394
|
}
|
|
20722
|
-
const quotedPattern = /["']([^"']+)["']/g;
|
|
20723
|
-
let quotedMatch;
|
|
20724
|
-
const quotedValues = [];
|
|
20725
|
-
while ((quotedMatch = quotedPattern.exec(paramText)) !== null) {
|
|
20726
|
-
quotedValues.push(quotedMatch[1]);
|
|
20727
|
-
}
|
|
20728
|
-
if (normalizedToolName === "Task") {
|
|
20729
|
-
if (!args.subagent_type) {
|
|
20730
|
-
const stMatch = paramText.match(/subagent_type\s*[=:]\s*["']?(\w+)["']?/i);
|
|
20731
|
-
if (stMatch) {
|
|
20732
|
-
args.subagent_type = stMatch[1];
|
|
20733
|
-
} else if (/explore|codebase|structure/i.test(paramText)) {
|
|
20734
|
-
args.subagent_type = "Explore";
|
|
20735
|
-
} else if (/plan|architect/i.test(paramText)) {
|
|
20736
|
-
args.subagent_type = "Plan";
|
|
20737
|
-
} else {
|
|
20738
|
-
args.subagent_type = "general-purpose";
|
|
20739
|
-
}
|
|
20740
|
-
}
|
|
20741
|
-
if (!args.prompt) {
|
|
20742
|
-
const toMatch = paramText.match(/\bto\s+(.+)/i);
|
|
20743
|
-
if (toMatch) {
|
|
20744
|
-
args.prompt = toMatch[1].trim();
|
|
20745
|
-
} else {
|
|
20746
|
-
args.prompt = paramText.trim();
|
|
20747
|
-
}
|
|
20748
|
-
}
|
|
20749
|
-
if (!args.description) {
|
|
20750
|
-
args.description = (args.prompt || paramText).substring(0, 50).trim();
|
|
20751
|
-
}
|
|
20752
|
-
} else if (normalizedToolName === "Read") {
|
|
20753
|
-
if (!args.file_path) {
|
|
20754
|
-
if (quotedValues.length > 0) {
|
|
20755
|
-
args.file_path = quotedValues[0];
|
|
20756
|
-
} else {
|
|
20757
|
-
const pathMatch = paramText.match(/(?:read|file)\s+([\/\w.-]+)/i);
|
|
20758
|
-
if (pathMatch) {
|
|
20759
|
-
args.file_path = pathMatch[1];
|
|
20760
|
-
}
|
|
20761
|
-
}
|
|
20762
|
-
}
|
|
20763
|
-
} else if (normalizedToolName === "Bash") {
|
|
20764
|
-
if (!args.command) {
|
|
20765
|
-
if (quotedValues.length > 0) {
|
|
20766
|
-
args.command = quotedValues[0];
|
|
20767
|
-
} else {
|
|
20768
|
-
const cmdMatch = paramText.match(/(?:run|execute)\s+(.+)/i);
|
|
20769
|
-
if (cmdMatch) {
|
|
20770
|
-
args.command = cmdMatch[1].trim();
|
|
20771
|
-
}
|
|
20772
|
-
}
|
|
20773
|
-
}
|
|
20774
|
-
if (args.command && !args.description) {
|
|
20775
|
-
args.description = `Run ${args.command.split(" ")[0]} command`;
|
|
20776
|
-
}
|
|
20777
|
-
} else if (normalizedToolName === "Grep" || normalizedToolName === "Glob") {
|
|
20778
|
-
if (!args.pattern) {
|
|
20779
|
-
if (quotedValues.length > 0) {
|
|
20780
|
-
args.pattern = quotedValues[0];
|
|
20781
|
-
} else {
|
|
20782
|
-
const searchMatch = paramText.match(/(?:search|find|look for)\s+(.+)/i);
|
|
20783
|
-
if (searchMatch) {
|
|
20784
|
-
args.pattern = searchMatch[1].trim();
|
|
20785
|
-
}
|
|
20786
|
-
}
|
|
20787
|
-
}
|
|
20788
|
-
}
|
|
20789
21395
|
if (Object.keys(args).length > 0) {
|
|
20790
21396
|
extracted.push({
|
|
20791
21397
|
name: normalizedToolName,
|
|
@@ -20796,139 +21402,7 @@ function extractToolCallsFromText(text, knownToolNames) {
|
|
|
20796
21402
|
}
|
|
20797
21403
|
}
|
|
20798
21404
|
}
|
|
20799
|
-
return keepOnlyRealTools(extracted, knownToolNames);
|
|
20800
|
-
}
|
|
20801
|
-
function inferMissingParameters(toolName, args, missingParams, context) {
|
|
20802
|
-
const inferred = { ...args };
|
|
20803
|
-
if (toolName === "Task") {
|
|
20804
|
-
const validSubagentTypes = [
|
|
20805
|
-
"general-purpose",
|
|
20806
|
-
"Explore",
|
|
20807
|
-
"Plan",
|
|
20808
|
-
"claude-code-guide",
|
|
20809
|
-
"code-analysis:detective",
|
|
20810
|
-
"feature-dev:code-architect",
|
|
20811
|
-
"feature-dev:code-explorer",
|
|
20812
|
-
"feature-dev:code-reviewer"
|
|
20813
|
-
];
|
|
20814
|
-
if (inferred.subagent_type) {
|
|
20815
|
-
const st = inferred.subagent_type.toLowerCase();
|
|
20816
|
-
if (st.includes("explore") || st.includes("codebase") || st.includes("file")) {
|
|
20817
|
-
inferred.subagent_type = "Explore";
|
|
20818
|
-
} else if (st.includes("plan") || st.includes("architect")) {
|
|
20819
|
-
inferred.subagent_type = "Plan";
|
|
20820
|
-
} else if (st.includes("analysis") || st.includes("analyz") || st.includes("config") || st.includes("git") || st.includes("test") || st.includes("doc") || st.includes("version")) {
|
|
20821
|
-
inferred.subagent_type = "general-purpose";
|
|
20822
|
-
} else if (!validSubagentTypes.includes(inferred.subagent_type)) {
|
|
20823
|
-
log(`[ToolRecovery] Unknown subagent_type "${inferred.subagent_type}", mapping to general-purpose`);
|
|
20824
|
-
inferred.subagent_type = "general-purpose";
|
|
20825
|
-
}
|
|
20826
|
-
}
|
|
20827
|
-
if (missingParams.includes("subagent_type") && !inferred.subagent_type) {
|
|
20828
|
-
inferred.subagent_type = "general-purpose";
|
|
20829
|
-
log("[ToolRecovery] Inferred subagent_type: general-purpose");
|
|
20830
|
-
}
|
|
20831
|
-
let extractedTask = "";
|
|
20832
|
-
if (context) {
|
|
20833
|
-
const patterns = [
|
|
20834
|
-
/(?:I(?:'ll| will| need to| want to| am going to)|Let me|Going to)\s+([^.!?\n]+)/i,
|
|
20835
|
-
/(?:help you|assist with)\s+([^.!?\n]+)/i,
|
|
20836
|
-
/(?:explore|search|find|look for|investigate)\s+([^.!?\n]+)/i,
|
|
20837
|
-
/(?:implement|create|build|add|fix|update)\s+([^.!?\n]+)/i
|
|
20838
|
-
];
|
|
20839
|
-
for (const pattern of patterns) {
|
|
20840
|
-
const match = context.match(pattern);
|
|
20841
|
-
if (match?.[1] && match[1].length > 10) {
|
|
20842
|
-
extractedTask = match[1].trim();
|
|
20843
|
-
log(`[ToolRecovery] Extracted task from context: "${extractedTask.substring(0, 50)}..."`);
|
|
20844
|
-
break;
|
|
20845
|
-
}
|
|
20846
|
-
}
|
|
20847
|
-
if (!extractedTask && context.length > 20) {
|
|
20848
|
-
const sentences = context.split(/[.!?\n]+/).filter((s) => s.trim().length > 15);
|
|
20849
|
-
if (sentences.length > 0) {
|
|
20850
|
-
extractedTask = sentences[sentences.length - 1].trim();
|
|
20851
|
-
}
|
|
20852
|
-
}
|
|
20853
|
-
}
|
|
20854
|
-
if (missingParams.includes("prompt") && !inferred.prompt) {
|
|
20855
|
-
if (inferred.query) {
|
|
20856
|
-
inferred.prompt = inferred.query;
|
|
20857
|
-
log(`[ToolRecovery] Mapped query -> prompt: "${inferred.query.substring(0, 50)}..."`);
|
|
20858
|
-
} else if (inferred.description && inferred.description !== "Execute task") {
|
|
20859
|
-
inferred.prompt = inferred.description;
|
|
20860
|
-
} else if (inferred.task) {
|
|
20861
|
-
inferred.prompt = inferred.task;
|
|
20862
|
-
} else if (extractedTask) {
|
|
20863
|
-
inferred.prompt = extractedTask;
|
|
20864
|
-
} else if (context && context.length > 20) {
|
|
20865
|
-
inferred.prompt = context.substring(0, 500).trim();
|
|
20866
|
-
}
|
|
20867
|
-
if (inferred.prompt) {
|
|
20868
|
-
log(`[ToolRecovery] Inferred prompt: "${inferred.prompt.substring(0, 50)}..."`);
|
|
20869
|
-
}
|
|
20870
|
-
}
|
|
20871
|
-
if (missingParams.includes("description") && !inferred.description) {
|
|
20872
|
-
if (inferred.prompt) {
|
|
20873
|
-
inferred.description = inferred.prompt.substring(0, 50).replace(/\s+/g, " ").trim();
|
|
20874
|
-
if (inferred.description.length < inferred.prompt.length) {
|
|
20875
|
-
inferred.description += "...";
|
|
20876
|
-
}
|
|
20877
|
-
} else if (extractedTask) {
|
|
20878
|
-
inferred.description = extractedTask.substring(0, 50).trim();
|
|
20879
|
-
} else {
|
|
20880
|
-
inferred.description = "Execute task";
|
|
20881
|
-
}
|
|
20882
|
-
log(`[ToolRecovery] Inferred description: ${inferred.description}`);
|
|
20883
|
-
}
|
|
20884
|
-
}
|
|
20885
|
-
if (toolName === "Bash") {
|
|
20886
|
-
if (missingParams.includes("command") && !inferred.command) {
|
|
20887
|
-
inferred.command = inferred.cmd || inferred.shell || inferred.script || "";
|
|
20888
|
-
}
|
|
20889
|
-
if (missingParams.includes("description") && !inferred.description) {
|
|
20890
|
-
if (inferred.command) {
|
|
20891
|
-
const cmd = inferred.command.split(" ")[0];
|
|
20892
|
-
inferred.description = `Run ${cmd} command`;
|
|
20893
|
-
}
|
|
20894
|
-
}
|
|
20895
|
-
}
|
|
20896
|
-
if (toolName === "Read") {
|
|
20897
|
-
if (missingParams.includes("file_path") && !inferred.file_path) {
|
|
20898
|
-
inferred.file_path = inferred.path || inferred.file || inferred.filename || "";
|
|
20899
|
-
}
|
|
20900
|
-
}
|
|
20901
|
-
if (toolName === "Write") {
|
|
20902
|
-
if (missingParams.includes("file_path") && !inferred.file_path) {
|
|
20903
|
-
inferred.file_path = inferred.path || inferred.file || inferred.filename || "";
|
|
20904
|
-
}
|
|
20905
|
-
if (missingParams.includes("content") && !inferred.content) {
|
|
20906
|
-
inferred.content = inferred.text || inferred.data || inferred.body || "";
|
|
20907
|
-
}
|
|
20908
|
-
}
|
|
20909
|
-
if (toolName === "Grep") {
|
|
20910
|
-
if (missingParams.includes("pattern") && !inferred.pattern) {
|
|
20911
|
-
inferred.pattern = inferred.query || inferred.search || inferred.regex || "";
|
|
20912
|
-
}
|
|
20913
|
-
}
|
|
20914
|
-
if (toolName === "Glob") {
|
|
20915
|
-
if (missingParams.includes("pattern") && !inferred.pattern) {
|
|
20916
|
-
inferred.pattern = inferred.glob || inferred.path || inferred.search || "**/*";
|
|
20917
|
-
}
|
|
20918
|
-
}
|
|
20919
|
-
if (toolName === "ToolSearch") {
|
|
20920
|
-
if (missingParams.includes("max_results") && inferred.max_results === undefined) {
|
|
20921
|
-
inferred.max_results = 5;
|
|
20922
|
-
log("[ToolRecovery] Inferred max_results: 5 (default)");
|
|
20923
|
-
}
|
|
20924
|
-
if (missingParams.includes("query") && !inferred.query) {
|
|
20925
|
-
inferred.query = inferred.search || inferred.keyword || inferred.tool || "";
|
|
20926
|
-
if (inferred.query) {
|
|
20927
|
-
log(`[ToolRecovery] Inferred ToolSearch query: "${inferred.query}"`);
|
|
20928
|
-
}
|
|
20929
|
-
}
|
|
20930
|
-
}
|
|
20931
|
-
return inferred;
|
|
21405
|
+
return keepOnlyRealTools(extracted, knownToolNames, decodeToolName);
|
|
20932
21406
|
}
|
|
20933
21407
|
function validateAndRepairToolCall(toolName, argsStr, toolSchemas, textContent) {
|
|
20934
21408
|
const schema = toolSchemas.find((t) => t.name === toolName);
|
|
@@ -20940,7 +21414,7 @@ function validateAndRepairToolCall(toolName, argsStr, toolSchemas, textContent)
|
|
|
20940
21414
|
parsedArgs = argsStr ? JSON.parse(argsStr) : {};
|
|
20941
21415
|
} catch (e) {
|
|
20942
21416
|
if (textContent) {
|
|
20943
|
-
const extracted = extractToolCallsFromText(textContent);
|
|
21417
|
+
const extracted = extractToolCallsFromText(textContent, toolSchemas.map((t) => t.name), toolSchemas);
|
|
20944
21418
|
const matching = extracted.find((tc) => tc.name === toolName);
|
|
20945
21419
|
if (matching) {
|
|
20946
21420
|
parsedArgs = matching.arguments;
|
|
@@ -20948,25 +21422,33 @@ function validateAndRepairToolCall(toolName, argsStr, toolSchemas, textContent)
|
|
|
20948
21422
|
}
|
|
20949
21423
|
}
|
|
20950
21424
|
}
|
|
20951
|
-
const
|
|
20952
|
-
const
|
|
20953
|
-
|
|
20954
|
-
|
|
21425
|
+
const input = schema.input_schema;
|
|
21426
|
+
const renamed = renameToDeclaredKeys(input, parsedArgs);
|
|
21427
|
+
const defaulted = applySchemaDefaults(input, renamed.args);
|
|
21428
|
+
const coerced = coerceToSchema(input, defaulted.args);
|
|
21429
|
+
const args = coerced.args;
|
|
21430
|
+
const repaired = renamed.renamed.length > 0 || defaulted.applied.length > 0;
|
|
21431
|
+
if (repaired) {
|
|
21432
|
+
log(`[ToolRecovery] ${toolName}: applied schema defaults [${defaulted.applied}] ` + `and key renames [${renamed.renamed}] \u2014 nothing was invented`);
|
|
20955
21433
|
}
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
|
|
21434
|
+
if (coerced.coerced.length > 0) {
|
|
21435
|
+
log(`[ToolRecovery] ${toolName}: coerced to declared types [${coerced.coerced}]`);
|
|
21436
|
+
}
|
|
21437
|
+
const missingParams = missingRequired(input, args);
|
|
21438
|
+
if (missingParams.length === 0) {
|
|
21439
|
+
return { valid: true, args, repaired, missingParams: [] };
|
|
20961
21440
|
}
|
|
20962
|
-
return { valid: false, args
|
|
21441
|
+
return { valid: false, args, repaired: false, missingParams };
|
|
20963
21442
|
}
|
|
20964
|
-
var FUNCTION_TAG_SOURCE, FUNCTION_TAG_PRESENT;
|
|
21443
|
+
var FUNCTION_TAG_SOURCE, FUNCTION_TAG_PRESENT, FUNCTION_TAG_AT_CURSOR, PARAMETER_TAG_AT_CURSOR;
|
|
20965
21444
|
var init_tool_call_recovery = __esm(() => {
|
|
20966
21445
|
init_tool_name_utils();
|
|
20967
21446
|
init_logger();
|
|
21447
|
+
init_schema_validate();
|
|
20968
21448
|
FUNCTION_TAG_SOURCE = `<function=(${TOOL_NAME_SOURCE})>([\\s\\S]*?)(?=<function=|$)`;
|
|
20969
21449
|
FUNCTION_TAG_PRESENT = new RegExp(`<function=${TOOL_NAME_SOURCE}>`);
|
|
21450
|
+
FUNCTION_TAG_AT_CURSOR = new RegExp(`<function=(${TOOL_NAME_SOURCE})>`, "y");
|
|
21451
|
+
PARAMETER_TAG_AT_CURSOR = /<parameter=([^>\s]+)>/y;
|
|
20970
21452
|
});
|
|
20971
21453
|
|
|
20972
21454
|
// src/handlers/shared/web-search-detector.ts
|
|
@@ -20983,11 +21465,218 @@ var init_web_search_detector = __esm(() => {
|
|
|
20983
21465
|
WEB_SEARCH_NAMES = new Set(["web_search", "brave_web_search", "tavily_search"]);
|
|
20984
21466
|
});
|
|
20985
21467
|
|
|
21468
|
+
// src/handlers/shared/stream-parsers/block-writer.ts
|
|
21469
|
+
function createBlockWriter(send) {
|
|
21470
|
+
let nextIndex = 0;
|
|
21471
|
+
let openRef = null;
|
|
21472
|
+
let anyBlockEmitted = false;
|
|
21473
|
+
const emittedToolRefs = [];
|
|
21474
|
+
const stopped = new Set;
|
|
21475
|
+
const describe = (ref) => ref ? `${ref.kind}@${ref.index}` : "none";
|
|
21476
|
+
const spendableIndex = (requested, kind) => {
|
|
21477
|
+
if (requested === undefined)
|
|
21478
|
+
return nextIndex++;
|
|
21479
|
+
if (!stopped.has(requested))
|
|
21480
|
+
return requested;
|
|
21481
|
+
const replacement = nextIndex++;
|
|
21482
|
+
log(`[BlockWriter] open error: index ${requested} was already stopped; ${kind} block re-indexed to ${replacement}`);
|
|
21483
|
+
return replacement;
|
|
21484
|
+
};
|
|
21485
|
+
const closeCurrent = () => {
|
|
21486
|
+
if (!openRef)
|
|
21487
|
+
return;
|
|
21488
|
+
send("content_block_stop", { type: "content_block_stop", index: openRef.index });
|
|
21489
|
+
stopped.add(openRef.index);
|
|
21490
|
+
openRef = null;
|
|
21491
|
+
};
|
|
21492
|
+
const start = (ref, contentBlock) => {
|
|
21493
|
+
send("content_block_start", {
|
|
21494
|
+
type: "content_block_start",
|
|
21495
|
+
index: ref.index,
|
|
21496
|
+
content_block: contentBlock
|
|
21497
|
+
});
|
|
21498
|
+
openRef = ref;
|
|
21499
|
+
anyBlockEmitted = true;
|
|
21500
|
+
return ref;
|
|
21501
|
+
};
|
|
21502
|
+
const writer = {
|
|
21503
|
+
openText(opts) {
|
|
21504
|
+
if (openRef?.kind === "text") {
|
|
21505
|
+
if (opts?.index !== undefined && opts.index !== openRef.index) {
|
|
21506
|
+
log(`[BlockWriter] reusing open text block @${openRef.index}; reserved index ${opts.index} goes unused`);
|
|
21507
|
+
}
|
|
21508
|
+
return openRef;
|
|
21509
|
+
}
|
|
21510
|
+
closeCurrent();
|
|
21511
|
+
const index = spendableIndex(opts?.index, "text");
|
|
21512
|
+
return start({ index, kind: "text" }, { type: "text", text: "" });
|
|
21513
|
+
},
|
|
21514
|
+
openThinking() {
|
|
21515
|
+
if (openRef?.kind === "thinking")
|
|
21516
|
+
return openRef;
|
|
21517
|
+
closeCurrent();
|
|
21518
|
+
return start({ index: nextIndex++, kind: "thinking" }, { type: "thinking", thinking: "" });
|
|
21519
|
+
},
|
|
21520
|
+
openTool({ id, name, index }) {
|
|
21521
|
+
closeCurrent();
|
|
21522
|
+
const ref = {
|
|
21523
|
+
index: spendableIndex(index, "tool_use"),
|
|
21524
|
+
kind: "tool_use",
|
|
21525
|
+
toolId: id
|
|
21526
|
+
};
|
|
21527
|
+
start(ref, { type: "tool_use", id, name, input: {} });
|
|
21528
|
+
emittedToolRefs.push(ref);
|
|
21529
|
+
return ref;
|
|
21530
|
+
},
|
|
21531
|
+
append(ref, payload) {
|
|
21532
|
+
if (!openRef || openRef.index !== ref.index || openRef.kind !== ref.kind) {
|
|
21533
|
+
log(`[BlockWriter] append error: ${describe(ref)} is not the open block (open=${describe(openRef)}); ${payload.length} chars withheld for the caller to handle`);
|
|
21534
|
+
return false;
|
|
21535
|
+
}
|
|
21536
|
+
const delta = ref.kind === "text" ? { type: "text_delta", text: payload } : ref.kind === "thinking" ? { type: "thinking_delta", thinking: payload } : { type: "input_json_delta", partial_json: payload };
|
|
21537
|
+
send("content_block_delta", { type: "content_block_delta", index: ref.index, delta });
|
|
21538
|
+
return true;
|
|
21539
|
+
},
|
|
21540
|
+
close(ref) {
|
|
21541
|
+
if (openRef && openRef.index === ref.index && openRef.kind === ref.kind) {
|
|
21542
|
+
closeCurrent();
|
|
21543
|
+
return;
|
|
21544
|
+
}
|
|
21545
|
+
if (stopped.has(ref.index))
|
|
21546
|
+
return;
|
|
21547
|
+
log(`[BlockWriter] close error: ${describe(ref)} was never opened and is not stopped (open=${describe(openRef)})`);
|
|
21548
|
+
},
|
|
21549
|
+
closeCurrent,
|
|
21550
|
+
reserve() {
|
|
21551
|
+
return nextIndex++;
|
|
21552
|
+
},
|
|
21553
|
+
get openRef() {
|
|
21554
|
+
return openRef;
|
|
21555
|
+
},
|
|
21556
|
+
get anyBlockEmitted() {
|
|
21557
|
+
return anyBlockEmitted;
|
|
21558
|
+
},
|
|
21559
|
+
get emittedToolRefs() {
|
|
21560
|
+
return emittedToolRefs;
|
|
21561
|
+
}
|
|
21562
|
+
};
|
|
21563
|
+
return writer;
|
|
21564
|
+
}
|
|
21565
|
+
var init_block_writer = __esm(() => {
|
|
21566
|
+
init_logger();
|
|
21567
|
+
});
|
|
21568
|
+
|
|
20986
21569
|
// src/handlers/shared/stream-parsers/message-start-usage.ts
|
|
20987
21570
|
function messageStartUsage(priorInputTokens) {
|
|
20988
21571
|
return {
|
|
20989
21572
|
input_tokens: priorInputTokens && priorInputTokens > 0 ? priorInputTokens : 100,
|
|
20990
|
-
output_tokens: 1
|
|
21573
|
+
output_tokens: 1,
|
|
21574
|
+
cache_read_input_tokens: 0,
|
|
21575
|
+
cache_creation_input_tokens: 0
|
|
21576
|
+
};
|
|
21577
|
+
}
|
|
21578
|
+
|
|
21579
|
+
// src/handlers/shared/stream-parsers/think-tag-splitter.ts
|
|
21580
|
+
function createThinkTagSplitter() {
|
|
21581
|
+
let phase = "deciding";
|
|
21582
|
+
let pending = "";
|
|
21583
|
+
let openArmed = true;
|
|
21584
|
+
const heldSuffixLength = (s) => {
|
|
21585
|
+
for (let n = Math.min(CLOSE.length - 1, s.length);n > 0; n--) {
|
|
21586
|
+
if (CLOSE.startsWith(s.slice(s.length - n)))
|
|
21587
|
+
return n;
|
|
21588
|
+
}
|
|
21589
|
+
return 0;
|
|
21590
|
+
};
|
|
21591
|
+
const consumeThinking = (chunk) => {
|
|
21592
|
+
pending += chunk;
|
|
21593
|
+
const at = pending.indexOf(CLOSE);
|
|
21594
|
+
if (at >= 0) {
|
|
21595
|
+
const thinking = pending.slice(0, at);
|
|
21596
|
+
const text = pending.slice(at + CLOSE.length);
|
|
21597
|
+
pending = "";
|
|
21598
|
+
phase = "passthrough";
|
|
21599
|
+
return { thinking, text };
|
|
21600
|
+
}
|
|
21601
|
+
const hold = heldSuffixLength(pending);
|
|
21602
|
+
const thinking = pending.slice(0, pending.length - hold);
|
|
21603
|
+
pending = hold === 0 ? "" : pending.slice(pending.length - hold);
|
|
21604
|
+
return { thinking, text: "" };
|
|
21605
|
+
};
|
|
21606
|
+
const decide = (isFlush) => {
|
|
21607
|
+
const lead = pending.replace(/^\s+/, "");
|
|
21608
|
+
const releaseAsText = () => {
|
|
21609
|
+
phase = "passthrough";
|
|
21610
|
+
const text = pending;
|
|
21611
|
+
pending = "";
|
|
21612
|
+
return { thinking: "", text };
|
|
21613
|
+
};
|
|
21614
|
+
if (lead === "")
|
|
21615
|
+
return isFlush ? releaseAsText() : EMPTY;
|
|
21616
|
+
if (openArmed && lead.startsWith(OPEN)) {
|
|
21617
|
+
phase = "thinking";
|
|
21618
|
+
pending = "";
|
|
21619
|
+
return consumeThinking(lead.slice(OPEN.length));
|
|
21620
|
+
}
|
|
21621
|
+
if (lead.startsWith(CLOSE)) {
|
|
21622
|
+
phase = "passthrough";
|
|
21623
|
+
pending = "";
|
|
21624
|
+
return { thinking: "", text: lead.slice(CLOSE.length) };
|
|
21625
|
+
}
|
|
21626
|
+
if (openArmed && OPEN.startsWith(lead) || CLOSE.startsWith(lead)) {
|
|
21627
|
+
return isFlush ? releaseAsText() : EMPTY;
|
|
21628
|
+
}
|
|
21629
|
+
return releaseAsText();
|
|
21630
|
+
};
|
|
21631
|
+
return {
|
|
21632
|
+
push(chunk) {
|
|
21633
|
+
if (!chunk)
|
|
21634
|
+
return EMPTY;
|
|
21635
|
+
if (phase === "passthrough")
|
|
21636
|
+
return { thinking: "", text: chunk };
|
|
21637
|
+
if (phase === "thinking")
|
|
21638
|
+
return consumeThinking(chunk);
|
|
21639
|
+
pending += chunk;
|
|
21640
|
+
return decide(false);
|
|
21641
|
+
},
|
|
21642
|
+
flush() {
|
|
21643
|
+
if (phase === "passthrough")
|
|
21644
|
+
return EMPTY;
|
|
21645
|
+
if (phase === "thinking") {
|
|
21646
|
+
const thinking = pending;
|
|
21647
|
+
pending = "";
|
|
21648
|
+
return { thinking, text: "" };
|
|
21649
|
+
}
|
|
21650
|
+
return decide(true);
|
|
21651
|
+
},
|
|
21652
|
+
disarmOpen() {
|
|
21653
|
+
openArmed = false;
|
|
21654
|
+
},
|
|
21655
|
+
get inThinking() {
|
|
21656
|
+
return phase === "thinking";
|
|
21657
|
+
}
|
|
21658
|
+
};
|
|
21659
|
+
}
|
|
21660
|
+
var OPEN = "<think>", CLOSE = "</think>", EMPTY;
|
|
21661
|
+
var init_think_tag_splitter = __esm(() => {
|
|
21662
|
+
EMPTY = { thinking: "", text: "" };
|
|
21663
|
+
});
|
|
21664
|
+
|
|
21665
|
+
// src/handlers/shared/stream-parsers/usage-cache-split.ts
|
|
21666
|
+
function nonNegativeInt(value) {
|
|
21667
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
|
|
21668
|
+
}
|
|
21669
|
+
function splitPromptTokens(usage) {
|
|
21670
|
+
const u = usage ?? {};
|
|
21671
|
+
const promptTokens = nonNegativeInt(u.prompt_tokens);
|
|
21672
|
+
const details = u.prompt_tokens_details ?? {};
|
|
21673
|
+
const cacheReadTokens = Math.min(nonNegativeInt(details.cached_tokens), promptTokens);
|
|
21674
|
+
const cacheCreationTokens = Math.min(nonNegativeInt(details.cache_write_tokens), promptTokens - cacheReadTokens);
|
|
21675
|
+
return {
|
|
21676
|
+
promptTokens,
|
|
21677
|
+
inputTokens: promptTokens - cacheReadTokens - cacheCreationTokens,
|
|
21678
|
+
cacheReadTokens,
|
|
21679
|
+
cacheCreationTokens
|
|
20991
21680
|
};
|
|
20992
21681
|
}
|
|
20993
21682
|
|
|
@@ -20997,6 +21686,31 @@ function formatRawSseLogPayload(dataStr) {
|
|
|
20997
21686
|
return dataStr;
|
|
20998
21687
|
return `${dataStr.substring(0, SSE_LOG_MAX_CHARS)} ${SSE_LOG_TRUNCATION_MARKER} original_chars=${dataStr.length}`;
|
|
20999
21688
|
}
|
|
21689
|
+
function describeInStreamError(chunk) {
|
|
21690
|
+
if (!chunk || typeof chunk !== "object")
|
|
21691
|
+
return;
|
|
21692
|
+
const error = chunk.error;
|
|
21693
|
+
if (!error)
|
|
21694
|
+
return;
|
|
21695
|
+
if (typeof error === "string")
|
|
21696
|
+
return error;
|
|
21697
|
+
if (typeof error !== "object")
|
|
21698
|
+
return String(error);
|
|
21699
|
+
const e = error;
|
|
21700
|
+
const metadata = e.metadata ?? {};
|
|
21701
|
+
const parts = [];
|
|
21702
|
+
const provider = chunk.provider;
|
|
21703
|
+
if (typeof provider === "string" && provider)
|
|
21704
|
+
parts.push(`[${provider}]`);
|
|
21705
|
+
const code = e.code ?? metadata.provider_code;
|
|
21706
|
+
const type = e.type ?? metadata.error_type;
|
|
21707
|
+
const label = [code, type].filter((v) => v !== undefined && v !== null && v !== "").join(" ");
|
|
21708
|
+
if (label)
|
|
21709
|
+
parts.push(label);
|
|
21710
|
+
const message = e.message ?? metadata.raw;
|
|
21711
|
+
parts.push(typeof message === "string" && message.trim() ? message : JSON.stringify(error).slice(0, 500));
|
|
21712
|
+
return parts.join(" ");
|
|
21713
|
+
}
|
|
21000
21714
|
function validateToolArguments(toolName, argsStr, toolSchemas, textContent) {
|
|
21001
21715
|
const result = validateAndRepairToolCall(toolName, argsStr, toolSchemas, textContent);
|
|
21002
21716
|
if (result.repaired) {
|
|
@@ -21014,12 +21728,9 @@ function createStreamingState() {
|
|
|
21014
21728
|
return {
|
|
21015
21729
|
usage: null,
|
|
21016
21730
|
finalized: false,
|
|
21017
|
-
textStarted: false,
|
|
21018
|
-
textIdx: -1,
|
|
21019
|
-
reasoningStarted: false,
|
|
21020
|
-
reasoningIdx: -1,
|
|
21021
|
-
curIdx: 0,
|
|
21022
21731
|
tools: new Map,
|
|
21732
|
+
pendingToolArgs: new Map,
|
|
21733
|
+
pendingToolName: new Map,
|
|
21023
21734
|
toolIds: new Set,
|
|
21024
21735
|
lastActivity: Date.now(),
|
|
21025
21736
|
accumulatedText: "",
|
|
@@ -21066,6 +21777,24 @@ data: ${JSON.stringify(d)}
|
|
|
21066
21777
|
};
|
|
21067
21778
|
const msgId = `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
21068
21779
|
const state = createStreamingState();
|
|
21780
|
+
const writer = createBlockWriter(send);
|
|
21781
|
+
const thinkSplitter = createThinkTagSplitter();
|
|
21782
|
+
const emitSplitContent = ({ thinking, text }) => {
|
|
21783
|
+
if (thinking) {
|
|
21784
|
+
behavior?.onAssistantText?.(thinking, "reasoning");
|
|
21785
|
+
writer.append(writer.openThinking(), thinking);
|
|
21786
|
+
}
|
|
21787
|
+
if (!text)
|
|
21788
|
+
return;
|
|
21789
|
+
state.accumulatedText += text;
|
|
21790
|
+
const hasStructuredToolPattern = hasExtractableFunctionTag(state.accumulatedText) || /\{\s*"(?:name|tool)"\s*:\s*"(?:Task|Read|Write|Edit|Bash|Grep|Glob)"/i.test(state.accumulatedText) || /<tool_call>/.test(state.accumulatedText);
|
|
21791
|
+
const shouldHoldBack = hasStructuredToolPattern && state.accumulatedText.length < 1000;
|
|
21792
|
+
if (shouldHoldBack) {
|
|
21793
|
+
log(`[Streaming] Text held back (structured tool pattern): ${state.accumulatedText.length} chars accumulated`);
|
|
21794
|
+
return;
|
|
21795
|
+
}
|
|
21796
|
+
writer.append(writer.openText(), text);
|
|
21797
|
+
};
|
|
21069
21798
|
send("message_start", {
|
|
21070
21799
|
type: "message_start",
|
|
21071
21800
|
message: {
|
|
@@ -21110,67 +21839,50 @@ data: ${JSON.stringify(d)}
|
|
|
21110
21839
|
}
|
|
21111
21840
|
state.finalized = true;
|
|
21112
21841
|
try {
|
|
21842
|
+
emitSplitContent(thinkSplitter.flush());
|
|
21843
|
+
for (const [idx, pending] of state.pendingToolArgs) {
|
|
21844
|
+
log(`[Streaming] Tool argument error: discarding ${pending.length} buffered chars for tool_calls index ${idx} \u2014 function.name never arrived`);
|
|
21845
|
+
}
|
|
21846
|
+
state.pendingToolArgs.clear();
|
|
21113
21847
|
if (state.accumulatedText.length > 0) {
|
|
21114
21848
|
const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
|
|
21115
21849
|
log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
|
|
21116
21850
|
}
|
|
21117
|
-
const textToolCalls = state.tools.size > 0 ? [] : extractToolCallsFromText(state.accumulatedText, toolSchemas?.map((t) => t?.name).filter((n) => !!n));
|
|
21851
|
+
const textToolCalls = state.tools.size > 0 ? [] : extractToolCallsFromText(state.accumulatedText, toolSchemas?.map((t) => t?.name).filter((n) => !!n), toolSchemas, toolNameMap ? (name) => toolNameMap.get(name) ?? name : undefined);
|
|
21118
21852
|
if (state.tools.size > 0 && state.accumulatedText.length > 0) {
|
|
21119
21853
|
log(`[Streaming] Skipping text-based tool extraction: ${state.tools.size} structured tool call(s) already present`);
|
|
21120
21854
|
}
|
|
21121
21855
|
log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
|
|
21122
|
-
|
|
21856
|
+
const producedContent = state.accumulatedText.length > 0 || writer.anyBlockEmitted || state.tools.size > 0 || textToolCalls.length > 0;
|
|
21857
|
+
const toolInFlight = writer.emittedToolRefs.length > 0 || Array.from(state.tools.values()).some((t) => !t.closed && (t.started || t.buffered)) || textToolCalls.length > 0;
|
|
21858
|
+
const ending = reason === "error" ? "failure" : state.finishReason !== null || !producedContent ? "success" : toolInFlight ? "failure" : "silent-truncation";
|
|
21859
|
+
if (ending !== "success") {
|
|
21860
|
+
log(`[Streaming] Stream ending error: reason=${reason} finish_reason=${state.finishReason ?? "null"} content=${producedContent} tool_in_flight=${toolInFlight} \u2192 ${ending}`);
|
|
21861
|
+
}
|
|
21862
|
+
const emitToolCalls = ending !== "failure";
|
|
21863
|
+
if (textToolCalls.length > 0 && !emitToolCalls) {
|
|
21864
|
+
log(`[Streaming] Suppressing ${textToolCalls.length} text-recovered tool call(s): the turn ended in failure`);
|
|
21865
|
+
} else if (textToolCalls.length > 0) {
|
|
21123
21866
|
log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
|
|
21124
|
-
if (state.textStarted) {
|
|
21125
|
-
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
21126
|
-
state.textStarted = false;
|
|
21127
|
-
}
|
|
21128
21867
|
for (const tc of textToolCalls) {
|
|
21129
|
-
const toolIdx =
|
|
21868
|
+
const toolIdx = writer.reserve();
|
|
21130
21869
|
const toolId = `tool_${Date.now()}_${toolIdx}`;
|
|
21131
|
-
|
|
21132
|
-
|
|
21133
|
-
|
|
21134
|
-
content_block: { type: "tool_use", id: toolId, name: tc.name }
|
|
21135
|
-
});
|
|
21136
|
-
send("content_block_delta", {
|
|
21137
|
-
type: "content_block_delta",
|
|
21138
|
-
index: toolIdx,
|
|
21139
|
-
delta: {
|
|
21140
|
-
type: "input_json_delta",
|
|
21141
|
-
partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
|
|
21142
|
-
}
|
|
21143
|
-
});
|
|
21144
|
-
send("content_block_stop", { type: "content_block_stop", index: toolIdx });
|
|
21870
|
+
const ref = writer.openTool({ id: toolId, name: tc.name, index: toolIdx });
|
|
21871
|
+
writer.append(ref, repairArgs(tc.name, JSON.stringify(tc.arguments)));
|
|
21872
|
+
writer.close(ref);
|
|
21145
21873
|
}
|
|
21146
21874
|
}
|
|
21147
|
-
|
|
21148
|
-
|
|
21149
|
-
}
|
|
21150
|
-
if (state.textStarted) {
|
|
21151
|
-
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
21152
|
-
}
|
|
21153
|
-
for (const t of Array.from(state.tools.values())) {
|
|
21875
|
+
writer.closeCurrent();
|
|
21876
|
+
for (const t of emitToolCalls ? Array.from(state.tools.values()) : []) {
|
|
21154
21877
|
if (!t.closed && t.buffered && !t.started) {
|
|
21155
21878
|
if (toolSchemas && toolSchemas.length > 0) {
|
|
21156
21879
|
const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
|
|
21157
21880
|
if (validation.valid || validation.repaired && validation.repairedArgs) {
|
|
21158
21881
|
const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
|
|
21159
21882
|
log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
|
|
21163
|
-
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
21164
|
-
});
|
|
21165
|
-
send("content_block_delta", {
|
|
21166
|
-
type: "content_block_delta",
|
|
21167
|
-
index: t.blockIndex,
|
|
21168
|
-
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
21169
|
-
});
|
|
21170
|
-
send("content_block_stop", {
|
|
21171
|
-
type: "content_block_stop",
|
|
21172
|
-
index: t.blockIndex
|
|
21173
|
-
});
|
|
21883
|
+
t.ref = writer.openTool({ id: t.id, name: t.name, index: t.blockIndex });
|
|
21884
|
+
writer.append(t.ref, argsJson);
|
|
21885
|
+
writer.close(t.ref);
|
|
21174
21886
|
t.started = true;
|
|
21175
21887
|
t.closed = true;
|
|
21176
21888
|
} else {
|
|
@@ -21180,20 +21892,9 @@ data: ${JSON.stringify(d)}
|
|
|
21180
21892
|
} else {
|
|
21181
21893
|
const argsJson = repairArgs(t.name, t.arguments || "{}");
|
|
21182
21894
|
log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
21187
|
-
});
|
|
21188
|
-
send("content_block_delta", {
|
|
21189
|
-
type: "content_block_delta",
|
|
21190
|
-
index: t.blockIndex,
|
|
21191
|
-
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
21192
|
-
});
|
|
21193
|
-
send("content_block_stop", {
|
|
21194
|
-
type: "content_block_stop",
|
|
21195
|
-
index: t.blockIndex
|
|
21196
|
-
});
|
|
21895
|
+
t.ref = writer.openTool({ id: t.id, name: t.name, index: t.blockIndex });
|
|
21896
|
+
writer.append(t.ref, argsJson);
|
|
21897
|
+
writer.close(t.ref);
|
|
21197
21898
|
t.started = true;
|
|
21198
21899
|
t.closed = true;
|
|
21199
21900
|
}
|
|
@@ -21201,28 +21902,41 @@ data: ${JSON.stringify(d)}
|
|
|
21201
21902
|
}
|
|
21202
21903
|
for (const t of Array.from(state.tools.values())) {
|
|
21203
21904
|
if (t.started && !t.closed) {
|
|
21204
|
-
|
|
21905
|
+
if (t.ref)
|
|
21906
|
+
writer.close(t.ref);
|
|
21205
21907
|
t.closed = true;
|
|
21206
21908
|
}
|
|
21207
21909
|
}
|
|
21208
21910
|
if (middlewareManager) {
|
|
21209
21911
|
await middlewareManager.afterStreamComplete(target, streamMetadata);
|
|
21210
21912
|
}
|
|
21211
|
-
if (
|
|
21212
|
-
|
|
21913
|
+
if (ending === "failure") {
|
|
21914
|
+
const message = err ?? "Upstream stream ended with no finish_reason while a tool call was in flight. " + "The tool call is incomplete and was not dispatched.";
|
|
21915
|
+
send("error", { type: "error", error: { type: "api_error", message } });
|
|
21213
21916
|
} else {
|
|
21214
21917
|
const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
|
|
21215
|
-
const truncated = state.finishReason === "length";
|
|
21918
|
+
const truncated = state.finishReason === "length" || ending === "silent-truncation";
|
|
21216
21919
|
const refused = state.finishReason === "content_filter";
|
|
21217
21920
|
const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
|
|
21218
21921
|
if (truncated || refused) {
|
|
21219
21922
|
log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
|
|
21220
21923
|
}
|
|
21924
|
+
if (stopReason === "end_turn" && !writer.anyBlockEmitted) {
|
|
21925
|
+
log(`[Streaming] Contentless turn error: end_turn with no content block emitted (finish_reason=${state.finishReason ?? "null"}) \u2014 emitting an empty text block`);
|
|
21926
|
+
writer.close(writer.openText());
|
|
21927
|
+
}
|
|
21928
|
+
const split = splitPromptTokens(state.usage);
|
|
21929
|
+
const fullyCached = split.promptTokens > 0 && split.inputTokens === 0;
|
|
21930
|
+
const wireInput = fullyCached ? split.promptTokens : split.inputTokens;
|
|
21931
|
+
const wireCacheRead = fullyCached ? 0 : split.cacheReadTokens;
|
|
21932
|
+
const wireCacheCreation = fullyCached ? 0 : split.cacheCreationTokens;
|
|
21221
21933
|
send("message_delta", {
|
|
21222
21934
|
type: "message_delta",
|
|
21223
21935
|
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
21224
21936
|
usage: {
|
|
21225
|
-
|
|
21937
|
+
input_tokens: wireInput,
|
|
21938
|
+
cache_read_input_tokens: wireCacheRead,
|
|
21939
|
+
cache_creation_input_tokens: wireCacheCreation,
|
|
21226
21940
|
output_tokens: state.usage?.completion_tokens || 0
|
|
21227
21941
|
}
|
|
21228
21942
|
});
|
|
@@ -21232,7 +21946,11 @@ data: ${JSON.stringify(d)}
|
|
|
21232
21946
|
if (onTokenUpdate) {
|
|
21233
21947
|
if (state.usage) {
|
|
21234
21948
|
log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
|
|
21235
|
-
|
|
21949
|
+
const costSplit = splitPromptTokens(state.usage);
|
|
21950
|
+
onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0, {
|
|
21951
|
+
cacheReadTokens: costSplit.cacheReadTokens,
|
|
21952
|
+
cacheCreationTokens: costSplit.cacheCreationTokens
|
|
21953
|
+
});
|
|
21236
21954
|
} else {
|
|
21237
21955
|
const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
|
|
21238
21956
|
log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
|
|
@@ -21269,6 +21987,12 @@ data: ${JSON.stringify(d)}
|
|
|
21269
21987
|
state.usage = chunk.usage;
|
|
21270
21988
|
log(`[Streaming] Usage data received: prompt=${chunk.usage.prompt_tokens}, completion=${chunk.usage.completion_tokens}, total=${chunk.usage.total_tokens}`);
|
|
21271
21989
|
}
|
|
21990
|
+
const inStreamError = describeInStreamError(chunk);
|
|
21991
|
+
if (inStreamError) {
|
|
21992
|
+
log(`[Streaming] Upstream error inside a 200 stream: ${inStreamError}`);
|
|
21993
|
+
await finalize("error", inStreamError);
|
|
21994
|
+
return;
|
|
21995
|
+
}
|
|
21272
21996
|
const delta = chunk.choices?.[0]?.delta;
|
|
21273
21997
|
const finishReason = chunk.choices?.[0]?.finish_reason;
|
|
21274
21998
|
if (finishReason)
|
|
@@ -21287,22 +22011,10 @@ data: ${JSON.stringify(d)}
|
|
|
21287
22011
|
}
|
|
21288
22012
|
const reasoningText = delta.reasoning_content || delta.reasoning;
|
|
21289
22013
|
if (reasoningText) {
|
|
22014
|
+
thinkSplitter.disarmOpen();
|
|
21290
22015
|
behavior?.onAssistantText?.(reasoningText, "reasoning");
|
|
21291
22016
|
state.lastActivity = Date.now();
|
|
21292
|
-
|
|
21293
|
-
state.reasoningIdx = state.curIdx++;
|
|
21294
|
-
send("content_block_start", {
|
|
21295
|
-
type: "content_block_start",
|
|
21296
|
-
index: state.reasoningIdx,
|
|
21297
|
-
content_block: { type: "thinking", thinking: "" }
|
|
21298
|
-
});
|
|
21299
|
-
state.reasoningStarted = true;
|
|
21300
|
-
}
|
|
21301
|
-
send("content_block_delta", {
|
|
21302
|
-
type: "content_block_delta",
|
|
21303
|
-
index: state.reasoningIdx,
|
|
21304
|
-
delta: { type: "thinking_delta", thinking: reasoningText }
|
|
21305
|
-
});
|
|
22017
|
+
writer.append(writer.openThinking(), reasoningText);
|
|
21306
22018
|
}
|
|
21307
22019
|
const txt = delta.content || "";
|
|
21308
22020
|
if (txt)
|
|
@@ -21310,42 +22022,13 @@ data: ${JSON.stringify(d)}
|
|
|
21310
22022
|
log(`[Streaming] Text chunk: "${txt.substring(0, 30).replace(/\n/g, "\\n")}" (${txt.length} chars)`);
|
|
21311
22023
|
if (txt) {
|
|
21312
22024
|
state.lastActivity = Date.now();
|
|
21313
|
-
if (state.reasoningStarted) {
|
|
21314
|
-
send("content_block_stop", {
|
|
21315
|
-
type: "content_block_stop",
|
|
21316
|
-
index: state.reasoningIdx
|
|
21317
|
-
});
|
|
21318
|
-
state.reasoningStarted = false;
|
|
21319
|
-
}
|
|
21320
22025
|
const res = adapter.processTextContent(txt, "");
|
|
21321
22026
|
log(`[Streaming] After adapter: "${res.cleanedText.substring(0, 30).replace(/\n/g, "\\n")}" (${res.cleanedText.length} chars, transformed=${res.wasTransformed})`);
|
|
21322
22027
|
if (txt.length > 0 && res.cleanedText.length === 0) {
|
|
21323
22028
|
log(`[Streaming] Text filtered out by adapter: "${txt.substring(0, 50)}"`);
|
|
21324
22029
|
}
|
|
21325
22030
|
if (res.cleanedText) {
|
|
21326
|
-
|
|
21327
|
-
const hasStructuredToolPattern = hasExtractableFunctionTag(state.accumulatedText) || /\{\s*"(?:name|tool)"\s*:\s*"(?:Task|Read|Write|Edit|Bash|Grep|Glob)"/i.test(state.accumulatedText) || /<tool_call>/.test(state.accumulatedText);
|
|
21328
|
-
const shouldHoldBack = hasStructuredToolPattern && state.accumulatedText.length < 1000;
|
|
21329
|
-
if (shouldHoldBack) {
|
|
21330
|
-
log(`[Streaming] Text held back (structured tool pattern): ${state.accumulatedText.length} chars accumulated`);
|
|
21331
|
-
}
|
|
21332
|
-
if (!shouldHoldBack) {
|
|
21333
|
-
if (!state.textStarted) {
|
|
21334
|
-
state.textIdx = state.curIdx++;
|
|
21335
|
-
send("content_block_start", {
|
|
21336
|
-
type: "content_block_start",
|
|
21337
|
-
index: state.textIdx,
|
|
21338
|
-
content_block: { type: "text", text: "" }
|
|
21339
|
-
});
|
|
21340
|
-
state.textStarted = true;
|
|
21341
|
-
log(`[Streaming] Started text block at index ${state.textIdx}`);
|
|
21342
|
-
}
|
|
21343
|
-
send("content_block_delta", {
|
|
21344
|
-
type: "content_block_delta",
|
|
21345
|
-
index: state.textIdx,
|
|
21346
|
-
delta: { type: "text_delta", text: res.cleanedText }
|
|
21347
|
-
});
|
|
21348
|
-
}
|
|
22031
|
+
emitSplitContent(thinkSplitter.push(res.cleanedText));
|
|
21349
22032
|
}
|
|
21350
22033
|
}
|
|
21351
22034
|
if (delta.tool_calls) {
|
|
@@ -21354,57 +22037,62 @@ data: ${JSON.stringify(d)}
|
|
|
21354
22037
|
const idx = tc.index;
|
|
21355
22038
|
let t = state.tools.get(idx);
|
|
21356
22039
|
if (tc.function?.name) {
|
|
22040
|
+
const accumulatedName = (state.pendingToolName.get(idx) ?? "") + tc.function.name;
|
|
22041
|
+
state.pendingToolName.set(idx, accumulatedName);
|
|
22042
|
+
const restoredName = toolNameMap?.get(accumulatedName) || accumulatedName;
|
|
21357
22043
|
if (!t) {
|
|
21358
|
-
if (state.reasoningStarted) {
|
|
21359
|
-
send("content_block_stop", {
|
|
21360
|
-
type: "content_block_stop",
|
|
21361
|
-
index: state.reasoningIdx
|
|
21362
|
-
});
|
|
21363
|
-
state.reasoningStarted = false;
|
|
21364
|
-
}
|
|
21365
|
-
if (state.textStarted) {
|
|
21366
|
-
send("content_block_stop", {
|
|
21367
|
-
type: "content_block_stop",
|
|
21368
|
-
index: state.textIdx
|
|
21369
|
-
});
|
|
21370
|
-
state.textStarted = false;
|
|
21371
|
-
}
|
|
21372
|
-
const rawName = tc.function.name;
|
|
21373
|
-
const restoredName = toolNameMap?.get(rawName) || rawName;
|
|
21374
22044
|
t = {
|
|
21375
22045
|
id: tc.id || `tool_${Date.now()}_${idx}`,
|
|
21376
22046
|
name: restoredName,
|
|
21377
|
-
blockIndex:
|
|
22047
|
+
blockIndex: writer.reserve(),
|
|
21378
22048
|
started: false,
|
|
21379
22049
|
closed: false,
|
|
21380
|
-
arguments: "",
|
|
22050
|
+
arguments: state.pendingToolArgs.get(idx) ?? "",
|
|
22051
|
+
ref: null,
|
|
21381
22052
|
buffered: !!toolSchemas && toolSchemas.length > 0 || behavior?.shouldBufferTool?.(restoredName) === true
|
|
21382
22053
|
};
|
|
22054
|
+
if (t.arguments) {
|
|
22055
|
+
log(`[Streaming] tool ${t.name} (index ${idx}): seeded ${t.arguments.length} argument chars that arrived before function.name`);
|
|
22056
|
+
}
|
|
22057
|
+
state.pendingToolArgs.delete(idx);
|
|
21383
22058
|
state.tools.set(idx, t);
|
|
21384
22059
|
if (isWebSearchToolCall(restoredName)) {
|
|
21385
22060
|
warnWebSearchUnsupported(restoredName, target);
|
|
21386
22061
|
}
|
|
22062
|
+
} else if (t.name !== restoredName) {
|
|
22063
|
+
if (t.started) {
|
|
22064
|
+
log(`[Streaming] error: tool block ${t.blockIndex} was started as "${t.name}" but the full name is "${restoredName}" \u2014 the client sees the wrong name`);
|
|
22065
|
+
} else {
|
|
22066
|
+
t.name = restoredName;
|
|
22067
|
+
t.buffered = !!toolSchemas && toolSchemas.length > 0 || behavior?.shouldBufferTool?.(restoredName) === true;
|
|
22068
|
+
if (isWebSearchToolCall(restoredName)) {
|
|
22069
|
+
warnWebSearchUnsupported(restoredName, target);
|
|
22070
|
+
}
|
|
22071
|
+
}
|
|
21387
22072
|
}
|
|
21388
22073
|
if (!t.started && !t.buffered) {
|
|
21389
|
-
|
|
21390
|
-
|
|
21391
|
-
|
|
21392
|
-
|
|
22074
|
+
t.ref = writer.openTool({
|
|
22075
|
+
id: t.id,
|
|
22076
|
+
name: t.name,
|
|
22077
|
+
index: t.blockIndex
|
|
21393
22078
|
});
|
|
21394
22079
|
t.started = true;
|
|
22080
|
+
if (t.arguments)
|
|
22081
|
+
writer.append(t.ref, t.arguments);
|
|
21395
22082
|
}
|
|
21396
22083
|
}
|
|
22084
|
+
if (tc.function?.arguments && !t) {
|
|
22085
|
+
state.pendingToolArgs.set(idx, (state.pendingToolArgs.get(idx) ?? "") + tc.function.arguments);
|
|
22086
|
+
}
|
|
21397
22087
|
if (tc.function?.arguments && t) {
|
|
21398
22088
|
t.arguments += tc.function.arguments;
|
|
21399
22089
|
if (!t.buffered) {
|
|
21400
|
-
|
|
21401
|
-
|
|
21402
|
-
|
|
21403
|
-
|
|
21404
|
-
|
|
21405
|
-
|
|
21406
|
-
}
|
|
21407
|
-
});
|
|
22090
|
+
if (!t.ref || !writer.append(t.ref, tc.function.arguments)) {
|
|
22091
|
+
log(`[Streaming] tool ${t.name} (index ${idx}) lost its open block mid-arguments \u2014 buffering the rest`);
|
|
22092
|
+
t.buffered = true;
|
|
22093
|
+
t.started = false;
|
|
22094
|
+
t.ref = null;
|
|
22095
|
+
}
|
|
21408
22096
|
}
|
|
21409
22097
|
}
|
|
21410
22098
|
}
|
|
@@ -21420,110 +22108,74 @@ data: ${JSON.stringify(d)}
|
|
|
21420
22108
|
const repairedJson = repairArgs(t.name, JSON.stringify(validation.repairedArgs));
|
|
21421
22109
|
log(`[Streaming] Sending repaired tool call: ${t.name} with args: ${repairedJson}`);
|
|
21422
22110
|
if (t.buffered && !t.started) {
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
21427
|
-
});
|
|
21428
|
-
send("content_block_delta", {
|
|
21429
|
-
type: "content_block_delta",
|
|
21430
|
-
index: t.blockIndex,
|
|
21431
|
-
delta: { type: "input_json_delta", partial_json: repairedJson }
|
|
21432
|
-
});
|
|
21433
|
-
send("content_block_stop", {
|
|
21434
|
-
type: "content_block_stop",
|
|
22111
|
+
t.ref = writer.openTool({
|
|
22112
|
+
id: t.id,
|
|
22113
|
+
name: t.name,
|
|
21435
22114
|
index: t.blockIndex
|
|
21436
22115
|
});
|
|
22116
|
+
writer.append(t.ref, repairedJson);
|
|
22117
|
+
writer.close(t.ref);
|
|
21437
22118
|
t.started = true;
|
|
21438
22119
|
t.closed = true;
|
|
21439
22120
|
continue;
|
|
21440
22121
|
}
|
|
21441
22122
|
if (t.started) {
|
|
21442
|
-
|
|
21443
|
-
|
|
21444
|
-
|
|
21445
|
-
});
|
|
21446
|
-
const repairedIdx = state.curIdx++;
|
|
22123
|
+
if (t.ref)
|
|
22124
|
+
writer.close(t.ref);
|
|
22125
|
+
const repairedIdx = writer.reserve();
|
|
21447
22126
|
const repairedId = `tool_repaired_${Date.now()}_${repairedIdx}`;
|
|
21448
|
-
|
|
21449
|
-
|
|
21450
|
-
|
|
21451
|
-
content_block: { type: "tool_use", id: repairedId, name: t.name }
|
|
21452
|
-
});
|
|
21453
|
-
send("content_block_delta", {
|
|
21454
|
-
type: "content_block_delta",
|
|
21455
|
-
index: repairedIdx,
|
|
21456
|
-
delta: { type: "input_json_delta", partial_json: repairedJson }
|
|
21457
|
-
});
|
|
21458
|
-
send("content_block_stop", {
|
|
21459
|
-
type: "content_block_stop",
|
|
22127
|
+
const repairedRef = writer.openTool({
|
|
22128
|
+
id: repairedId,
|
|
22129
|
+
name: t.name,
|
|
21460
22130
|
index: repairedIdx
|
|
21461
22131
|
});
|
|
22132
|
+
writer.append(repairedRef, repairedJson);
|
|
22133
|
+
writer.close(repairedRef);
|
|
22134
|
+
t.ref = repairedRef;
|
|
21462
22135
|
t.closed = true;
|
|
21463
22136
|
continue;
|
|
21464
22137
|
}
|
|
21465
22138
|
}
|
|
21466
22139
|
if (!validation.valid) {
|
|
21467
22140
|
log(`[Streaming] Tool call ${t.name} validation failed: ${validation.missingParams.join(", ")}`);
|
|
21468
|
-
const errorIdx = t.buffered ? t.blockIndex :
|
|
22141
|
+
const errorIdx = t.buffered ? t.blockIndex : undefined;
|
|
21469
22142
|
const errorMsg = `
|
|
21470
22143
|
|
|
21471
22144
|
\u26A0\uFE0F Tool call "${t.name}" failed: missing required parameters: ${validation.missingParams.join(", ")}. Local models sometimes generate incomplete tool calls. Please try again or use a model with better tool support.`;
|
|
21472
|
-
|
|
21473
|
-
|
|
21474
|
-
|
|
21475
|
-
|
|
21476
|
-
|
|
21477
|
-
send("content_block_delta", {
|
|
21478
|
-
type: "content_block_delta",
|
|
21479
|
-
index: errorIdx,
|
|
21480
|
-
delta: { type: "text_delta", text: errorMsg }
|
|
21481
|
-
});
|
|
21482
|
-
send("content_block_stop", {
|
|
21483
|
-
type: "content_block_stop",
|
|
21484
|
-
index: errorIdx
|
|
21485
|
-
});
|
|
21486
|
-
if (t.started && !t.buffered) {
|
|
21487
|
-
send("content_block_stop", {
|
|
21488
|
-
type: "content_block_stop",
|
|
21489
|
-
index: t.blockIndex
|
|
21490
|
-
});
|
|
22145
|
+
const errorRef = writer.openText({ index: errorIdx });
|
|
22146
|
+
writer.append(errorRef, errorMsg);
|
|
22147
|
+
writer.close(errorRef);
|
|
22148
|
+
if (t.started && !t.buffered && t.ref) {
|
|
22149
|
+
writer.close(t.ref);
|
|
21491
22150
|
}
|
|
21492
22151
|
t.closed = true;
|
|
21493
22152
|
continue;
|
|
21494
22153
|
}
|
|
21495
22154
|
if (t.buffered && !t.started) {
|
|
21496
22155
|
const argsJson = repairArgs(t.name, JSON.stringify(validation.parsedArgs));
|
|
21497
|
-
|
|
21498
|
-
|
|
21499
|
-
|
|
21500
|
-
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
21501
|
-
});
|
|
21502
|
-
send("content_block_delta", {
|
|
21503
|
-
type: "content_block_delta",
|
|
21504
|
-
index: t.blockIndex,
|
|
21505
|
-
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
21506
|
-
});
|
|
21507
|
-
send("content_block_stop", {
|
|
21508
|
-
type: "content_block_stop",
|
|
22156
|
+
t.ref = writer.openTool({
|
|
22157
|
+
id: t.id,
|
|
22158
|
+
name: t.name,
|
|
21509
22159
|
index: t.blockIndex
|
|
21510
22160
|
});
|
|
22161
|
+
writer.append(t.ref, argsJson);
|
|
22162
|
+
writer.close(t.ref);
|
|
21511
22163
|
t.started = true;
|
|
21512
22164
|
t.closed = true;
|
|
21513
22165
|
continue;
|
|
21514
22166
|
}
|
|
21515
22167
|
}
|
|
21516
22168
|
if (t.started && !t.closed) {
|
|
21517
|
-
|
|
21518
|
-
|
|
21519
|
-
index: t.blockIndex
|
|
21520
|
-
});
|
|
22169
|
+
if (t.ref)
|
|
22170
|
+
writer.close(t.ref);
|
|
21521
22171
|
t.closed = true;
|
|
21522
22172
|
}
|
|
21523
22173
|
}
|
|
21524
22174
|
}
|
|
21525
22175
|
}
|
|
21526
|
-
} catch (e) {
|
|
22176
|
+
} catch (e) {
|
|
22177
|
+
log(`[Streaming] Chunk processing error (chunk dropped): ${e} \u2014 payload starts: ${dataStr.slice(0, 120)}`);
|
|
22178
|
+
}
|
|
21527
22179
|
}
|
|
21528
22180
|
}
|
|
21529
22181
|
await finalize("unexpected");
|
|
@@ -21549,10 +22201,13 @@ var init_openai_sse = __esm(() => {
|
|
|
21549
22201
|
init_logger();
|
|
21550
22202
|
init_tool_call_recovery();
|
|
21551
22203
|
init_web_search_detector();
|
|
22204
|
+
init_block_writer();
|
|
22205
|
+
init_think_tag_splitter();
|
|
21552
22206
|
});
|
|
21553
22207
|
|
|
21554
22208
|
// src/handlers/shared/openai-compat.ts
|
|
21555
22209
|
var init_openai_compat = __esm(() => {
|
|
22210
|
+
init_openai_messages();
|
|
21556
22211
|
init_openai_tools();
|
|
21557
22212
|
init_openai_sse();
|
|
21558
22213
|
});
|
|
@@ -21560,6 +22215,7 @@ var init_openai_compat = __esm(() => {
|
|
|
21560
22215
|
// src/adapters/gemini-api-format.ts
|
|
21561
22216
|
var GeminiAPIFormat;
|
|
21562
22217
|
var init_gemini_api_format = __esm(() => {
|
|
22218
|
+
init_openai_tools();
|
|
21563
22219
|
init_gemini_schema();
|
|
21564
22220
|
init_openai_compat();
|
|
21565
22221
|
init_logger();
|
|
@@ -21704,6 +22360,11 @@ CRITICAL INSTRUCTION FOR OUTPUT FORMAT:
|
|
|
21704
22360
|
}
|
|
21705
22361
|
if (tools && tools.length > 0) {
|
|
21706
22362
|
payload.tools = tools;
|
|
22363
|
+
const toolConfig = mapToolChoiceToGemini(claudeRequest.tool_choice);
|
|
22364
|
+
if (toolConfig) {
|
|
22365
|
+
payload.toolConfig = toolConfig;
|
|
22366
|
+
log(`[GeminiAPIFormat] toolConfig.mode -> ${toolConfig.functionCallingConfig.mode} for ${this.modelId}`);
|
|
22367
|
+
}
|
|
21707
22368
|
}
|
|
21708
22369
|
const effort = this.resolveEffortLevel(claudeRequest);
|
|
21709
22370
|
if (effort) {
|
|
@@ -21825,6 +22486,7 @@ CRITICAL INSTRUCTION FOR OUTPUT FORMAT:
|
|
|
21825
22486
|
// src/adapters/litellm-api-format.ts
|
|
21826
22487
|
var INLINE_IMAGE_MODEL_PATTERNS, LiteLLMAPIFormat;
|
|
21827
22488
|
var init_litellm_api_format = __esm(() => {
|
|
22489
|
+
init_openai_tools();
|
|
21828
22490
|
init_logger();
|
|
21829
22491
|
init_base_api_format();
|
|
21830
22492
|
INLINE_IMAGE_MODEL_PATTERNS = ["minimax"];
|
|
@@ -21900,14 +22562,11 @@ var init_litellm_api_format = __esm(() => {
|
|
|
21900
22562
|
if (tools.length > 0) {
|
|
21901
22563
|
payload.tools = tools;
|
|
21902
22564
|
}
|
|
21903
|
-
|
|
21904
|
-
|
|
21905
|
-
|
|
21906
|
-
payload.tool_choice = { type: "function", function: { name } };
|
|
21907
|
-
} else if (type === "auto" || type === "none") {
|
|
21908
|
-
payload.tool_choice = type;
|
|
21909
|
-
}
|
|
22565
|
+
const toolChoice = mapToolChoiceToOpenAI(claudeRequest.tool_choice);
|
|
22566
|
+
if (toolChoice !== undefined) {
|
|
22567
|
+
payload.tool_choice = toolChoice;
|
|
21910
22568
|
}
|
|
22569
|
+
this.applyOpenAISamplingParams(payload, claudeRequest);
|
|
21911
22570
|
return payload;
|
|
21912
22571
|
}
|
|
21913
22572
|
checkVisionSupport() {
|
|
@@ -22010,6 +22669,7 @@ var init_ollama_api_format = __esm(() => {
|
|
|
22010
22669
|
// src/adapters/openai-api-format.ts
|
|
22011
22670
|
var OpenAIAPIFormat;
|
|
22012
22671
|
var init_openai_api_format = __esm(() => {
|
|
22672
|
+
init_openai_tools();
|
|
22013
22673
|
init_logger();
|
|
22014
22674
|
init_base_api_format();
|
|
22015
22675
|
init_model_catalog();
|
|
@@ -22027,13 +22687,6 @@ var init_openai_api_format = __esm(() => {
|
|
|
22027
22687
|
getMaxToolCount() {
|
|
22028
22688
|
return 128;
|
|
22029
22689
|
}
|
|
22030
|
-
prepareRequestCommon(request, _originalRequest) {
|
|
22031
|
-
this.truncateToolNames(request);
|
|
22032
|
-
if (request.messages) {
|
|
22033
|
-
this.truncateToolNamesInMessages(request.messages);
|
|
22034
|
-
}
|
|
22035
|
-
return request;
|
|
22036
|
-
}
|
|
22037
22690
|
applyNativeReasoning(request, originalRequest) {
|
|
22038
22691
|
if (this.supportsReasoningEffort() && request.reasoning_effort === undefined) {
|
|
22039
22692
|
const effort = this.resolveReasoningEffort(originalRequest);
|
|
@@ -22187,13 +22840,9 @@ var init_openai_api_format = __esm(() => {
|
|
|
22187
22840
|
if (tools.length > 0) {
|
|
22188
22841
|
payload.tools = tools;
|
|
22189
22842
|
}
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
payload.tool_choice = { type: "function", function: { name } };
|
|
22194
|
-
} else if (type === "auto" || type === "none") {
|
|
22195
|
-
payload.tool_choice = type;
|
|
22196
|
-
}
|
|
22843
|
+
const toolChoice = mapToolChoiceToOpenAI(claudeRequest.tool_choice);
|
|
22844
|
+
if (toolChoice !== undefined) {
|
|
22845
|
+
payload.tool_choice = toolChoice;
|
|
22197
22846
|
}
|
|
22198
22847
|
if (this.supportsReasoningEffort()) {
|
|
22199
22848
|
const effort = this.resolveReasoningEffort(claudeRequest);
|
|
@@ -22202,6 +22851,7 @@ var init_openai_api_format = __esm(() => {
|
|
|
22202
22851
|
log(`[OpenAIAPIFormat] reasoning_effort -> ${effort} for ${this.modelId}`);
|
|
22203
22852
|
}
|
|
22204
22853
|
}
|
|
22854
|
+
this.applyOpenAISamplingParams(payload, claudeRequest);
|
|
22205
22855
|
return payload;
|
|
22206
22856
|
}
|
|
22207
22857
|
};
|
|
@@ -22688,30 +23338,30 @@ var init_grok_model_dialect = __esm(() => {
|
|
|
22688
23338
|
return value;
|
|
22689
23339
|
}
|
|
22690
23340
|
recoverFromRejection(payload, errorText) {
|
|
22691
|
-
if (
|
|
22692
|
-
|
|
22693
|
-
|
|
22694
|
-
|
|
22695
|
-
|
|
22696
|
-
|
|
22697
|
-
|
|
22698
|
-
|
|
22699
|
-
|
|
22700
|
-
|
|
22701
|
-
|
|
22702
|
-
|
|
22703
|
-
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
|
|
22707
|
-
|
|
22708
|
-
|
|
22709
|
-
}
|
|
23341
|
+
if (payload?.reasoning_effort !== undefined) {
|
|
23342
|
+
if (isReasoningEffortRejection(errorText)) {
|
|
23343
|
+
rememberReasoningEffortRejected(this.modelId);
|
|
23344
|
+
const next = { ...payload };
|
|
23345
|
+
delete next.reasoning_effort;
|
|
23346
|
+
return { payload: next, note: `dropped reasoning_effort for ${this.modelId}` };
|
|
23347
|
+
}
|
|
23348
|
+
const rejected = rejectedReasoningEffortValue(errorText);
|
|
23349
|
+
if (rejected) {
|
|
23350
|
+
rememberReasoningEffortValueRejected(this.modelId, rejected);
|
|
23351
|
+
const fallback = fallbackReasoningEffortValue(rejected);
|
|
23352
|
+
const next = { ...payload };
|
|
23353
|
+
if (fallback) {
|
|
23354
|
+
next.reasoning_effort = fallback;
|
|
23355
|
+
return {
|
|
23356
|
+
payload: next,
|
|
23357
|
+
note: `reasoning_effort "${rejected}" -> "${fallback}" for ${this.modelId}`
|
|
23358
|
+
};
|
|
23359
|
+
}
|
|
23360
|
+
delete next.reasoning_effort;
|
|
23361
|
+
return { payload: next, note: `dropped unsupported reasoning_effort for ${this.modelId}` };
|
|
22710
23362
|
}
|
|
22711
|
-
delete next.reasoning_effort;
|
|
22712
|
-
return { payload: next, note: `dropped unsupported reasoning_effort for ${this.modelId}` };
|
|
22713
23363
|
}
|
|
22714
|
-
return
|
|
23364
|
+
return super.recoverFromRejection(payload, errorText);
|
|
22715
23365
|
}
|
|
22716
23366
|
parseXmlParameters(xmlContent) {
|
|
22717
23367
|
const params = {};
|
|
@@ -22738,6 +23388,7 @@ var init_grok_model_dialect = __esm(() => {
|
|
|
22738
23388
|
return lookupModel(this.modelId)?.contextWindow ?? 0;
|
|
22739
23389
|
}
|
|
22740
23390
|
reset() {
|
|
23391
|
+
super.reset();
|
|
22741
23392
|
this.xmlBuffer = "";
|
|
22742
23393
|
}
|
|
22743
23394
|
};
|
|
@@ -22847,11 +23498,18 @@ var init_qwen_model_dialect = __esm(() => {
|
|
|
22847
23498
|
log(`[QwenModelDialect] effort ${effort} -> enable_thinking: false for ${this.modelId}`);
|
|
22848
23499
|
} else {
|
|
22849
23500
|
request.enable_thinking = true;
|
|
22850
|
-
const
|
|
22851
|
-
|
|
22852
|
-
|
|
23501
|
+
const requested = this.effortToThinkingTokenBudget(effort);
|
|
23502
|
+
const ceiling = outputCeilingOf(originalRequest, request);
|
|
23503
|
+
const budget = clampThinkingBudget(requested, ceiling);
|
|
23504
|
+
if (budget === "no-room") {
|
|
23505
|
+
delete request.thinking_budget;
|
|
23506
|
+
log(`[QwenModelDialect] effort ${effort} -> enable_thinking: true, no thinking_budget (${requested} does not fit under ceiling ${ceiling}) for ${this.modelId}`);
|
|
23507
|
+
} else {
|
|
23508
|
+
if (budget !== undefined) {
|
|
23509
|
+
request.thinking_budget = budget;
|
|
23510
|
+
}
|
|
23511
|
+
log(`[QwenModelDialect] effort ${effort} -> enable_thinking: true, thinking_budget: ${budget ?? "(model max)"}${budget !== undefined && budget !== requested ? ` (clamped from ${requested} under ceiling ${ceiling})` : ""} for ${this.modelId}`);
|
|
22853
23512
|
}
|
|
22854
|
-
log(`[QwenModelDialect] effort ${effort} -> enable_thinking: true, thinking_budget: ${budget ?? "(model max)"} for ${this.modelId}`);
|
|
22855
23513
|
}
|
|
22856
23514
|
if (originalRequest.thinking)
|
|
22857
23515
|
delete request.thinking;
|
|
@@ -22881,16 +23539,6 @@ var init_xiaomi_model_dialect = __esm(() => {
|
|
|
22881
23539
|
wasTransformed: false
|
|
22882
23540
|
};
|
|
22883
23541
|
}
|
|
22884
|
-
getToolNameLimit() {
|
|
22885
|
-
return 64;
|
|
22886
|
-
}
|
|
22887
|
-
prepareRequestCommon(request, _originalRequest) {
|
|
22888
|
-
this.truncateToolNames(request);
|
|
22889
|
-
if (request.messages) {
|
|
22890
|
-
this.truncateToolNamesInMessages(request.messages);
|
|
22891
|
-
}
|
|
22892
|
-
return request;
|
|
22893
|
-
}
|
|
22894
23542
|
applyNativeReasoning(request, originalRequest) {
|
|
22895
23543
|
if (originalRequest.thinking) {
|
|
22896
23544
|
log("[XiaomiModelDialect] Stripping thinking object (not supported by Xiaomi API)");
|
|
@@ -31672,6 +32320,31 @@ var init_stats = __esm(() => {
|
|
|
31672
32320
|
});
|
|
31673
32321
|
});
|
|
31674
32322
|
|
|
32323
|
+
// src/handlers/shared/request-shape.ts
|
|
32324
|
+
function isRequestShapeError(errorBody) {
|
|
32325
|
+
if (!errorBody)
|
|
32326
|
+
return false;
|
|
32327
|
+
const lower = errorBody.toLowerCase();
|
|
32328
|
+
return lower.includes("unknown_parameter") || lower.includes("unsupported_parameter") || lower.includes("invalid_parameter") || lower.includes("unknown parameter:") || lower.includes("unsupported parameter:") || lower.includes("invalid parameter:");
|
|
32329
|
+
}
|
|
32330
|
+
function isContextOverflowError(status, errorBody) {
|
|
32331
|
+
if (!errorBody)
|
|
32332
|
+
return false;
|
|
32333
|
+
if (isRequestShapeError(errorBody))
|
|
32334
|
+
return false;
|
|
32335
|
+
const lower = errorBody.toLowerCase();
|
|
32336
|
+
if (lower.includes("context_length_exceeded") || lower.includes("string_above_max_length") || lower.includes("request_too_large") || lower.includes("context_window_exceeded") || lower.includes("prompt_too_long")) {
|
|
32337
|
+
return true;
|
|
32338
|
+
}
|
|
32339
|
+
if (lower.includes("maximum context length is") || lower.includes("reduce the length of the messages") || lower.includes("input is too long") || lower.includes(CONTEXT_OVERFLOW_PHRASE)) {
|
|
32340
|
+
return true;
|
|
32341
|
+
}
|
|
32342
|
+
if (status === 413 && (lower.includes("token") || lower.includes("context")))
|
|
32343
|
+
return true;
|
|
32344
|
+
return false;
|
|
32345
|
+
}
|
|
32346
|
+
var CONTEXT_OVERFLOW_PHRASE = "prompt is too long";
|
|
32347
|
+
|
|
31675
32348
|
// src/handlers/shared/anthropic-error.ts
|
|
31676
32349
|
function statusToErrorType(status) {
|
|
31677
32350
|
switch (status) {
|
|
@@ -31750,6 +32423,8 @@ function isTerminalError(status, bodyText, terminal429) {
|
|
|
31750
32423
|
return true;
|
|
31751
32424
|
if (status === 429 && terminal429)
|
|
31752
32425
|
return true;
|
|
32426
|
+
if (isContextOverflowError(status, bodyText))
|
|
32427
|
+
return true;
|
|
31753
32428
|
const lower = (bodyText || "").toLowerCase();
|
|
31754
32429
|
if (lower.includes("insufficient balance") || lower.includes("insufficient_balance") || lower.includes("insufficient_quota") || lower.includes("insufficient quota") || lower.includes("billing_not_active") || lower.includes("billing not active") || lower.includes("out of credits") || lower.includes("no credits remaining") || lower.includes("exceeded your current quota") || lower.includes("quota_exceeded")) {
|
|
31755
32430
|
return true;
|
|
@@ -31763,11 +32438,13 @@ function isTerminalError(status, bodyText, terminal429) {
|
|
|
31763
32438
|
return false;
|
|
31764
32439
|
}
|
|
31765
32440
|
function buildSurfacedErrorMessage(opts) {
|
|
31766
|
-
const { providerDisplayName, status, hint, providerMessage } = opts;
|
|
32441
|
+
const { providerDisplayName, status, hint, providerMessage, leadPhrase } = opts;
|
|
31767
32442
|
const head = `${providerDisplayName} error (HTTP ${status})`;
|
|
31768
32443
|
const parts = [head];
|
|
31769
32444
|
if (hint)
|
|
31770
32445
|
parts[0] = `${head}: ${hint}`;
|
|
32446
|
+
if (leadPhrase && !parts[0].startsWith(leadPhrase))
|
|
32447
|
+
parts[0] = `${leadPhrase} \u2014 ${parts[0]}`;
|
|
31771
32448
|
const detail = (providerMessage || "").trim();
|
|
31772
32449
|
if (detail && !parts[0].includes(detail)) {
|
|
31773
32450
|
const trimmed = detail.length > 600 ? `${detail.slice(0, 600)}\u2026` : detail;
|
|
@@ -32177,14 +32854,6 @@ var init_model_unsupported = __esm(() => {
|
|
|
32177
32854
|
];
|
|
32178
32855
|
});
|
|
32179
32856
|
|
|
32180
|
-
// src/handlers/shared/request-shape.ts
|
|
32181
|
-
function isRequestShapeError(errorBody) {
|
|
32182
|
-
if (!errorBody)
|
|
32183
|
-
return false;
|
|
32184
|
-
const lower = errorBody.toLowerCase();
|
|
32185
|
-
return lower.includes("unknown_parameter") || lower.includes("unsupported_parameter") || lower.includes("invalid_parameter") || lower.includes("unknown parameter:") || lower.includes("unsupported parameter:") || lower.includes("invalid parameter:");
|
|
32186
|
-
}
|
|
32187
|
-
|
|
32188
32857
|
// src/handlers/shared/stream-head-sniffer.ts
|
|
32189
32858
|
function isRetryableStreamError(code, type, message) {
|
|
32190
32859
|
if (RETRYABLE_ERROR_CODES.has(code))
|
|
@@ -33938,6 +34607,17 @@ function stripProviderPrefix(name) {
|
|
|
33938
34607
|
const at = name.indexOf("@");
|
|
33939
34608
|
return at === -1 ? name : name.slice(at + 1);
|
|
33940
34609
|
}
|
|
34610
|
+
function computeCacheReadDiscount(pricing, billedInputTokens, detail) {
|
|
34611
|
+
const cacheReadTokens = detail?.cacheReadTokens ?? 0;
|
|
34612
|
+
if (cacheReadTokens <= 0 || billedInputTokens <= 0)
|
|
34613
|
+
return 0;
|
|
34614
|
+
const rate = pricing.cacheReadCostPer1M ?? pricing.inputCostPer1M;
|
|
34615
|
+
const perMillionSaved = pricing.inputCostPer1M - rate;
|
|
34616
|
+
if (!(perMillionSaved > 0))
|
|
34617
|
+
return 0;
|
|
34618
|
+
const discountedTokens = Math.min(cacheReadTokens, billedInputTokens);
|
|
34619
|
+
return discountedTokens / 1e6 * perMillionSaved;
|
|
34620
|
+
}
|
|
33941
34621
|
|
|
33942
34622
|
class TokenTracker {
|
|
33943
34623
|
port;
|
|
@@ -33952,6 +34632,7 @@ class TokenTracker {
|
|
|
33952
34632
|
toolCallsByName = new Map;
|
|
33953
34633
|
startedAt = Date.now();
|
|
33954
34634
|
sessionBilledInputTokens = 0;
|
|
34635
|
+
sessionCacheReadTokens = 0;
|
|
33955
34636
|
constructor(port, config) {
|
|
33956
34637
|
this.port = port;
|
|
33957
34638
|
this.config = config;
|
|
@@ -33987,27 +34668,32 @@ class TokenTracker {
|
|
|
33987
34668
|
rewrite() {
|
|
33988
34669
|
this.writeFile(this.getLastInputTokens(), this.sessionOutputTokens);
|
|
33989
34670
|
}
|
|
33990
|
-
update(inputTokens, outputTokens) {
|
|
34671
|
+
update(inputTokens, outputTokens, detail) {
|
|
33991
34672
|
this.sessionInputTokens = inputTokens;
|
|
33992
34673
|
this.lastInputTokens = inputTokens;
|
|
33993
34674
|
this.sessionOutputTokens += outputTokens;
|
|
33994
34675
|
this.sessionBilledInputTokens += inputTokens;
|
|
34676
|
+
this.sessionCacheReadTokens += detail?.cacheReadTokens ?? 0;
|
|
33995
34677
|
const pricing = this.getPricing();
|
|
33996
|
-
const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
34678
|
+
const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M - this.cacheReadDiscount(pricing, inputTokens, detail);
|
|
33997
34679
|
this.sessionTotalCost += cost;
|
|
33998
34680
|
this.writeFile(inputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
33999
34681
|
}
|
|
34000
|
-
accumulateBoth(inputTokens, outputTokens) {
|
|
34682
|
+
accumulateBoth(inputTokens, outputTokens, detail) {
|
|
34001
34683
|
this.sessionInputTokens += inputTokens;
|
|
34002
34684
|
this.lastInputTokens = this.sessionInputTokens;
|
|
34003
34685
|
this.sessionOutputTokens += outputTokens;
|
|
34686
|
+
this.sessionCacheReadTokens += detail?.cacheReadTokens ?? 0;
|
|
34004
34687
|
const pricing = this.getPricing();
|
|
34005
|
-
const cost = this.sessionInputTokens / 1e6 * pricing.inputCostPer1M + this.sessionOutputTokens / 1e6 * pricing.outputCostPer1M
|
|
34688
|
+
const cost = this.sessionInputTokens / 1e6 * pricing.inputCostPer1M + this.sessionOutputTokens / 1e6 * pricing.outputCostPer1M - this.cacheReadDiscount(pricing, this.sessionInputTokens, {
|
|
34689
|
+
cacheReadTokens: this.sessionCacheReadTokens,
|
|
34690
|
+
cacheCreationTokens: 0
|
|
34691
|
+
});
|
|
34006
34692
|
this.sessionTotalCost = cost;
|
|
34007
34693
|
this.sessionBilledInputTokens = this.sessionInputTokens;
|
|
34008
34694
|
this.writeFile(this.sessionInputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
34009
34695
|
}
|
|
34010
|
-
updateWithDelta(inputTokens, outputTokens) {
|
|
34696
|
+
updateWithDelta(inputTokens, outputTokens, detail) {
|
|
34011
34697
|
let incrementalInputTokens;
|
|
34012
34698
|
this.lastInputTokens = inputTokens;
|
|
34013
34699
|
if (inputTokens >= this.sessionInputTokens) {
|
|
@@ -34022,17 +34708,19 @@ class TokenTracker {
|
|
|
34022
34708
|
log(`[TokenTracker] Ambiguous token decrease (${inputTokens} vs ${this.sessionInputTokens}), charging full input`);
|
|
34023
34709
|
}
|
|
34024
34710
|
this.sessionOutputTokens += outputTokens;
|
|
34711
|
+
this.sessionCacheReadTokens += detail?.cacheReadTokens ?? 0;
|
|
34025
34712
|
const pricing = this.getPricing();
|
|
34026
34713
|
this.sessionBilledInputTokens += incrementalInputTokens;
|
|
34027
|
-
const cost = incrementalInputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
34714
|
+
const cost = incrementalInputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M - this.cacheReadDiscount(pricing, incrementalInputTokens, detail);
|
|
34028
34715
|
this.sessionTotalCost += cost;
|
|
34029
34716
|
this.writeFile(inputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
34030
34717
|
}
|
|
34031
|
-
updateWithActualCost(inputTokens, outputTokens, actualCost) {
|
|
34718
|
+
updateWithActualCost(inputTokens, outputTokens, actualCost, detail) {
|
|
34032
34719
|
this.sessionInputTokens = inputTokens;
|
|
34033
34720
|
this.lastInputTokens = inputTokens;
|
|
34034
34721
|
this.sessionOutputTokens += outputTokens;
|
|
34035
34722
|
this.sessionBilledInputTokens += inputTokens;
|
|
34723
|
+
this.sessionCacheReadTokens += detail?.cacheReadTokens ?? 0;
|
|
34036
34724
|
if (typeof actualCost === "number" && actualCost > 0) {
|
|
34037
34725
|
this.sessionTotalCost += actualCost;
|
|
34038
34726
|
log(`[TokenTracker] Actual cost from API: $${actualCost.toFixed(6)}`);
|
|
@@ -34040,11 +34728,11 @@ class TokenTracker {
|
|
|
34040
34728
|
const pricing = this.getPricing();
|
|
34041
34729
|
const inputCost = inputTokens / 1e6 * pricing.inputCostPer1M;
|
|
34042
34730
|
const outputCost = outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
34043
|
-
this.sessionTotalCost += inputCost + outputCost;
|
|
34731
|
+
this.sessionTotalCost += inputCost + outputCost - this.cacheReadDiscount(pricing, inputTokens, detail);
|
|
34044
34732
|
}
|
|
34045
34733
|
this.writeFile(inputTokens, this.sessionOutputTokens);
|
|
34046
34734
|
}
|
|
34047
|
-
updateLocal(inputTokens, outputTokens) {
|
|
34735
|
+
updateLocal(inputTokens, outputTokens, _detail) {
|
|
34048
34736
|
if (inputTokens > 0) {
|
|
34049
34737
|
this.sessionInputTokens = inputTokens;
|
|
34050
34738
|
this.lastInputTokens = inputTokens;
|
|
@@ -34074,6 +34762,9 @@ class TokenTracker {
|
|
|
34074
34762
|
getPricing() {
|
|
34075
34763
|
return getModelPricing(this.config.providerName, this.config.modelName);
|
|
34076
34764
|
}
|
|
34765
|
+
cacheReadDiscount(pricing, billedInputTokens, detail) {
|
|
34766
|
+
return computeCacheReadDiscount(pricing, billedInputTokens, detail);
|
|
34767
|
+
}
|
|
34077
34768
|
getDisplayName() {
|
|
34078
34769
|
if (this.config.providerDisplayName)
|
|
34079
34770
|
return this.config.providerDisplayName;
|
|
@@ -34242,6 +34933,9 @@ class ComposedHandler {
|
|
|
34242
34933
|
if (resolvedModelAdapter.getName() !== "DefaultAPIFormat") {
|
|
34243
34934
|
this.modelAdapter = resolvedModelAdapter;
|
|
34244
34935
|
}
|
|
34936
|
+
const responseWire = this.resolveStreamFormat();
|
|
34937
|
+
this.resolvedDialect.setResponseWireFormat(responseWire);
|
|
34938
|
+
this.explicitAdapter?.setResponseWireFormat(responseWire);
|
|
34245
34939
|
this.middlewareManager = new MiddlewareManager;
|
|
34246
34940
|
if (this.bareModelName.includes("gemini") || this.bareModelName.includes("google/")) {
|
|
34247
34941
|
this.middlewareManager.register(new GeminiThoughtSignatureMiddleware);
|
|
@@ -34508,9 +35202,15 @@ class ComposedHandler {
|
|
|
34508
35202
|
log(`[${this.provider.displayName}] Response status: ${response.status}`);
|
|
34509
35203
|
this.capturePlanUsage(response);
|
|
34510
35204
|
if (!response.ok) {
|
|
34511
|
-
if (response.status >= 400 && response.status < 500
|
|
34512
|
-
const
|
|
34513
|
-
const
|
|
35205
|
+
if (response.status >= 400 && response.status < 500) {
|
|
35206
|
+
const candidates = [this.modelAdapter, this.getAdapter()].filter((a, i, all) => !!a?.recoverFromRejection && all.indexOf(a) === i);
|
|
35207
|
+
const errorText = candidates.length > 0 ? await response.clone().text() : "";
|
|
35208
|
+
let recovery = null;
|
|
35209
|
+
for (const candidate of candidates) {
|
|
35210
|
+
recovery = candidate.recoverFromRejection(requestPayload, errorText);
|
|
35211
|
+
if (recovery)
|
|
35212
|
+
break;
|
|
35213
|
+
}
|
|
34514
35214
|
if (recovery) {
|
|
34515
35215
|
log(`[${this.provider.displayName}] Parameter rejected \u2014 retrying: ${recovery.note}`);
|
|
34516
35216
|
requestPayload = recovery.payload;
|
|
@@ -34690,7 +35390,8 @@ class ComposedHandler {
|
|
|
34690
35390
|
providerDisplayName: this.provider.displayName,
|
|
34691
35391
|
status: response.status,
|
|
34692
35392
|
hint,
|
|
34693
|
-
providerMessage: providerMsg
|
|
35393
|
+
providerMessage: providerMsg,
|
|
35394
|
+
leadPhrase: isContextOverflowError(response.status, errorText) ? CONTEXT_OVERFLOW_PHRASE : undefined
|
|
34694
35395
|
});
|
|
34695
35396
|
return c.json(wrapAnthropicError(400, surfaced, "invalid_request_error", response.status), 400);
|
|
34696
35397
|
}
|
|
@@ -34913,20 +35614,20 @@ class ComposedHandler {
|
|
|
34913
35614
|
}
|
|
34914
35615
|
handleStream(c, response, adapter, claudeRequest, toolNameMap, onComplete, onApiError, behaviorSession) {
|
|
34915
35616
|
let pendingOnComplete = onComplete;
|
|
34916
|
-
const onTokenUpdate = (input, output) => {
|
|
35617
|
+
const onTokenUpdate = (input, output, detail) => {
|
|
34917
35618
|
const strategy = this.options.tokenStrategy || "standard";
|
|
34918
35619
|
switch (strategy) {
|
|
34919
35620
|
case "accumulate-both":
|
|
34920
|
-
this.tokenTracker.accumulateBoth(input, output);
|
|
35621
|
+
this.tokenTracker.accumulateBoth(input, output, detail);
|
|
34921
35622
|
break;
|
|
34922
35623
|
case "delta-aware":
|
|
34923
|
-
this.tokenTracker.updateWithDelta(input, output);
|
|
35624
|
+
this.tokenTracker.updateWithDelta(input, output, detail);
|
|
34924
35625
|
break;
|
|
34925
35626
|
case "local":
|
|
34926
|
-
this.tokenTracker.updateLocal(input, output);
|
|
35627
|
+
this.tokenTracker.updateLocal(input, output, detail);
|
|
34927
35628
|
break;
|
|
34928
35629
|
default:
|
|
34929
|
-
this.tokenTracker.update(input, output);
|
|
35630
|
+
this.tokenTracker.update(input, output, detail);
|
|
34930
35631
|
break;
|
|
34931
35632
|
}
|
|
34932
35633
|
if (pendingOnComplete) {
|
|
@@ -34955,7 +35656,7 @@ class ComposedHandler {
|
|
|
34955
35656
|
return createResponsesStreamHandler(c, response, {
|
|
34956
35657
|
modelName: this.bareModelName,
|
|
34957
35658
|
onTokenUpdate,
|
|
34958
|
-
toolNameMap
|
|
35659
|
+
toolNameMap,
|
|
34959
35660
|
contextWindow: lookupModelForProvider(this.bareModelName, this.provider.name),
|
|
34960
35661
|
onApiError,
|
|
34961
35662
|
priorInputTokens,
|
|
@@ -35111,6 +35812,9 @@ function getRecoveryHint(status, errorText, providerName, transportTerminal429)
|
|
|
35111
35812
|
}
|
|
35112
35813
|
return "Request format may be incompatible with provider.";
|
|
35113
35814
|
}
|
|
35815
|
+
if (isContextOverflowError(status, errorText)) {
|
|
35816
|
+
return "Input too large. Reduce message history or use a larger-context model.";
|
|
35817
|
+
}
|
|
35114
35818
|
if (status >= 500) {
|
|
35115
35819
|
return "Server error \u2014 retry after a brief wait.";
|
|
35116
35820
|
}
|
|
@@ -37055,6 +37759,12 @@ var init_auto_route = __esm(() => {
|
|
|
37055
37759
|
});
|
|
37056
37760
|
|
|
37057
37761
|
// src/providers/catalog-client.ts
|
|
37762
|
+
function catalogUrl() {
|
|
37763
|
+
return process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? DEFAULT_CATALOG_URL;
|
|
37764
|
+
}
|
|
37765
|
+
function plansUrl() {
|
|
37766
|
+
return process.env.CLAUDISH_PLANS_URL ?? derivePlansUrl(catalogUrl());
|
|
37767
|
+
}
|
|
37058
37768
|
function derivePlansUrl(catalogUrl) {
|
|
37059
37769
|
try {
|
|
37060
37770
|
const url = new URL(catalogUrl);
|
|
@@ -37203,49 +37913,97 @@ function catalogWarmDisabledFor(value) {
|
|
|
37203
37913
|
function catalogWarmDisabled() {
|
|
37204
37914
|
return catalogWarmDisabledFor(process.env.CLAUDISH_DISABLE_CATALOG_WARM);
|
|
37205
37915
|
}
|
|
37206
|
-
|
|
37207
|
-
|
|
37208
|
-
|
|
37209
|
-
|
|
37210
|
-
|
|
37916
|
+
function buildCatalogPageUrl(baseUrl, offset, limit, revision) {
|
|
37917
|
+
const url = new URL(baseUrl);
|
|
37918
|
+
url.searchParams.set("offset", String(offset));
|
|
37919
|
+
url.searchParams.set("limit", String(limit));
|
|
37920
|
+
if (revision)
|
|
37921
|
+
url.searchParams.set("revision", revision);
|
|
37922
|
+
return url.toString();
|
|
37923
|
+
}
|
|
37924
|
+
async function fetchCatalogPage(url, timeoutMs) {
|
|
37211
37925
|
let response;
|
|
37212
37926
|
try {
|
|
37213
|
-
response = await fetch(
|
|
37927
|
+
response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
37214
37928
|
} catch (err) {
|
|
37215
37929
|
const name = err?.name;
|
|
37216
37930
|
const reason = name === "TimeoutError" || name === "AbortError" ? "timeout" : "network";
|
|
37217
|
-
return {
|
|
37931
|
+
return { ok: false, reason };
|
|
37218
37932
|
}
|
|
37219
37933
|
if (!response.ok)
|
|
37220
|
-
return {
|
|
37221
|
-
let
|
|
37934
|
+
return { ok: false, reason: "http_error" };
|
|
37935
|
+
let page;
|
|
37222
37936
|
try {
|
|
37223
|
-
|
|
37937
|
+
page = await response.json();
|
|
37224
37938
|
} catch {
|
|
37225
|
-
return {
|
|
37939
|
+
return { ok: false, reason: "network" };
|
|
37226
37940
|
}
|
|
37227
|
-
|
|
37228
|
-
|
|
37941
|
+
const revision = response.headers.get(CATALOG_REVISION_HEADER) ?? undefined;
|
|
37942
|
+
return { ok: true, page, revision };
|
|
37943
|
+
}
|
|
37944
|
+
async function refreshCatalog(timeoutMs, options = {}) {
|
|
37945
|
+
if (catalogWarmDisabled()) {
|
|
37946
|
+
return { kind: "fetch_failed", reason: "disabled" };
|
|
37947
|
+
}
|
|
37948
|
+
const entries = [];
|
|
37949
|
+
let revision;
|
|
37950
|
+
let pages = 0;
|
|
37951
|
+
let offset = 0;
|
|
37952
|
+
for (;; ) {
|
|
37953
|
+
const url = buildCatalogPageUrl(catalogUrl(), offset, CATALOG_PAGE_LIMIT, revision);
|
|
37954
|
+
const result = await fetchCatalogPage(url, timeoutMs);
|
|
37955
|
+
if (!result.ok) {
|
|
37956
|
+
return {
|
|
37957
|
+
kind: "fetch_failed",
|
|
37958
|
+
reason: pages === 0 ? result.reason : "incomplete"
|
|
37959
|
+
};
|
|
37960
|
+
}
|
|
37961
|
+
const { page } = result;
|
|
37962
|
+
if (!Array.isArray(page.models))
|
|
37963
|
+
return { kind: "fetch_failed", reason: "empty" };
|
|
37964
|
+
if (pages === 0) {
|
|
37965
|
+
revision = result.revision;
|
|
37966
|
+
if (page.models.length === 0)
|
|
37967
|
+
return { kind: "fetch_failed", reason: "empty" };
|
|
37968
|
+
} else if (revision && result.revision && result.revision !== revision) {
|
|
37969
|
+
return { kind: "fetch_failed", reason: "revision_mismatch" };
|
|
37970
|
+
}
|
|
37971
|
+
entries.push(...page.models);
|
|
37972
|
+
pages++;
|
|
37973
|
+
if (page.hasMore !== true)
|
|
37974
|
+
break;
|
|
37975
|
+
if (page.models.length === 0)
|
|
37976
|
+
return { kind: "fetch_failed", reason: "incomplete" };
|
|
37977
|
+
offset += page.models.length;
|
|
37978
|
+
if (pages >= MAX_CATALOG_PAGES) {
|
|
37979
|
+
return { kind: "fetch_failed", reason: "incomplete" };
|
|
37980
|
+
}
|
|
37229
37981
|
}
|
|
37982
|
+
if (entries.length === 0)
|
|
37983
|
+
return { kind: "fetch_failed", reason: "empty" };
|
|
37984
|
+
const plans = await fetchSubscriptionPlans(timeoutMs, revision);
|
|
37230
37985
|
const backwardCompatModels = [];
|
|
37231
|
-
for (const entry of
|
|
37986
|
+
for (const entry of entries) {
|
|
37232
37987
|
const id = externalIdFor(entry, "openrouter");
|
|
37233
37988
|
if (id)
|
|
37234
37989
|
backwardCompatModels.push({ id });
|
|
37235
37990
|
}
|
|
37236
|
-
_memCache =
|
|
37237
|
-
const plans = await plansPromise;
|
|
37991
|
+
_memCache = entries;
|
|
37238
37992
|
writeAllModelsCache({
|
|
37239
|
-
entries
|
|
37993
|
+
entries,
|
|
37240
37994
|
models: backwardCompatModels,
|
|
37241
|
-
...plans !== undefined ? { plans } : {}
|
|
37242
|
-
|
|
37995
|
+
...plans !== undefined ? { plans } : {},
|
|
37996
|
+
...revision !== undefined ? { catalogRevision: revision } : {}
|
|
37997
|
+
}, options.cachePath);
|
|
37243
37998
|
_warmPromise = Promise.resolve();
|
|
37244
|
-
return { kind: "refreshed", modelCount:
|
|
37999
|
+
return { kind: "refreshed", modelCount: entries.length, catalogRevision: revision, pages };
|
|
37245
38000
|
}
|
|
37246
|
-
async function fetchSubscriptionPlans(timeoutMs) {
|
|
38001
|
+
async function fetchSubscriptionPlans(timeoutMs, revision) {
|
|
37247
38002
|
try {
|
|
37248
|
-
const
|
|
38003
|
+
const url = new URL(plansUrl());
|
|
38004
|
+
if (revision)
|
|
38005
|
+
url.searchParams.set("revision", revision);
|
|
38006
|
+
const response = await fetch(url.toString(), {
|
|
37249
38007
|
signal: AbortSignal.timeout(timeoutMs)
|
|
37250
38008
|
});
|
|
37251
38009
|
if (!response.ok)
|
|
@@ -37277,11 +38035,9 @@ async function ensureCatalogReady(timeoutMs = 5000) {
|
|
|
37277
38035
|
new Promise((resolve) => setTimeout(resolve, timeoutMs))
|
|
37278
38036
|
]);
|
|
37279
38037
|
}
|
|
37280
|
-
var
|
|
38038
|
+
var DEFAULT_CATALOG_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000", _memCache = null, _catalogEntriesForTest, _warmPromise = null, CATALOG_REVISION_HEADER = "x-catalog-revision", MAX_CATALOG_PAGES = 40, CATALOG_PAGE_LIMIT = 1000;
|
|
37281
38039
|
var init_catalog_client = __esm(() => {
|
|
37282
38040
|
init_all_models_cache();
|
|
37283
|
-
FIREBASE_CATALOG_URL = process.env.CLAUDISH_CATALOG_URL ?? process.env.FIREBASE_CATALOG_URL ?? "https://us-central1-claudish-6da10.cloudfunctions.net/queryModels?status=active&catalog=slim&limit=1000";
|
|
37284
|
-
FIREBASE_PLANS_URL = process.env.CLAUDISH_PLANS_URL ?? derivePlansUrl(FIREBASE_CATALOG_URL);
|
|
37285
38041
|
});
|
|
37286
38042
|
|
|
37287
38043
|
// src/config-schema.ts
|
|
@@ -40480,6 +41236,18 @@ async function shutdownAllTeamRuns() {
|
|
|
40480
41236
|
return;
|
|
40481
41237
|
})));
|
|
40482
41238
|
}
|
|
41239
|
+
function snippetHeadAndTail(text) {
|
|
41240
|
+
if (text.length <= SNIPPET_LIMIT)
|
|
41241
|
+
return text;
|
|
41242
|
+
const head = text.slice(0, SNIPPET_HEAD);
|
|
41243
|
+
const tail = text.slice(-(SNIPPET_LIMIT - SNIPPET_HEAD));
|
|
41244
|
+
const omitted = text.length - head.length - tail.length;
|
|
41245
|
+
return `${head}
|
|
41246
|
+
|
|
41247
|
+
\u2026 [${omitted} bytes omitted] \u2026
|
|
41248
|
+
|
|
41249
|
+
${tail}`;
|
|
41250
|
+
}
|
|
40483
41251
|
function classifyRunOutput(opts) {
|
|
40484
41252
|
const {
|
|
40485
41253
|
outputSize,
|
|
@@ -40850,7 +41618,7 @@ async function startModels(sessionPath, opts = {}) {
|
|
|
40850
41618
|
reason,
|
|
40851
41619
|
detail,
|
|
40852
41620
|
stderrSnippet: stderr ? redactSecrets(stderr).slice(-2000) : undefined,
|
|
40853
|
-
stdoutSnippet: stdoutTail ? redactSecrets(stdoutTail)
|
|
41621
|
+
stdoutSnippet: stdoutTail ? snippetHeadAndTail(redactSecrets(stdoutTail)) : undefined,
|
|
40854
41622
|
errorLogPath,
|
|
40855
41623
|
upstreamErrorLogPath: existsSync20(upstreamErrorLogPath) ? upstreamErrorLogPath : undefined,
|
|
40856
41624
|
workDir: sessionPath
|
|
@@ -41127,7 +41895,7 @@ function formatVerdict(verdict, sessionPath) {
|
|
|
41127
41895
|
}
|
|
41128
41896
|
return output;
|
|
41129
41897
|
}
|
|
41130
|
-
var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", liveTeamRuns, STDOUT_TAIL_LIMIT = 4000, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, BENIGN_STDERR_PATTERNS;
|
|
41898
|
+
var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", liveTeamRuns, STDOUT_TAIL_LIMIT = 4000, SNIPPET_LIMIT = 2000, SNIPPET_HEAD = 600, API_ERROR_RE, BG_CEILING_RE, DEFAULT_MIN_OUTPUT_BYTES = 0, BENIGN_STDERR_PATTERNS;
|
|
41131
41899
|
var init_team_orchestrator = __esm(() => {
|
|
41132
41900
|
init_prehydrate();
|
|
41133
41901
|
init_stream_json_reducer();
|
|
@@ -42866,8 +43634,10 @@ function accountStreamEvent(rawEvent) {
|
|
|
42866
43634
|
if (typeof text === "string" && text.length > 0) {
|
|
42867
43635
|
contentDelta = true;
|
|
42868
43636
|
textChars = text.length;
|
|
42869
|
-
} else if (parsed?.type === "content_block_delta"
|
|
43637
|
+
} else if (parsed?.type === "content_block_delta") {
|
|
42870
43638
|
contentDelta = true;
|
|
43639
|
+
} else if (parsed?.type === "content_block_start") {
|
|
43640
|
+
contentDelta = parsed.content_block?.type === "tool_use";
|
|
42871
43641
|
}
|
|
42872
43642
|
const outputTokens = parsed?.usage?.output_tokens ?? parsed?.message?.usage?.output_tokens ?? parsed?.usage?.completion_tokens;
|
|
42873
43643
|
const stopReason = parsed?.delta?.stop_reason ?? (Array.isArray(parsed?.choices) ? parsed.choices[0]?.finish_reason : undefined);
|
|
@@ -44591,6 +45361,7 @@ var init_cors = () => {};
|
|
|
44591
45361
|
// src/adapters/local-adapter.ts
|
|
44592
45362
|
var LocalModelAdapter;
|
|
44593
45363
|
var init_local_adapter = __esm(() => {
|
|
45364
|
+
init_openai_tools();
|
|
44594
45365
|
init_logger();
|
|
44595
45366
|
init_base_api_format();
|
|
44596
45367
|
init_dialect_manager();
|
|
@@ -44657,26 +45428,26 @@ ${messages[0].content}`;
|
|
|
44657
45428
|
tools: tools.length > 0 ? tools : undefined,
|
|
44658
45429
|
stream_options: { include_usage: true }
|
|
44659
45430
|
};
|
|
44660
|
-
if (
|
|
44661
|
-
const
|
|
44662
|
-
if (
|
|
44663
|
-
payload.tool_choice =
|
|
44664
|
-
} else if (type === "auto" || type === "none") {
|
|
44665
|
-
payload.tool_choice = type;
|
|
45431
|
+
if (tools.length > 0) {
|
|
45432
|
+
const toolChoice = mapToolChoiceToOpenAI(claudeRequest.tool_choice);
|
|
45433
|
+
if (toolChoice !== undefined) {
|
|
45434
|
+
payload.tool_choice = toolChoice;
|
|
44666
45435
|
}
|
|
44667
45436
|
}
|
|
45437
|
+
this.applyOpenAISamplingParams(payload, claudeRequest);
|
|
44668
45438
|
return payload;
|
|
44669
45439
|
}
|
|
44670
45440
|
prepareRequestCommon(request, originalRequest) {
|
|
44671
45441
|
this.innerAdapter.prepareRequest(request, originalRequest);
|
|
44672
|
-
for (const [k, v] of this.innerAdapter.getToolNameMap()) {
|
|
44673
|
-
this.toolNameMap.set(k, v);
|
|
44674
|
-
}
|
|
44675
45442
|
delete request.enable_thinking;
|
|
44676
45443
|
delete request.thinking_budget;
|
|
44677
45444
|
delete request.thinking;
|
|
44678
45445
|
return request;
|
|
44679
45446
|
}
|
|
45447
|
+
setResponseWireFormat(format) {
|
|
45448
|
+
super.setResponseWireFormat(format);
|
|
45449
|
+
this.innerAdapter.setResponseWireFormat(format);
|
|
45450
|
+
}
|
|
44680
45451
|
getToolNameMap() {
|
|
44681
45452
|
const map = new Map(super.getToolNameMap());
|
|
44682
45453
|
for (const [k, v] of this.innerAdapter.getToolNameMap()) {
|
|
@@ -44835,19 +45606,20 @@ ${text}`;
|
|
|
44835
45606
|
if (claudeRequest.thinking) {
|
|
44836
45607
|
payload.thinking = claudeRequest.thinking;
|
|
44837
45608
|
}
|
|
44838
|
-
|
|
44839
|
-
|
|
44840
|
-
|
|
44841
|
-
payload.tool_choice = { type: "function", function: { name } };
|
|
44842
|
-
} else if (type === "auto" || type === "none") {
|
|
44843
|
-
payload.tool_choice = type;
|
|
44844
|
-
}
|
|
45609
|
+
const toolChoice = mapToolChoiceToOpenAI(claudeRequest.tool_choice);
|
|
45610
|
+
if (toolChoice !== undefined) {
|
|
45611
|
+
payload.tool_choice = toolChoice;
|
|
44845
45612
|
}
|
|
45613
|
+
this.applyOpenAISamplingParams(payload, claudeRequest);
|
|
44846
45614
|
return payload;
|
|
44847
45615
|
}
|
|
44848
45616
|
prepareRequestCommon(request, originalRequest) {
|
|
44849
45617
|
return this.innerAdapter.prepareRequest(request, originalRequest);
|
|
44850
45618
|
}
|
|
45619
|
+
setResponseWireFormat(format) {
|
|
45620
|
+
super.setResponseWireFormat(format);
|
|
45621
|
+
this.innerAdapter.setResponseWireFormat(format);
|
|
45622
|
+
}
|
|
44851
45623
|
getToolNameMap() {
|
|
44852
45624
|
const map = new Map(super.getToolNameMap());
|
|
44853
45625
|
for (const [k, v] of this.innerAdapter.getToolNameMap()) {
|
|
@@ -67211,6 +67983,46 @@ function resolveAdvisorModelArg(config, cwd = process.cwd()) {
|
|
|
67211
67983
|
source: "claudish"
|
|
67212
67984
|
};
|
|
67213
67985
|
}
|
|
67986
|
+
function isSuppressibleChildStderrLine(line) {
|
|
67987
|
+
return CHILD_STDERR_NOISE.some((re) => re.test(line));
|
|
67988
|
+
}
|
|
67989
|
+
function relayChildStderr(stream) {
|
|
67990
|
+
let buffered = "";
|
|
67991
|
+
const emit = (line) => {
|
|
67992
|
+
if (isSuppressibleChildStderrLine(line)) {
|
|
67993
|
+
log(`[Suppressed] claude-code-stderr: ${line.trimEnd()}`);
|
|
67994
|
+
return;
|
|
67995
|
+
}
|
|
67996
|
+
process.stderr.write(`${line}
|
|
67997
|
+
`);
|
|
67998
|
+
};
|
|
67999
|
+
stream.setEncoding("utf8");
|
|
68000
|
+
stream.on("data", (chunk) => {
|
|
68001
|
+
buffered += chunk;
|
|
68002
|
+
const lines = buffered.split(`
|
|
68003
|
+
`);
|
|
68004
|
+
buffered = lines.pop() ?? "";
|
|
68005
|
+
for (const line of lines)
|
|
68006
|
+
emit(line);
|
|
68007
|
+
});
|
|
68008
|
+
const flush = () => {
|
|
68009
|
+
if (buffered.length === 0)
|
|
68010
|
+
return;
|
|
68011
|
+
const tail = buffered;
|
|
68012
|
+
buffered = "";
|
|
68013
|
+
if (isSuppressibleChildStderrLine(tail)) {
|
|
68014
|
+
log(`[Suppressed] claude-code-stderr: ${tail}`);
|
|
68015
|
+
} else {
|
|
68016
|
+
process.stderr.write(tail);
|
|
68017
|
+
}
|
|
68018
|
+
};
|
|
68019
|
+
stream.on("end", flush);
|
|
68020
|
+
stream.on("close", flush);
|
|
68021
|
+
stream.on("error", (err) => {
|
|
68022
|
+
flush();
|
|
68023
|
+
logStderr(`child stderr relay ended: ${err}`);
|
|
68024
|
+
});
|
|
68025
|
+
}
|
|
67214
68026
|
async function runClaudeWithProxy(config, proxyUrl, onCleanup) {
|
|
67215
68027
|
const hasProfileMappings = config.modelOpus || config.modelSonnet || config.modelHaiku || config.modelSubagent;
|
|
67216
68028
|
const advisorNativeSession = isAdvisorNativeSession(config);
|
|
@@ -67285,6 +68097,9 @@ async function runClaudeWithProxy(config, proxyUrl, onCleanup) {
|
|
|
67285
68097
|
}
|
|
67286
68098
|
let hidAnthropicApiKey = false;
|
|
67287
68099
|
delete env.CLAUDECODE;
|
|
68100
|
+
if (!config.interactive && env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC === undefined) {
|
|
68101
|
+
env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
|
|
68102
|
+
}
|
|
67288
68103
|
if (config.monitor || advisorNativeSession) {
|
|
67289
68104
|
delete env.ANTHROPIC_API_KEY;
|
|
67290
68105
|
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
@@ -67382,12 +68197,16 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
67382
68197
|
} else if (config.interactive && !process.stdout.isTTY && !process.stdin.isTTY) {
|
|
67383
68198
|
console.error("[claudish] An interactive session was requested but no terminal is attached (stdin and stdout are both non-TTY). Pass a prompt argument, or use --stdin / -p for non-interactive mode.");
|
|
67384
68199
|
}
|
|
67385
|
-
const
|
|
68200
|
+
const filterChildStderr = !config.interactive && ttyFd === undefined;
|
|
68201
|
+
const stdio = ttyFd !== undefined ? [0, ttyFd, ttyFd] : filterChildStderr ? ["inherit", "inherit", "pipe"] : "inherit";
|
|
67386
68202
|
const proc = spawn5(spawnCommand, claudeArgs, {
|
|
67387
68203
|
env,
|
|
67388
68204
|
stdio,
|
|
67389
68205
|
shell: needsShell
|
|
67390
68206
|
});
|
|
68207
|
+
if (filterChildStderr && proc.stderr) {
|
|
68208
|
+
relayChildStderr(proc.stderr);
|
|
68209
|
+
}
|
|
67391
68210
|
if (config.interactive) {
|
|
67392
68211
|
restoreTerminal = beginTerminalIsolation((entry) => {
|
|
67393
68212
|
logStderr(`[Suppressed] ${entry.source}: ${entry.text.trimEnd()}`);
|
|
@@ -67525,7 +68344,7 @@ var restoreTerminal = null, macosKeychainAnthropicResult, defaultKeychainAnthrop
|
|
|
67525
68344
|
macosKeychainAnthropicResult = false;
|
|
67526
68345
|
}
|
|
67527
68346
|
return macosKeychainAnthropicResult;
|
|
67528
|
-
}, CLAUDISH_PLACEHOLDER_API_KEY = "sk-ant-api03-placeholder-not-used-proxy-handles-auth-with-openrouter-key-xxxxxxxxxxxxxxxxxxxxx", CLAUDISH_PLACEHOLDER_AUTH_TOKEN = "placeholder-token-not-used-proxy-handles-auth", STALE_TOKEN_FILE_MS, MAX_TOKEN_FILES_SCANNED = 4000, CLAUDE_CODE_DEFAULT_MAX_CONTEXT = 200000, USER_STATUS_LINE_TIMEOUT_SECONDS = 3, MIN_AUTO_COMPACT_WINDOW = 200000, ADVISOR_TOOL_ENV_VAR = "CLAUDE_CODE_ENABLE_EXPERIMENTAL_ADVISOR_TOOL", CLAUDISH_CHILD_ADVISOR_MODEL = "sonnet", SIGNAL_EXIT_NUMBERS;
|
|
68347
|
+
}, CLAUDISH_PLACEHOLDER_API_KEY = "sk-ant-api03-placeholder-not-used-proxy-handles-auth-with-openrouter-key-xxxxxxxxxxxxxxxxxxxxx", CLAUDISH_PLACEHOLDER_AUTH_TOKEN = "placeholder-token-not-used-proxy-handles-auth", STALE_TOKEN_FILE_MS, MAX_TOKEN_FILES_SCANNED = 4000, CLAUDE_CODE_DEFAULT_MAX_CONTEXT = 200000, USER_STATUS_LINE_TIMEOUT_SECONDS = 3, MIN_AUTO_COMPACT_WINDOW = 200000, ADVISOR_TOOL_ENV_VAR = "CLAUDE_CODE_ENABLE_EXPERIMENTAL_ADVISOR_TOOL", CLAUDISH_CHILD_ADVISOR_MODEL = "sonnet", CHILD_STDERR_NOISE, SIGNAL_EXIT_NUMBERS;
|
|
67529
68348
|
var init_claude_runner = __esm(() => {
|
|
67530
68349
|
init_model_catalog();
|
|
67531
68350
|
init_config2();
|
|
@@ -67539,6 +68358,7 @@ var init_claude_runner = __esm(() => {
|
|
|
67539
68358
|
init_terminal_isolation();
|
|
67540
68359
|
init_theme_mode();
|
|
67541
68360
|
STALE_TOKEN_FILE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
68361
|
+
CHILD_STDERR_NOISE = [/^\[claude-code:unrecognized_model\]/];
|
|
67542
68362
|
SIGNAL_EXIT_NUMBERS = {
|
|
67543
68363
|
SIGHUP: 1,
|
|
67544
68364
|
SIGINT: 2,
|