openzoo 0.48.47 → 0.48.49
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/proxy.js +39 -3
- package/package.json +1 -1
package/lib/proxy.js
CHANGED
|
@@ -863,6 +863,10 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
863
863
|
// answer back on the way out. See lib/anthropic.js.
|
|
864
864
|
let anthropicMode = false;
|
|
865
865
|
let anthropicModel = null;
|
|
866
|
+
// The CLIENT's streaming intent, kept separate from the body we send
|
|
867
|
+
// upstream. The Anthropic lane asks the gateway for a complete message (we
|
|
868
|
+
// can only translate a finished one) while still owing the caller SSE.
|
|
869
|
+
let clientWantsStream = false;
|
|
866
870
|
let responsesMode = false;
|
|
867
871
|
let responsesModel = null;
|
|
868
872
|
let responsesCustom = null; // names of freeform tools needing custom_tool_call on the way back
|
|
@@ -912,7 +916,24 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
912
916
|
try {
|
|
913
917
|
const inbound = JSON.parse(bodyBuf.toString('utf8'));
|
|
914
918
|
anthropicModel = inbound.model;
|
|
915
|
-
|
|
919
|
+
// ANTHROPIC CLIENTS CANNOT READ AN OPENAI STREAM.
|
|
920
|
+
//
|
|
921
|
+
// The gateway now streams for real, and `relay()` pipes those frames
|
|
922
|
+
// through untouched — which is correct for an OpenAI client and
|
|
923
|
+
// unreadable to Claude Code, which speaks the Anthropic SSE grammar
|
|
924
|
+
// (message_start / content_block_delta / message_stop). It surfaced as
|
|
925
|
+
// "API returned an empty or malformed response (HTTP 200)": a 200 whose
|
|
926
|
+
// body the client cannot parse.
|
|
927
|
+
//
|
|
928
|
+
// The translation we have (openAIToAnthropic + writeAnthropicSse) works
|
|
929
|
+
// on a COMPLETE message, so this lane asks the gateway not to stream and
|
|
930
|
+
// keeps the buffered translation. That costs Claude Code the
|
|
931
|
+
// time-to-first-byte win until an incremental OpenAI->Anthropic frame
|
|
932
|
+
// translator exists; a readable answer late beats an unreadable one now.
|
|
933
|
+
const converted = anthropicToOpenAI(inbound);
|
|
934
|
+
clientWantsStream = converted.stream === true || inbound.stream === true;
|
|
935
|
+
converted.stream = false;
|
|
936
|
+
bodyBuf = Buffer.from(JSON.stringify(converted));
|
|
916
937
|
anthropicMode = true;
|
|
917
938
|
req.url = '/v1/chat/completions';
|
|
918
939
|
url = `${config.apiBase}${req.url}`;
|
|
@@ -934,12 +955,27 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
|
|
|
934
955
|
if (rewritablePath(req.method, req.url)) {
|
|
935
956
|
const rw = await maybeRewriteModel(bodyBuf);
|
|
936
957
|
if (rw) {
|
|
937
|
-
|
|
958
|
+
// SAY WHETHER THE OVERRIDE IS ACTUALLY SET, and name it.
|
|
959
|
+
//
|
|
960
|
+
// This used to print "(OPENZOO_DEFAULT_MODEL overrides)" on EVERY
|
|
961
|
+
// rewrite whether or not the variable existed — a hint about a knob,
|
|
962
|
+
// phrased as a statement about this request. It cost a real incident:
|
|
963
|
+
// the proxy was restarted from a shell carrying
|
|
964
|
+
// OPENZOO_DEFAULT_MODEL=deepseek/deepseek-v4-pro-0813, so every
|
|
965
|
+
// claude-sonnet-5 ask was served by deepseek, and the log line looked
|
|
966
|
+
// exactly the same as it always had. deepseek matches the reasoning
|
|
967
|
+
// regex, so a 16-token safety classification became a 4,000-token
|
|
968
|
+
// reasoning generation — 11.5s, past the caller's timeout, and Claude
|
|
969
|
+
// Code reported "claude-sonnet-5 is temporarily unavailable".
|
|
970
|
+
const forced = process.env.OPENZOO_DEFAULT_MODEL;
|
|
971
|
+
log(forced
|
|
972
|
+
? `model "${rw.from}" -> FORCED to ${forced} by OPENZOO_DEFAULT_MODEL (nearest match would have been ${rw.to})`
|
|
973
|
+
: `model "${rw.from}" is not on the zoo — nearest match ${rw.to}`);
|
|
938
974
|
bodyBuf = rw.body;
|
|
939
975
|
}
|
|
940
976
|
try {
|
|
941
977
|
const parsed = JSON.parse(bodyBuf.toString('utf8'));
|
|
942
|
-
wantsStream = parsed?.stream === true;
|
|
978
|
+
wantsStream = parsed?.stream === true || clientWantsStream;
|
|
943
979
|
// REASONING MODELS SPEND max_tokens ON THINKING FIRST.
|
|
944
980
|
//
|
|
945
981
|
// The budget covers hidden reasoning AND the visible answer, so a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.49",
|
|
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",
|