openzoo 0.48.67 → 0.48.69
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/lib/anthropic.js +37 -1
- package/package.json +1 -1
package/lib/anthropic.js
CHANGED
|
@@ -87,6 +87,28 @@ export function anthropicToOpenAI(body) {
|
|
|
87
87
|
delete out.system;
|
|
88
88
|
delete out.anthropic_version;
|
|
89
89
|
delete out.metadata;
|
|
90
|
+
// A SERVER-SIDE web_search TOOL IS A PLUGIN, NOT A FUNCTION — TRANSLATE IT.
|
|
91
|
+
//
|
|
92
|
+
// Claude Code's Web Search arrives as an Anthropic server tool (type
|
|
93
|
+
// "web_search_20250305", name "web_search") with NO input_schema, because
|
|
94
|
+
// Anthropic runs it itself. Routed to OpenRouter->some other provider, nobody
|
|
95
|
+
// runs it, and the old code simply DROPPED it (no schema) — so the model got
|
|
96
|
+
// no search capability and every Web Search came back "0 results". Silent,
|
|
97
|
+
// and worse than the 400 it replaced: the agent believes it searched.
|
|
98
|
+
//
|
|
99
|
+
// OpenRouter's `web` plugin is the cross-model equivalent — search-then-inject
|
|
100
|
+
// middleware that works on every model. Detect the server tool and switch it
|
|
101
|
+
// on, mapping max_uses -> max_results, rather than discarding the intent.
|
|
102
|
+
const webTool = (Array.isArray(body.tools) ? body.tools : [])
|
|
103
|
+
.find((t) => t?.type?.startsWith?.('web_search') || t?.name === 'web_search');
|
|
104
|
+
if (webTool) {
|
|
105
|
+
const existing = Array.isArray(body.plugins) ? body.plugins : [];
|
|
106
|
+
if (!existing.some((p) => p?.id === 'web')) {
|
|
107
|
+
const web = { id: 'web' };
|
|
108
|
+
if (Number.isFinite(webTool.max_uses)) web.max_results = webTool.max_uses;
|
|
109
|
+
out.plugins = [...existing, web];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
90
112
|
if (Array.isArray(body.tools) && body.tools.length) {
|
|
91
113
|
out.tools = body.tools
|
|
92
114
|
.filter((t) => t?.name && t?.input_schema)
|
|
@@ -96,7 +118,21 @@ export function anthropicToOpenAI(body) {
|
|
|
96
118
|
}));
|
|
97
119
|
if (!out.tools.length) delete out.tools;
|
|
98
120
|
}
|
|
99
|
-
|
|
121
|
+
// TOOL_CHOICE MAY NOT OUTLIVE TOOLS.
|
|
122
|
+
//
|
|
123
|
+
// The block above DROPS any tool without a name+input_schema — which is every
|
|
124
|
+
// server-side tool (web_search, computer, bash), because those carry no schema
|
|
125
|
+
// in the Anthropic shape. When that empties the list, `out.tools` is deleted.
|
|
126
|
+
// But tool_choice was set unconditionally, so the body went upstream with a
|
|
127
|
+
// choice and nothing to choose from.
|
|
128
|
+
//
|
|
129
|
+
// OBSERVED on grok-4.6: a Claude Code Web Search 400'd with "A tool_choice was
|
|
130
|
+
// set on the request but no tools were specified." Every provider rejects this
|
|
131
|
+
// shape; it just words it differently. Gate the choice on tools surviving.
|
|
132
|
+
const hasTools = Array.isArray(out.tools) && out.tools.length > 0;
|
|
133
|
+
if (!hasTools) {
|
|
134
|
+
delete out.tool_choice;
|
|
135
|
+
} else if (body.tool_choice?.type === 'auto') out.tool_choice = 'auto';
|
|
100
136
|
else if (body.tool_choice?.type === 'any') out.tool_choice = 'required';
|
|
101
137
|
else if (body.tool_choice?.type === 'tool') {
|
|
102
138
|
out.tool_choice = { type: 'function', function: { name: body.tool_choice.name } };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.69",
|
|
4
4
|
"description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|