openzoo 0.48.65 → 0.48.67
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 +48 -0
- package/package.json +1 -1
package/lib/anthropic.js
CHANGED
|
@@ -115,6 +115,12 @@ export function openAIToAnthropic(data, requestedModel) {
|
|
|
115
115
|
const msg = choice.message ?? {};
|
|
116
116
|
const content = [];
|
|
117
117
|
if (msg.content) content.push({ type: 'text', text: msg.content });
|
|
118
|
+
// Same reason as the streaming path: a refusal arrives with `content: null`
|
|
119
|
+
// and the sentence in `refusal`, so dropping it yields an empty message that
|
|
120
|
+
// reads as a broken model rather than as the answer it is.
|
|
121
|
+
else if (typeof msg.refusal === 'string' && msg.refusal.length) {
|
|
122
|
+
content.push({ type: 'text', text: msg.refusal });
|
|
123
|
+
}
|
|
118
124
|
for (const t of msg.tool_calls ?? []) {
|
|
119
125
|
let input = {};
|
|
120
126
|
try { input = JSON.parse(t.function?.arguments || '{}'); } catch { input = {}; }
|
|
@@ -240,6 +246,48 @@ export function streamOpenAIToAnthropic(res, upstream, requestedModel, onReceipt
|
|
|
240
246
|
if (!ch) return;
|
|
241
247
|
start();
|
|
242
248
|
const delta = ch.delta ?? {};
|
|
249
|
+
// A REASONING CHUNK IS NOT A SILENT CHUNK.
|
|
250
|
+
//
|
|
251
|
+
// Only `delta.content` produced output here, so a reasoning model emitted
|
|
252
|
+
// NOTHING on the socket for the whole time it was thinking. fable reasons
|
|
253
|
+
// before it answers — measured at 53 completion tokens to say one word — so
|
|
254
|
+
// a real turn is minutes of zero bytes, and any hop in between is entitled
|
|
255
|
+
// to drop a connection that has gone idle.
|
|
256
|
+
//
|
|
257
|
+
// OBSERVED: a fable turn rendering as "I'll ground this in your own context
|
|
258
|
+
// first (holo), scout the monorepo layout, then f" and stopping mid-word.
|
|
259
|
+
// Claude Code 2.1.232 PRESERVES partial responses on a mid-stream drop
|
|
260
|
+
// instead of erroring, which is why this reads as "empty/truncated output"
|
|
261
|
+
// rather than as the connection failure it is.
|
|
262
|
+
//
|
|
263
|
+
// `ping` is in Anthropic's own SSE grammar and carries no content, so a
|
|
264
|
+
// client either ignores it or treats it as liveness — never as text.
|
|
265
|
+
if (!(typeof delta.content === 'string' && delta.content.length)
|
|
266
|
+
&& !(delta.tool_calls ?? []).length
|
|
267
|
+
&& !(typeof delta.refusal === 'string' && delta.refusal.length)) {
|
|
268
|
+
ev('ping', {});
|
|
269
|
+
}
|
|
270
|
+
// A REFUSAL IS A RESPONSE. SHOW IT.
|
|
271
|
+
//
|
|
272
|
+
// Upstream returns refusals out-of-band: `finish_reason: "content_filter"`,
|
|
273
|
+
// `content: null`, and the actual sentence in `refusal`. We only ever read
|
|
274
|
+
// `delta.content`, so the whole turn translated to a VALID, EMPTY stream —
|
|
275
|
+
// message_start, message_stop, output_tokens 0 — and rendered as silence.
|
|
276
|
+
//
|
|
277
|
+
// OBSERVED: a security audit of the user's OWN exploited protocol returning
|
|
278
|
+
// nothing at all, repeatedly, and reading as "fable is broken" when the
|
|
279
|
+
// provider had in fact answered: "This request triggered restrictions on
|
|
280
|
+
// violative cyber content and was blocked under Anthropic's Usage Policy."
|
|
281
|
+
// Being told you were refused is actionable — you can rephrase, or route to
|
|
282
|
+
// another model. Being shown an empty box is not.
|
|
283
|
+
if (typeof delta.refusal === 'string' && delta.refusal.length) {
|
|
284
|
+
if (textIndex < 0) {
|
|
285
|
+
textIndex = nextIndex++;
|
|
286
|
+
ev('content_block_start', { index: textIndex, content_block: { type: 'text', text: '' } });
|
|
287
|
+
open.add(textIndex);
|
|
288
|
+
}
|
|
289
|
+
ev('content_block_delta', { index: textIndex, delta: { type: 'text_delta', text: delta.refusal } });
|
|
290
|
+
}
|
|
243
291
|
if (typeof delta.content === 'string' && delta.content.length) {
|
|
244
292
|
if (textIndex < 0) {
|
|
245
293
|
textIndex = nextIndex++;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.67",
|
|
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",
|