claudish 2.10.1 → 2.11.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 +536 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34308,7 +34308,8 @@ var init_config = __esm(() => {
|
|
|
34308
34308
|
OLLAMA_BASE_URL: "OLLAMA_BASE_URL",
|
|
34309
34309
|
OLLAMA_HOST: "OLLAMA_HOST",
|
|
34310
34310
|
LMSTUDIO_BASE_URL: "LMSTUDIO_BASE_URL",
|
|
34311
|
-
VLLM_BASE_URL: "VLLM_BASE_URL"
|
|
34311
|
+
VLLM_BASE_URL: "VLLM_BASE_URL",
|
|
34312
|
+
CLAUDISH_SUMMARIZE_TOOLS: "CLAUDISH_SUMMARIZE_TOOLS"
|
|
34312
34313
|
};
|
|
34313
34314
|
OPENROUTER_HEADERS = {
|
|
34314
34315
|
"HTTP-Referer": "https://github.com/MadAppGang/claude-code",
|
|
@@ -34743,6 +34744,10 @@ async function parseArgs(args) {
|
|
|
34743
34744
|
config3.port = port;
|
|
34744
34745
|
}
|
|
34745
34746
|
}
|
|
34747
|
+
const envSummarizeTools = process.env[ENV.CLAUDISH_SUMMARIZE_TOOLS];
|
|
34748
|
+
if (envSummarizeTools === "true" || envSummarizeTools === "1") {
|
|
34749
|
+
config3.summarizeTools = true;
|
|
34750
|
+
}
|
|
34746
34751
|
let i = 0;
|
|
34747
34752
|
while (i < args.length) {
|
|
34748
34753
|
const arg = args[i];
|
|
@@ -34859,6 +34864,8 @@ async function parseArgs(args) {
|
|
|
34859
34864
|
await printAllModels(hasJsonFlag, forceUpdate);
|
|
34860
34865
|
}
|
|
34861
34866
|
process.exit(0);
|
|
34867
|
+
} else if (arg === "--summarize-tools") {
|
|
34868
|
+
config3.summarizeTools = true;
|
|
34862
34869
|
} else {
|
|
34863
34870
|
config3.claudeArgs = args.slice(i);
|
|
34864
34871
|
break;
|
|
@@ -38867,26 +38874,222 @@ function transformOpenAIToClaude(claudeRequestInput) {
|
|
|
38867
38874
|
}
|
|
38868
38875
|
var init_transform = () => {};
|
|
38869
38876
|
|
|
38870
|
-
// src/handlers/shared/
|
|
38871
|
-
function
|
|
38872
|
-
const
|
|
38877
|
+
// src/handlers/shared/tool-call-recovery.ts
|
|
38878
|
+
function extractToolCallsFromText(text) {
|
|
38879
|
+
const extracted = [];
|
|
38880
|
+
const qwenPattern = /<function=([^>]+)>([\s\S]*?)(?=<function=|$)/gi;
|
|
38881
|
+
let match2;
|
|
38882
|
+
while ((match2 = qwenPattern.exec(text)) !== null) {
|
|
38883
|
+
const funcName = match2[1];
|
|
38884
|
+
const paramsText = match2[2];
|
|
38885
|
+
const args = {};
|
|
38886
|
+
const paramPattern = /<parameter=([^>]+)>\s*([\s\S]*?)(?=<parameter=|<function=|$)/gi;
|
|
38887
|
+
let paramMatch;
|
|
38888
|
+
while ((paramMatch = paramPattern.exec(paramsText)) !== null) {
|
|
38889
|
+
const paramName = paramMatch[1];
|
|
38890
|
+
const paramValue = paramMatch[2].trim();
|
|
38891
|
+
args[paramName] = paramValue;
|
|
38892
|
+
}
|
|
38893
|
+
if (funcName) {
|
|
38894
|
+
extracted.push({
|
|
38895
|
+
name: funcName,
|
|
38896
|
+
arguments: args,
|
|
38897
|
+
source: "xml_text"
|
|
38898
|
+
});
|
|
38899
|
+
log(`[ToolRecovery] Extracted Qwen-style tool call: ${funcName}`);
|
|
38900
|
+
}
|
|
38901
|
+
}
|
|
38902
|
+
const xmlPattern = /<tool_call>\s*(\{[\s\S]*?\})\s*<\/tool_call>/gi;
|
|
38903
|
+
while ((match2 = xmlPattern.exec(text)) !== null) {
|
|
38904
|
+
try {
|
|
38905
|
+
const parsed = JSON.parse(match2[1]);
|
|
38906
|
+
if (parsed.name) {
|
|
38907
|
+
extracted.push({
|
|
38908
|
+
name: parsed.name,
|
|
38909
|
+
arguments: parsed.arguments || parsed.input || parsed.parameters || {},
|
|
38910
|
+
source: "xml_text"
|
|
38911
|
+
});
|
|
38912
|
+
}
|
|
38913
|
+
} catch (e) {}
|
|
38914
|
+
}
|
|
38915
|
+
const funcCallPattern = /\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"(?:arguments|input|parameters)"\s*:\s*(\{[\s\S]*?\})\s*\}/gi;
|
|
38916
|
+
while ((match2 = funcCallPattern.exec(text)) !== null) {
|
|
38917
|
+
try {
|
|
38918
|
+
const args = JSON.parse(match2[2]);
|
|
38919
|
+
extracted.push({
|
|
38920
|
+
name: match2[1],
|
|
38921
|
+
arguments: args,
|
|
38922
|
+
source: "json_text"
|
|
38923
|
+
});
|
|
38924
|
+
} catch (e) {}
|
|
38925
|
+
}
|
|
38926
|
+
const anthropicPattern = /\{\s*"type"\s*:\s*"tool_use"\s*,\s*"id"\s*:\s*"[^"]*"\s*,\s*"name"\s*:\s*"([^"]+)"\s*,\s*"input"\s*:\s*(\{[\s\S]*?\})\s*\}/gi;
|
|
38927
|
+
while ((match2 = anthropicPattern.exec(text)) !== null) {
|
|
38928
|
+
try {
|
|
38929
|
+
const args = JSON.parse(match2[2]);
|
|
38930
|
+
extracted.push({
|
|
38931
|
+
name: match2[1],
|
|
38932
|
+
arguments: args,
|
|
38933
|
+
source: "json_text"
|
|
38934
|
+
});
|
|
38935
|
+
} catch (e) {}
|
|
38936
|
+
}
|
|
38937
|
+
const jsonBlockPattern = /```(?:json)?\s*(\{[\s\S]*?\})\s*```/gi;
|
|
38938
|
+
while ((match2 = jsonBlockPattern.exec(text)) !== null) {
|
|
38939
|
+
try {
|
|
38940
|
+
const parsed = JSON.parse(match2[1]);
|
|
38941
|
+
if (parsed.name && (parsed.arguments || parsed.input || parsed.parameters)) {
|
|
38942
|
+
extracted.push({
|
|
38943
|
+
name: parsed.name,
|
|
38944
|
+
arguments: parsed.arguments || parsed.input || parsed.parameters,
|
|
38945
|
+
source: "json_text"
|
|
38946
|
+
});
|
|
38947
|
+
}
|
|
38948
|
+
} catch (e) {}
|
|
38949
|
+
}
|
|
38950
|
+
return extracted;
|
|
38951
|
+
}
|
|
38952
|
+
function inferMissingParameters(toolName, args, missingParams, context) {
|
|
38953
|
+
const inferred = { ...args };
|
|
38954
|
+
if (toolName === "Task") {
|
|
38955
|
+
if (missingParams.includes("subagent_type") && !inferred.subagent_type) {
|
|
38956
|
+
inferred.subagent_type = "general-purpose";
|
|
38957
|
+
log(`[ToolRecovery] Inferred subagent_type: general-purpose`);
|
|
38958
|
+
}
|
|
38959
|
+
let extractedTask = "";
|
|
38960
|
+
if (context) {
|
|
38961
|
+
const patterns = [
|
|
38962
|
+
/(?:I(?:'ll| will| need to| want to| am going to)|Let me|Going to)\s+([^.!?\n]+)/i,
|
|
38963
|
+
/(?:help you|assist with)\s+([^.!?\n]+)/i,
|
|
38964
|
+
/(?:explore|search|find|look for|investigate)\s+([^.!?\n]+)/i,
|
|
38965
|
+
/(?:implement|create|build|add|fix|update)\s+([^.!?\n]+)/i
|
|
38966
|
+
];
|
|
38967
|
+
for (const pattern of patterns) {
|
|
38968
|
+
const match2 = context.match(pattern);
|
|
38969
|
+
if (match2 && match2[1] && match2[1].length > 10) {
|
|
38970
|
+
extractedTask = match2[1].trim();
|
|
38971
|
+
log(`[ToolRecovery] Extracted task from context: "${extractedTask.substring(0, 50)}..."`);
|
|
38972
|
+
break;
|
|
38973
|
+
}
|
|
38974
|
+
}
|
|
38975
|
+
if (!extractedTask && context.length > 20) {
|
|
38976
|
+
const sentences = context.split(/[.!?\n]+/).filter((s) => s.trim().length > 15);
|
|
38977
|
+
if (sentences.length > 0) {
|
|
38978
|
+
extractedTask = sentences[sentences.length - 1].trim();
|
|
38979
|
+
}
|
|
38980
|
+
}
|
|
38981
|
+
}
|
|
38982
|
+
if (missingParams.includes("prompt") && !inferred.prompt) {
|
|
38983
|
+
if (inferred.description && inferred.description !== "Execute task") {
|
|
38984
|
+
inferred.prompt = inferred.description;
|
|
38985
|
+
} else if (inferred.task) {
|
|
38986
|
+
inferred.prompt = inferred.task;
|
|
38987
|
+
} else if (extractedTask) {
|
|
38988
|
+
inferred.prompt = extractedTask;
|
|
38989
|
+
} else if (context && context.length > 20) {
|
|
38990
|
+
inferred.prompt = context.substring(0, 500).trim();
|
|
38991
|
+
}
|
|
38992
|
+
if (inferred.prompt) {
|
|
38993
|
+
log(`[ToolRecovery] Inferred prompt: "${inferred.prompt.substring(0, 50)}..."`);
|
|
38994
|
+
}
|
|
38995
|
+
}
|
|
38996
|
+
if (missingParams.includes("description") && !inferred.description) {
|
|
38997
|
+
if (inferred.prompt) {
|
|
38998
|
+
inferred.description = inferred.prompt.substring(0, 50).replace(/\s+/g, " ").trim();
|
|
38999
|
+
if (inferred.description.length < inferred.prompt.length) {
|
|
39000
|
+
inferred.description += "...";
|
|
39001
|
+
}
|
|
39002
|
+
} else if (extractedTask) {
|
|
39003
|
+
inferred.description = extractedTask.substring(0, 50).trim();
|
|
39004
|
+
} else {
|
|
39005
|
+
inferred.description = "Execute task";
|
|
39006
|
+
}
|
|
39007
|
+
log(`[ToolRecovery] Inferred description: ${inferred.description}`);
|
|
39008
|
+
}
|
|
39009
|
+
}
|
|
39010
|
+
if (toolName === "Bash") {
|
|
39011
|
+
if (missingParams.includes("command") && !inferred.command) {
|
|
39012
|
+
inferred.command = inferred.cmd || inferred.shell || inferred.script || "";
|
|
39013
|
+
}
|
|
39014
|
+
if (missingParams.includes("description") && !inferred.description) {
|
|
39015
|
+
if (inferred.command) {
|
|
39016
|
+
const cmd = inferred.command.split(" ")[0];
|
|
39017
|
+
inferred.description = `Run ${cmd} command`;
|
|
39018
|
+
}
|
|
39019
|
+
}
|
|
39020
|
+
}
|
|
39021
|
+
if (toolName === "Read") {
|
|
39022
|
+
if (missingParams.includes("file_path") && !inferred.file_path) {
|
|
39023
|
+
inferred.file_path = inferred.path || inferred.file || inferred.filename || "";
|
|
39024
|
+
}
|
|
39025
|
+
}
|
|
39026
|
+
if (toolName === "Write") {
|
|
39027
|
+
if (missingParams.includes("file_path") && !inferred.file_path) {
|
|
39028
|
+
inferred.file_path = inferred.path || inferred.file || inferred.filename || "";
|
|
39029
|
+
}
|
|
39030
|
+
if (missingParams.includes("content") && !inferred.content) {
|
|
39031
|
+
inferred.content = inferred.text || inferred.data || inferred.body || "";
|
|
39032
|
+
}
|
|
39033
|
+
}
|
|
39034
|
+
if (toolName === "Grep") {
|
|
39035
|
+
if (missingParams.includes("pattern") && !inferred.pattern) {
|
|
39036
|
+
inferred.pattern = inferred.query || inferred.search || inferred.regex || "";
|
|
39037
|
+
}
|
|
39038
|
+
}
|
|
39039
|
+
if (toolName === "Glob") {
|
|
39040
|
+
if (missingParams.includes("pattern") && !inferred.pattern) {
|
|
39041
|
+
inferred.pattern = inferred.glob || inferred.path || inferred.search || "**/*";
|
|
39042
|
+
}
|
|
39043
|
+
}
|
|
39044
|
+
return inferred;
|
|
39045
|
+
}
|
|
39046
|
+
function validateAndRepairToolCall(toolName, argsStr, toolSchemas, textContent) {
|
|
39047
|
+
const schema = toolSchemas.find((t) => t.name === toolName);
|
|
38873
39048
|
if (!schema?.input_schema) {
|
|
38874
|
-
return { valid: true,
|
|
39049
|
+
return { valid: true, args: {}, repaired: false, missingParams: [] };
|
|
38875
39050
|
}
|
|
38876
39051
|
let parsedArgs = {};
|
|
38877
39052
|
try {
|
|
38878
39053
|
parsedArgs = argsStr ? JSON.parse(argsStr) : {};
|
|
38879
39054
|
} catch (e) {
|
|
38880
|
-
|
|
39055
|
+
if (textContent) {
|
|
39056
|
+
const extracted = extractToolCallsFromText(textContent);
|
|
39057
|
+
const matching = extracted.find((tc) => tc.name === toolName);
|
|
39058
|
+
if (matching) {
|
|
39059
|
+
parsedArgs = matching.arguments;
|
|
39060
|
+
log(`[ToolRecovery] Extracted tool args from text for ${toolName}`);
|
|
39061
|
+
}
|
|
39062
|
+
}
|
|
38881
39063
|
}
|
|
38882
39064
|
const required2 = schema.input_schema.required || [];
|
|
38883
|
-
const missingParams = required2.filter((param) =>
|
|
38884
|
-
|
|
38885
|
-
|
|
39065
|
+
const missingParams = required2.filter((param) => parsedArgs[param] === undefined || parsedArgs[param] === null || parsedArgs[param] === "");
|
|
39066
|
+
if (missingParams.length === 0) {
|
|
39067
|
+
return { valid: true, args: parsedArgs, repaired: false, missingParams: [] };
|
|
39068
|
+
}
|
|
39069
|
+
const repairedArgs = inferMissingParameters(toolName, parsedArgs, missingParams, textContent);
|
|
39070
|
+
const stillMissing = required2.filter((param) => repairedArgs[param] === undefined || repairedArgs[param] === null || repairedArgs[param] === "");
|
|
39071
|
+
if (stillMissing.length === 0) {
|
|
39072
|
+
log(`[ToolRecovery] Successfully repaired tool call ${toolName}`);
|
|
39073
|
+
return { valid: true, args: repairedArgs, repaired: true, missingParams: [] };
|
|
39074
|
+
}
|
|
39075
|
+
return { valid: false, args: repairedArgs, repaired: false, missingParams: stillMissing };
|
|
39076
|
+
}
|
|
39077
|
+
var init_tool_call_recovery = __esm(() => {
|
|
39078
|
+
init_logger();
|
|
39079
|
+
});
|
|
39080
|
+
|
|
39081
|
+
// src/handlers/shared/openai-compat.ts
|
|
39082
|
+
function validateToolArguments(toolName, argsStr, toolSchemas, textContent) {
|
|
39083
|
+
const result = validateAndRepairToolCall(toolName, argsStr, toolSchemas, textContent);
|
|
39084
|
+
if (result.repaired) {
|
|
39085
|
+
log(`[ToolValidation] Repaired tool call ${toolName} - inferred missing parameters`);
|
|
39086
|
+
}
|
|
38886
39087
|
return {
|
|
38887
|
-
valid:
|
|
38888
|
-
missingParams,
|
|
38889
|
-
parsedArgs
|
|
39088
|
+
valid: result.valid,
|
|
39089
|
+
missingParams: result.missingParams,
|
|
39090
|
+
parsedArgs: result.args,
|
|
39091
|
+
repaired: result.repaired,
|
|
39092
|
+
repairedArgs: result.repaired ? result.args : undefined
|
|
38890
39093
|
};
|
|
38891
39094
|
}
|
|
38892
39095
|
function convertMessagesToOpenAI(req, modelId, filterIdentityFn) {
|
|
@@ -38983,16 +39186,44 @@ function processAssistantMessage(msg, messages) {
|
|
|
38983
39186
|
messages.push({ role: "assistant", content: msg.content });
|
|
38984
39187
|
}
|
|
38985
39188
|
}
|
|
38986
|
-
function convertToolsToOpenAI(req) {
|
|
39189
|
+
function convertToolsToOpenAI(req, summarize = false) {
|
|
38987
39190
|
return req.tools?.map((tool) => ({
|
|
38988
39191
|
type: "function",
|
|
38989
39192
|
function: {
|
|
38990
39193
|
name: tool.name,
|
|
38991
|
-
description: tool.description,
|
|
38992
|
-
parameters: removeUriFormat(tool.input_schema)
|
|
39194
|
+
description: summarize ? summarizeToolDescription(tool.name, tool.description) : tool.description,
|
|
39195
|
+
parameters: summarize ? summarizeToolParameters(tool.input_schema) : removeUriFormat(tool.input_schema)
|
|
38993
39196
|
}
|
|
38994
39197
|
})) || [];
|
|
38995
39198
|
}
|
|
39199
|
+
function summarizeToolDescription(name, description) {
|
|
39200
|
+
if (!description)
|
|
39201
|
+
return name;
|
|
39202
|
+
let clean = description.replace(/```[\s\S]*?```/g, "").replace(/<[^>]+>/g, "").replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
|
|
39203
|
+
const firstSentence = clean.match(/^[^.!?]+[.!?]/)?.[0] || clean;
|
|
39204
|
+
if (firstSentence.length > 150) {
|
|
39205
|
+
return firstSentence.slice(0, 147) + "...";
|
|
39206
|
+
}
|
|
39207
|
+
return firstSentence;
|
|
39208
|
+
}
|
|
39209
|
+
function summarizeToolParameters(schema) {
|
|
39210
|
+
if (!schema)
|
|
39211
|
+
return schema;
|
|
39212
|
+
const summarized = removeUriFormat({ ...schema });
|
|
39213
|
+
if (summarized.properties) {
|
|
39214
|
+
for (const [key, prop] of Object.entries(summarized.properties)) {
|
|
39215
|
+
const p = prop;
|
|
39216
|
+
if (p.description && p.description.length > 80) {
|
|
39217
|
+
const firstSentence = p.description.match(/^[^.!?]+[.!?]/)?.[0] || p.description;
|
|
39218
|
+
p.description = firstSentence.length > 80 ? firstSentence.slice(0, 77) + "..." : firstSentence;
|
|
39219
|
+
}
|
|
39220
|
+
if (p.enum && Array.isArray(p.enum) && p.enum.length > 5) {
|
|
39221
|
+
p.enum = p.enum.slice(0, 5);
|
|
39222
|
+
}
|
|
39223
|
+
}
|
|
39224
|
+
}
|
|
39225
|
+
return summarized;
|
|
39226
|
+
}
|
|
38996
39227
|
function filterIdentity(content) {
|
|
38997
39228
|
return content.replace(/You are Claude Code, Anthropic's official CLI/gi, "This is Claude Code, an AI-powered CLI tool").replace(/You are powered by the model named [^.]+\./gi, "You are powered by an AI model.").replace(/<claude_background_info>[\s\S]*?<\/claude_background_info>/gi, "").replace(/\n{3,}/g, `
|
|
38998
39229
|
|
|
@@ -39011,10 +39242,12 @@ function createStreamingState() {
|
|
|
39011
39242
|
curIdx: 0,
|
|
39012
39243
|
tools: new Map,
|
|
39013
39244
|
toolIds: new Set,
|
|
39014
|
-
lastActivity: Date.now()
|
|
39245
|
+
lastActivity: Date.now(),
|
|
39246
|
+
accumulatedText: ""
|
|
39015
39247
|
};
|
|
39016
39248
|
}
|
|
39017
39249
|
function createStreamingResponseHandler(c, response, adapter, target, middlewareManager, onTokenUpdate, toolSchemas) {
|
|
39250
|
+
log(`[Streaming] ===== HANDLER STARTED for ${target} =====`);
|
|
39018
39251
|
let isClosed = false;
|
|
39019
39252
|
let ping = null;
|
|
39020
39253
|
const encoder = new TextEncoder;
|
|
@@ -39055,6 +39288,34 @@ data: ${JSON.stringify(d)}
|
|
|
39055
39288
|
if (state.finalized)
|
|
39056
39289
|
return;
|
|
39057
39290
|
state.finalized = true;
|
|
39291
|
+
if (state.accumulatedText.length > 0) {
|
|
39292
|
+
const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
|
|
39293
|
+
log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
|
|
39294
|
+
}
|
|
39295
|
+
const textToolCalls = extractToolCallsFromText(state.accumulatedText);
|
|
39296
|
+
log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
|
|
39297
|
+
if (textToolCalls.length > 0) {
|
|
39298
|
+
log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
|
|
39299
|
+
if (state.textStarted) {
|
|
39300
|
+
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
39301
|
+
state.textStarted = false;
|
|
39302
|
+
}
|
|
39303
|
+
for (const tc of textToolCalls) {
|
|
39304
|
+
const toolIdx = state.curIdx++;
|
|
39305
|
+
const toolId = `tool_${Date.now()}_${toolIdx}`;
|
|
39306
|
+
send("content_block_start", {
|
|
39307
|
+
type: "content_block_start",
|
|
39308
|
+
index: toolIdx,
|
|
39309
|
+
content_block: { type: "tool_use", id: toolId, name: tc.name }
|
|
39310
|
+
});
|
|
39311
|
+
send("content_block_delta", {
|
|
39312
|
+
type: "content_block_delta",
|
|
39313
|
+
index: toolIdx,
|
|
39314
|
+
delta: { type: "input_json_delta", partial_json: JSON.stringify(tc.arguments) }
|
|
39315
|
+
});
|
|
39316
|
+
send("content_block_stop", { type: "content_block_stop", index: toolIdx });
|
|
39317
|
+
}
|
|
39318
|
+
}
|
|
39058
39319
|
if (state.reasoningStarted) {
|
|
39059
39320
|
send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
|
|
39060
39321
|
}
|
|
@@ -39073,15 +39334,23 @@ data: ${JSON.stringify(d)}
|
|
|
39073
39334
|
if (reason === "error") {
|
|
39074
39335
|
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39075
39336
|
} else {
|
|
39337
|
+
const stopReason = textToolCalls.length > 0 ? "tool_use" : "end_turn";
|
|
39076
39338
|
send("message_delta", {
|
|
39077
39339
|
type: "message_delta",
|
|
39078
|
-
delta: { stop_reason:
|
|
39340
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
39079
39341
|
usage: { output_tokens: state.usage?.completion_tokens || 0 }
|
|
39080
39342
|
});
|
|
39081
39343
|
send("message_stop", { type: "message_stop" });
|
|
39082
39344
|
}
|
|
39083
|
-
if (
|
|
39084
|
-
|
|
39345
|
+
if (onTokenUpdate) {
|
|
39346
|
+
if (state.usage) {
|
|
39347
|
+
log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
|
|
39348
|
+
onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
|
|
39349
|
+
} else {
|
|
39350
|
+
const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
|
|
39351
|
+
log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
|
|
39352
|
+
onTokenUpdate(100, estimatedOutputTokens);
|
|
39353
|
+
}
|
|
39085
39354
|
}
|
|
39086
39355
|
if (!isClosed) {
|
|
39087
39356
|
try {
|
|
@@ -39117,8 +39386,10 @@ data: ${JSON.stringify(d)}
|
|
|
39117
39386
|
}
|
|
39118
39387
|
try {
|
|
39119
39388
|
const chunk = JSON.parse(dataStr);
|
|
39120
|
-
if (chunk.usage)
|
|
39389
|
+
if (chunk.usage) {
|
|
39121
39390
|
state.usage = chunk.usage;
|
|
39391
|
+
log(`[Streaming] Usage data received: prompt=${chunk.usage.prompt_tokens}, completion=${chunk.usage.completion_tokens}, total=${chunk.usage.total_tokens}`);
|
|
39392
|
+
}
|
|
39122
39393
|
const delta = chunk.choices?.[0]?.delta;
|
|
39123
39394
|
if (delta) {
|
|
39124
39395
|
if (middlewareManager) {
|
|
@@ -39132,25 +39403,30 @@ data: ${JSON.stringify(d)}
|
|
|
39132
39403
|
const txt = delta.content || "";
|
|
39133
39404
|
if (txt) {
|
|
39134
39405
|
state.lastActivity = Date.now();
|
|
39135
|
-
if (!state.textStarted) {
|
|
39136
|
-
state.textIdx = state.curIdx++;
|
|
39137
|
-
send("content_block_start", {
|
|
39138
|
-
type: "content_block_start",
|
|
39139
|
-
index: state.textIdx,
|
|
39140
|
-
content_block: { type: "text", text: "" }
|
|
39141
|
-
});
|
|
39142
|
-
state.textStarted = true;
|
|
39143
|
-
}
|
|
39144
39406
|
const res = adapter.processTextContent(txt, "");
|
|
39145
39407
|
if (res.cleanedText) {
|
|
39146
|
-
|
|
39147
|
-
|
|
39148
|
-
|
|
39149
|
-
|
|
39150
|
-
|
|
39408
|
+
state.accumulatedText += res.cleanedText;
|
|
39409
|
+
const hasToolPattern = /<function=[^>]+>/.test(state.accumulatedText);
|
|
39410
|
+
if (!hasToolPattern) {
|
|
39411
|
+
if (!state.textStarted) {
|
|
39412
|
+
state.textIdx = state.curIdx++;
|
|
39413
|
+
send("content_block_start", {
|
|
39414
|
+
type: "content_block_start",
|
|
39415
|
+
index: state.textIdx,
|
|
39416
|
+
content_block: { type: "text", text: "" }
|
|
39417
|
+
});
|
|
39418
|
+
state.textStarted = true;
|
|
39419
|
+
}
|
|
39420
|
+
send("content_block_delta", {
|
|
39421
|
+
type: "content_block_delta",
|
|
39422
|
+
index: state.textIdx,
|
|
39423
|
+
delta: { type: "text_delta", text: res.cleanedText }
|
|
39424
|
+
});
|
|
39425
|
+
}
|
|
39151
39426
|
}
|
|
39152
39427
|
}
|
|
39153
39428
|
if (delta.tool_calls) {
|
|
39429
|
+
log(`[Streaming] Received ${delta.tool_calls.length} structured tool call(s) from model`);
|
|
39154
39430
|
for (const tc of delta.tool_calls) {
|
|
39155
39431
|
const idx = tc.index;
|
|
39156
39432
|
let t = state.tools.get(idx);
|
|
@@ -39166,11 +39442,12 @@ data: ${JSON.stringify(d)}
|
|
|
39166
39442
|
blockIndex: state.curIdx++,
|
|
39167
39443
|
started: false,
|
|
39168
39444
|
closed: false,
|
|
39169
|
-
arguments: ""
|
|
39445
|
+
arguments: "",
|
|
39446
|
+
buffered: !!toolSchemas && toolSchemas.length > 0
|
|
39170
39447
|
};
|
|
39171
39448
|
state.tools.set(idx, t);
|
|
39172
39449
|
}
|
|
39173
|
-
if (!t.started) {
|
|
39450
|
+
if (!t.started && !t.buffered) {
|
|
39174
39451
|
send("content_block_start", {
|
|
39175
39452
|
type: "content_block_start",
|
|
39176
39453
|
index: t.blockIndex,
|
|
@@ -39181,25 +39458,67 @@ data: ${JSON.stringify(d)}
|
|
|
39181
39458
|
}
|
|
39182
39459
|
if (tc.function?.arguments && t) {
|
|
39183
39460
|
t.arguments += tc.function.arguments;
|
|
39184
|
-
|
|
39185
|
-
|
|
39186
|
-
|
|
39187
|
-
|
|
39188
|
-
|
|
39461
|
+
if (!t.buffered) {
|
|
39462
|
+
send("content_block_delta", {
|
|
39463
|
+
type: "content_block_delta",
|
|
39464
|
+
index: t.blockIndex,
|
|
39465
|
+
delta: { type: "input_json_delta", partial_json: tc.function.arguments }
|
|
39466
|
+
});
|
|
39467
|
+
}
|
|
39189
39468
|
}
|
|
39190
39469
|
}
|
|
39191
39470
|
}
|
|
39192
39471
|
}
|
|
39193
39472
|
if (chunk.choices?.[0]?.finish_reason === "tool_calls") {
|
|
39194
39473
|
for (const t of Array.from(state.tools.values())) {
|
|
39195
|
-
if (
|
|
39474
|
+
if (!t.closed) {
|
|
39196
39475
|
if (toolSchemas && toolSchemas.length > 0) {
|
|
39197
|
-
const validation = validateToolArguments(t.name, t.arguments, toolSchemas);
|
|
39476
|
+
const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
|
|
39477
|
+
if (validation.repaired && validation.repairedArgs) {
|
|
39478
|
+
log(`[Streaming] Tool call ${t.name} was repaired with inferred parameters`);
|
|
39479
|
+
const repairedJson = JSON.stringify(validation.repairedArgs);
|
|
39480
|
+
log(`[Streaming] Sending repaired tool call: ${t.name} with args: ${repairedJson}`);
|
|
39481
|
+
if (t.buffered && !t.started) {
|
|
39482
|
+
send("content_block_start", {
|
|
39483
|
+
type: "content_block_start",
|
|
39484
|
+
index: t.blockIndex,
|
|
39485
|
+
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
39486
|
+
});
|
|
39487
|
+
send("content_block_delta", {
|
|
39488
|
+
type: "content_block_delta",
|
|
39489
|
+
index: t.blockIndex,
|
|
39490
|
+
delta: { type: "input_json_delta", partial_json: repairedJson }
|
|
39491
|
+
});
|
|
39492
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39493
|
+
t.started = true;
|
|
39494
|
+
t.closed = true;
|
|
39495
|
+
continue;
|
|
39496
|
+
}
|
|
39497
|
+
if (t.started) {
|
|
39498
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39499
|
+
const repairedIdx = state.curIdx++;
|
|
39500
|
+
const repairedId = `tool_repaired_${Date.now()}_${repairedIdx}`;
|
|
39501
|
+
send("content_block_start", {
|
|
39502
|
+
type: "content_block_start",
|
|
39503
|
+
index: repairedIdx,
|
|
39504
|
+
content_block: { type: "tool_use", id: repairedId, name: t.name }
|
|
39505
|
+
});
|
|
39506
|
+
send("content_block_delta", {
|
|
39507
|
+
type: "content_block_delta",
|
|
39508
|
+
index: repairedIdx,
|
|
39509
|
+
delta: { type: "input_json_delta", partial_json: repairedJson }
|
|
39510
|
+
});
|
|
39511
|
+
send("content_block_stop", { type: "content_block_stop", index: repairedIdx });
|
|
39512
|
+
t.closed = true;
|
|
39513
|
+
continue;
|
|
39514
|
+
}
|
|
39515
|
+
}
|
|
39198
39516
|
if (!validation.valid) {
|
|
39199
|
-
|
|
39517
|
+
log(`[Streaming] Tool call ${t.name} validation failed: ${validation.missingParams.join(", ")}`);
|
|
39518
|
+
const errorIdx = t.buffered ? t.blockIndex : state.curIdx++;
|
|
39200
39519
|
const errorMsg = `
|
|
39201
39520
|
|
|
39202
|
-
⚠️ Tool call "${t.name}" failed
|
|
39521
|
+
⚠️ 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.`;
|
|
39203
39522
|
send("content_block_start", {
|
|
39204
39523
|
type: "content_block_start",
|
|
39205
39524
|
index: errorIdx,
|
|
@@ -39211,12 +39530,34 @@ data: ${JSON.stringify(d)}
|
|
|
39211
39530
|
delta: { type: "text_delta", text: errorMsg }
|
|
39212
39531
|
});
|
|
39213
39532
|
send("content_block_stop", { type: "content_block_stop", index: errorIdx });
|
|
39533
|
+
if (t.started && !t.buffered) {
|
|
39534
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39535
|
+
}
|
|
39536
|
+
t.closed = true;
|
|
39537
|
+
continue;
|
|
39538
|
+
}
|
|
39539
|
+
if (t.buffered && !t.started) {
|
|
39540
|
+
const argsJson = JSON.stringify(validation.parsedArgs);
|
|
39541
|
+
send("content_block_start", {
|
|
39542
|
+
type: "content_block_start",
|
|
39543
|
+
index: t.blockIndex,
|
|
39544
|
+
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
39545
|
+
});
|
|
39546
|
+
send("content_block_delta", {
|
|
39547
|
+
type: "content_block_delta",
|
|
39548
|
+
index: t.blockIndex,
|
|
39549
|
+
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
39550
|
+
});
|
|
39551
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39552
|
+
t.started = true;
|
|
39214
39553
|
t.closed = true;
|
|
39215
39554
|
continue;
|
|
39216
39555
|
}
|
|
39217
39556
|
}
|
|
39218
|
-
|
|
39219
|
-
|
|
39557
|
+
if (t.started && !t.closed) {
|
|
39558
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39559
|
+
t.closed = true;
|
|
39560
|
+
}
|
|
39220
39561
|
}
|
|
39221
39562
|
}
|
|
39222
39563
|
}
|
|
@@ -39243,6 +39584,8 @@ data: ${JSON.stringify(d)}
|
|
|
39243
39584
|
}
|
|
39244
39585
|
var init_openai_compat = __esm(() => {
|
|
39245
39586
|
init_transform();
|
|
39587
|
+
init_logger();
|
|
39588
|
+
init_tool_call_recovery();
|
|
39246
39589
|
});
|
|
39247
39590
|
|
|
39248
39591
|
// src/handlers/openrouter-handler.ts
|
|
@@ -39663,18 +40006,24 @@ class LocalProviderHandler {
|
|
|
39663
40006
|
port;
|
|
39664
40007
|
healthChecked = false;
|
|
39665
40008
|
isHealthy = false;
|
|
39666
|
-
contextWindow =
|
|
40009
|
+
contextWindow = 32768;
|
|
39667
40010
|
sessionInputTokens = 0;
|
|
39668
40011
|
sessionOutputTokens = 0;
|
|
39669
|
-
|
|
40012
|
+
options;
|
|
40013
|
+
constructor(provider, modelName, port, options = {}) {
|
|
39670
40014
|
this.provider = provider;
|
|
39671
40015
|
this.modelName = modelName;
|
|
39672
40016
|
this.port = port;
|
|
40017
|
+
this.options = options;
|
|
39673
40018
|
this.adapterManager = new AdapterManager(modelName);
|
|
39674
40019
|
this.middlewareManager = new MiddlewareManager;
|
|
39675
40020
|
this.middlewareManager.initialize().catch((err) => {
|
|
39676
40021
|
log(`[LocalProvider:${provider.name}] Middleware init error: ${err}`);
|
|
39677
40022
|
});
|
|
40023
|
+
this.writeTokenFile(0, 0);
|
|
40024
|
+
if (options.summarizeTools) {
|
|
40025
|
+
log(`[LocalProvider:${provider.name}] Tool summarization enabled`);
|
|
40026
|
+
}
|
|
39678
40027
|
}
|
|
39679
40028
|
async checkHealth() {
|
|
39680
40029
|
if (this.healthChecked)
|
|
@@ -39711,8 +40060,16 @@ class LocalProviderHandler {
|
|
|
39711
40060
|
return false;
|
|
39712
40061
|
}
|
|
39713
40062
|
async fetchContextWindow() {
|
|
39714
|
-
|
|
39715
|
-
|
|
40063
|
+
log(`[LocalProvider:${this.provider.name}] Fetching context window...`);
|
|
40064
|
+
if (this.provider.name === "ollama") {
|
|
40065
|
+
await this.fetchOllamaContextWindow();
|
|
40066
|
+
} else if (this.provider.name === "lmstudio") {
|
|
40067
|
+
await this.fetchLMStudioContextWindow();
|
|
40068
|
+
} else {
|
|
40069
|
+
log(`[LocalProvider:${this.provider.name}] No context window fetch for this provider, using default: ${this.contextWindow}`);
|
|
40070
|
+
}
|
|
40071
|
+
}
|
|
40072
|
+
async fetchOllamaContextWindow() {
|
|
39716
40073
|
try {
|
|
39717
40074
|
const response = await fetch(`${this.provider.baseUrl}/api/show`, {
|
|
39718
40075
|
method: "POST",
|
|
@@ -39722,32 +40079,72 @@ class LocalProviderHandler {
|
|
|
39722
40079
|
});
|
|
39723
40080
|
if (response.ok) {
|
|
39724
40081
|
const data = await response.json();
|
|
39725
|
-
|
|
40082
|
+
let ctxFromInfo = data.model_info?.["general.context_length"];
|
|
40083
|
+
if (!ctxFromInfo && data.model_info) {
|
|
40084
|
+
for (const key of Object.keys(data.model_info)) {
|
|
40085
|
+
if (key.endsWith(".context_length")) {
|
|
40086
|
+
ctxFromInfo = data.model_info[key];
|
|
40087
|
+
break;
|
|
40088
|
+
}
|
|
40089
|
+
}
|
|
40090
|
+
}
|
|
39726
40091
|
const ctxFromParams = data.parameters?.match(/num_ctx\s+(\d+)/)?.[1];
|
|
39727
40092
|
if (ctxFromInfo) {
|
|
39728
|
-
this.contextWindow = parseInt(ctxFromInfo, 10);
|
|
40093
|
+
this.contextWindow = parseInt(String(ctxFromInfo), 10);
|
|
39729
40094
|
} else if (ctxFromParams) {
|
|
39730
40095
|
this.contextWindow = parseInt(ctxFromParams, 10);
|
|
39731
40096
|
} else {
|
|
39732
|
-
this.
|
|
40097
|
+
log(`[LocalProvider:${this.provider.name}] No context info found, using default: ${this.contextWindow}`);
|
|
40098
|
+
}
|
|
40099
|
+
if (ctxFromInfo || ctxFromParams) {
|
|
40100
|
+
log(`[LocalProvider:${this.provider.name}] Context window: ${this.contextWindow}`);
|
|
39733
40101
|
}
|
|
39734
|
-
log(`[LocalProvider:${this.provider.name}] Context window: ${this.contextWindow}`);
|
|
39735
40102
|
}
|
|
39736
40103
|
} catch (e) {}
|
|
39737
40104
|
}
|
|
40105
|
+
async fetchLMStudioContextWindow() {
|
|
40106
|
+
try {
|
|
40107
|
+
const response = await fetch(`${this.provider.baseUrl}/v1/models`, {
|
|
40108
|
+
method: "GET",
|
|
40109
|
+
signal: AbortSignal.timeout(3000)
|
|
40110
|
+
});
|
|
40111
|
+
if (response.ok) {
|
|
40112
|
+
const data = await response.json();
|
|
40113
|
+
log(`[LocalProvider:lmstudio] Models response: ${JSON.stringify(data).slice(0, 500)}`);
|
|
40114
|
+
const models = data.data || [];
|
|
40115
|
+
const targetModel = models.find((m) => m.id === this.modelName) || models.find((m) => m.id?.endsWith(`/${this.modelName}`)) || models.find((m) => this.modelName.includes(m.id));
|
|
40116
|
+
if (targetModel) {
|
|
40117
|
+
const ctxLength = targetModel.context_length || targetModel.max_context_length || targetModel.context_window || targetModel.max_tokens;
|
|
40118
|
+
if (ctxLength && typeof ctxLength === "number") {
|
|
40119
|
+
this.contextWindow = ctxLength;
|
|
40120
|
+
log(`[LocalProvider:lmstudio] Context window from model: ${this.contextWindow}`);
|
|
40121
|
+
return;
|
|
40122
|
+
}
|
|
40123
|
+
}
|
|
40124
|
+
this.contextWindow = 32768;
|
|
40125
|
+
log(`[LocalProvider:lmstudio] Using default context window: ${this.contextWindow}`);
|
|
40126
|
+
}
|
|
40127
|
+
} catch (e) {
|
|
40128
|
+
this.contextWindow = 32768;
|
|
40129
|
+
log(`[LocalProvider:lmstudio] Failed to fetch model info: ${e?.message || e}. Using default: ${this.contextWindow}`);
|
|
40130
|
+
}
|
|
40131
|
+
}
|
|
39738
40132
|
writeTokenFile(input, output) {
|
|
39739
40133
|
try {
|
|
39740
40134
|
this.sessionInputTokens += input;
|
|
39741
40135
|
this.sessionOutputTokens += output;
|
|
39742
|
-
const
|
|
39743
|
-
const
|
|
40136
|
+
const sessionTotal = this.sessionInputTokens + this.sessionOutputTokens;
|
|
40137
|
+
const used = input + output;
|
|
40138
|
+
const leftPct = this.contextWindow > 0 ? Math.max(0, Math.min(100, Math.round((this.contextWindow - used) / this.contextWindow * 100))) : 100;
|
|
39744
40139
|
const data = {
|
|
39745
40140
|
input_tokens: this.sessionInputTokens,
|
|
39746
40141
|
output_tokens: this.sessionOutputTokens,
|
|
39747
|
-
total_tokens:
|
|
40142
|
+
total_tokens: sessionTotal,
|
|
39748
40143
|
total_cost: 0,
|
|
39749
40144
|
context_window: this.contextWindow,
|
|
39750
40145
|
context_left_percent: leftPct,
|
|
40146
|
+
last_request_input: input,
|
|
40147
|
+
last_request_output: output,
|
|
39751
40148
|
updated_at: Date.now()
|
|
39752
40149
|
};
|
|
39753
40150
|
writeFileSync9(join9(tmpdir3(), `claudish-tokens-${this.port}.json`), JSON.stringify(data), "utf-8");
|
|
@@ -39770,11 +40167,66 @@ class LocalProviderHandler {
|
|
|
39770
40167
|
}
|
|
39771
40168
|
const { claudeRequest, droppedParams } = transformOpenAIToClaude(payload);
|
|
39772
40169
|
const messages = convertMessagesToOpenAI(claudeRequest, target, filterIdentity);
|
|
39773
|
-
const tools = convertToolsToOpenAI(claudeRequest);
|
|
40170
|
+
const tools = convertToolsToOpenAI(claudeRequest, this.options.summarizeTools);
|
|
39774
40171
|
const finalTools = this.provider.capabilities.supportsTools ? tools : [];
|
|
39775
40172
|
if (tools.length > 0 && !this.provider.capabilities.supportsTools) {
|
|
39776
40173
|
log(`[LocalProvider:${this.provider.name}] Tools stripped (not supported)`);
|
|
39777
40174
|
}
|
|
40175
|
+
if (tools.length > 0 && this.options.summarizeTools) {
|
|
40176
|
+
log(`[LocalProvider:${this.provider.name}] Tools summarized (${tools.length} tools)`);
|
|
40177
|
+
}
|
|
40178
|
+
if (messages.length > 0 && messages[0].role === "system") {
|
|
40179
|
+
let guidance = `
|
|
40180
|
+
|
|
40181
|
+
IMPORTANT INSTRUCTIONS FOR THIS MODEL:
|
|
40182
|
+
|
|
40183
|
+
1. OUTPUT BEHAVIOR:
|
|
40184
|
+
- NEVER output your internal reasoning, thinking process, or chain-of-thought as visible text.
|
|
40185
|
+
- Only output your final response, actions, or tool calls.
|
|
40186
|
+
- Do NOT ramble or speculate about what the user might want.
|
|
40187
|
+
|
|
40188
|
+
2. CONVERSATION HANDLING:
|
|
40189
|
+
- Always look back at the ORIGINAL user request in the conversation history.
|
|
40190
|
+
- When you receive results from a Task/agent you called, SYNTHESIZE those results and continue fulfilling the user's original request.
|
|
40191
|
+
- Do NOT ask "What would you like help with?" if there's already a user request in the conversation.
|
|
40192
|
+
- Only ask for clarification if the FIRST user message in the conversation is unclear.
|
|
40193
|
+
- After calling tools or agents, continue with the next step - don't restart or ask what to do.
|
|
40194
|
+
|
|
40195
|
+
3. CRITICAL - AFTER TOOL RESULTS:
|
|
40196
|
+
- When you see tool results (like file lists, search results, or command output), ALWAYS continue working.
|
|
40197
|
+
- Analyze the results and take the next action toward completing the user's request.
|
|
40198
|
+
- If the user asked for "evaluation and suggestions", you MUST provide analysis and recommendations after seeing the data.
|
|
40199
|
+
- NEVER stop after just calling one tool - continue until you've fully addressed the user's request.
|
|
40200
|
+
- If you called a Glob/Search and got files, READ important files next, then ANALYZE, then SUGGEST improvements.`;
|
|
40201
|
+
if (finalTools.length > 0) {
|
|
40202
|
+
const isQwen = target.toLowerCase().includes("qwen");
|
|
40203
|
+
if (isQwen) {
|
|
40204
|
+
guidance += `
|
|
40205
|
+
|
|
40206
|
+
4. TOOL CALLING FORMAT (CRITICAL FOR QWEN):
|
|
40207
|
+
You MUST use proper OpenAI-style function calling. Do NOT output tool calls as XML text.
|
|
40208
|
+
When you want to call a tool, use the API's tool_calls mechanism, NOT text like <function=...>.
|
|
40209
|
+
The tool calls must be structured JSON in the API response, not XML in your text output.
|
|
40210
|
+
|
|
40211
|
+
If you cannot use structured tool_calls, format as JSON:
|
|
40212
|
+
{"name": "tool_name", "arguments": {"param1": "value1", "param2": "value2"}}
|
|
40213
|
+
|
|
40214
|
+
5. TOOL PARAMETER REQUIREMENTS:`;
|
|
40215
|
+
} else {
|
|
40216
|
+
guidance += `
|
|
40217
|
+
|
|
40218
|
+
4. TOOL CALLING REQUIREMENTS:`;
|
|
40219
|
+
}
|
|
40220
|
+
guidance += `
|
|
40221
|
+
- When calling tools, you MUST include ALL required parameters. Incomplete tool calls will fail.
|
|
40222
|
+
- For Task: always include "description" (3-5 words), "prompt" (detailed instructions), and "subagent_type"
|
|
40223
|
+
- For Bash: always include "command" and "description"
|
|
40224
|
+
- For Read/Write/Edit: always include the full "file_path"
|
|
40225
|
+
- For Grep/Glob: always include "pattern"
|
|
40226
|
+
- Ensure your tool call JSON is complete with all required fields before submitting.`;
|
|
40227
|
+
}
|
|
40228
|
+
messages[0].content += guidance;
|
|
40229
|
+
}
|
|
39778
40230
|
const openAIPayload = {
|
|
39779
40231
|
model: target,
|
|
39780
40232
|
messages,
|
|
@@ -39784,6 +40236,11 @@ class LocalProviderHandler {
|
|
|
39784
40236
|
tools: finalTools.length > 0 ? finalTools : undefined,
|
|
39785
40237
|
stream_options: this.provider.capabilities.supportsStreaming ? { include_usage: true } : undefined
|
|
39786
40238
|
};
|
|
40239
|
+
if (this.provider.name === "ollama") {
|
|
40240
|
+
const numCtx = Math.max(this.contextWindow, 32768);
|
|
40241
|
+
openAIPayload.options = { num_ctx: numCtx };
|
|
40242
|
+
log(`[LocalProvider:${this.provider.name}] Setting num_ctx: ${numCtx} (detected: ${this.contextWindow})`);
|
|
40243
|
+
}
|
|
39787
40244
|
if (claudeRequest.tool_choice && finalTools.length > 0) {
|
|
39788
40245
|
const { type, name } = claudeRequest.tool_choice;
|
|
39789
40246
|
if (type === "tool" && name) {
|
|
@@ -39803,6 +40260,12 @@ class LocalProviderHandler {
|
|
|
39803
40260
|
stream: openAIPayload.stream
|
|
39804
40261
|
});
|
|
39805
40262
|
const apiUrl = `${this.provider.baseUrl}${this.provider.apiPath}`;
|
|
40263
|
+
log(`[LocalProvider:${this.provider.name}] Tools: ${openAIPayload.tools?.length || 0}, Messages: ${messages.length}`);
|
|
40264
|
+
if (openAIPayload.tools?.length > 0) {
|
|
40265
|
+
log(`[LocalProvider:${this.provider.name}] First tool: ${openAIPayload.tools[0]?.function?.name || "unknown"}`);
|
|
40266
|
+
}
|
|
40267
|
+
console.log(`[LocalProvider:${this.provider.name}] ===== ABOUT TO FETCH from ${apiUrl} =====`);
|
|
40268
|
+
log(`[LocalProvider:${this.provider.name}] ===== ABOUT TO FETCH from ${apiUrl} =====`);
|
|
39806
40269
|
try {
|
|
39807
40270
|
const response = await fetch(apiUrl, {
|
|
39808
40271
|
method: "POST",
|
|
@@ -39811,14 +40274,19 @@ class LocalProviderHandler {
|
|
|
39811
40274
|
},
|
|
39812
40275
|
body: JSON.stringify(openAIPayload)
|
|
39813
40276
|
});
|
|
40277
|
+
log(`[LocalProvider:${this.provider.name}] ===== FETCH COMPLETED, status: ${response.status} =====`);
|
|
39814
40278
|
if (!response.ok) {
|
|
39815
40279
|
const errorBody = await response.text();
|
|
40280
|
+
log(`[LocalProvider:${this.provider.name}] ERROR: ${errorBody.slice(0, 200)}`);
|
|
39816
40281
|
return this.handleErrorResponse(c, response.status, errorBody);
|
|
39817
40282
|
}
|
|
40283
|
+
log(`[LocalProvider:${this.provider.name}] Response OK, proceeding to streaming...`);
|
|
39818
40284
|
if (droppedParams.length > 0) {
|
|
39819
40285
|
c.header("X-Dropped-Params", droppedParams.join(", "));
|
|
39820
40286
|
}
|
|
40287
|
+
log(`[LocalProvider:${this.provider.name}] Streaming: ${openAIPayload.stream}`);
|
|
39821
40288
|
if (openAIPayload.stream) {
|
|
40289
|
+
log(`[LocalProvider:${this.provider.name}] ===== ENTERING STREAMING HANDLER =====`);
|
|
39822
40290
|
return createStreamingResponseHandler(c, response, adapter, target, this.middlewareManager, (input, output) => this.writeTokenFile(input, output), claudeRequest.tools);
|
|
39823
40291
|
}
|
|
39824
40292
|
const data = await response.json();
|
|
@@ -39986,7 +40454,7 @@ var exports_proxy_server = {};
|
|
|
39986
40454
|
__export(exports_proxy_server, {
|
|
39987
40455
|
createProxyServer: () => createProxyServer
|
|
39988
40456
|
});
|
|
39989
|
-
async function createProxyServer(port, openrouterApiKey, model, monitorMode = false, anthropicApiKey, modelMap) {
|
|
40457
|
+
async function createProxyServer(port, openrouterApiKey, model, monitorMode = false, anthropicApiKey, modelMap, options = {}) {
|
|
39990
40458
|
const nativeHandler = new NativeHandler(anthropicApiKey);
|
|
39991
40459
|
const openRouterHandlers = new Map;
|
|
39992
40460
|
const localProviderHandlers = new Map;
|
|
@@ -39996,13 +40464,16 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
|
|
|
39996
40464
|
}
|
|
39997
40465
|
return openRouterHandlers.get(targetModel);
|
|
39998
40466
|
};
|
|
40467
|
+
const localProviderOptions = {
|
|
40468
|
+
summarizeTools: options.summarizeTools
|
|
40469
|
+
};
|
|
39999
40470
|
const getLocalProviderHandler = (targetModel) => {
|
|
40000
40471
|
if (localProviderHandlers.has(targetModel)) {
|
|
40001
40472
|
return localProviderHandlers.get(targetModel);
|
|
40002
40473
|
}
|
|
40003
40474
|
const resolved = resolveProvider(targetModel);
|
|
40004
40475
|
if (resolved) {
|
|
40005
|
-
const handler = new LocalProviderHandler(resolved.provider, resolved.modelName, port);
|
|
40476
|
+
const handler = new LocalProviderHandler(resolved.provider, resolved.modelName, port, localProviderOptions);
|
|
40006
40477
|
localProviderHandlers.set(targetModel, handler);
|
|
40007
40478
|
log(`[Proxy] Created local provider handler: ${resolved.provider.name}/${resolved.modelName}`);
|
|
40008
40479
|
return handler;
|
|
@@ -40010,7 +40481,7 @@ async function createProxyServer(port, openrouterApiKey, model, monitorMode = fa
|
|
|
40010
40481
|
const urlParsed = parseUrlModel(targetModel);
|
|
40011
40482
|
if (urlParsed) {
|
|
40012
40483
|
const provider = createUrlProvider(urlParsed);
|
|
40013
|
-
const handler = new LocalProviderHandler(provider, urlParsed.modelName, port);
|
|
40484
|
+
const handler = new LocalProviderHandler(provider, urlParsed.modelName, port, localProviderOptions);
|
|
40014
40485
|
localProviderHandlers.set(targetModel, handler);
|
|
40015
40486
|
log(`[Proxy] Created URL-based local provider handler: ${urlParsed.baseUrl}/${urlParsed.modelName}`);
|
|
40016
40487
|
return handler;
|
|
@@ -40289,6 +40760,7 @@ var init_update_checker = __esm(() => {
|
|
|
40289
40760
|
|
|
40290
40761
|
// src/index.ts
|
|
40291
40762
|
var import_dotenv2 = __toESM(require_main(), 1);
|
|
40763
|
+
console.log("===== CLAUDISH FRESH START - CODE UPDATED =====");
|
|
40292
40764
|
import_dotenv2.config();
|
|
40293
40765
|
var isMcpMode = process.argv.includes("--mcp");
|
|
40294
40766
|
var args = process.argv.slice(2);
|
|
@@ -40367,6 +40839,8 @@ async function runCli() {
|
|
|
40367
40839
|
sonnet: cliConfig.modelSonnet,
|
|
40368
40840
|
haiku: cliConfig.modelHaiku,
|
|
40369
40841
|
subagent: cliConfig.modelSubagent
|
|
40842
|
+
}, {
|
|
40843
|
+
summarizeTools: cliConfig.summarizeTools
|
|
40370
40844
|
});
|
|
40371
40845
|
let exitCode = 0;
|
|
40372
40846
|
try {
|