@bman654/clodex 1.0.2 → 1.0.4
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/cli.js +96 -30
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -202,7 +202,7 @@ import { join } from "path";
|
|
|
202
202
|
// package.json
|
|
203
203
|
var package_default = {
|
|
204
204
|
name: "@bman654/clodex",
|
|
205
|
-
version: "1.0.
|
|
205
|
+
version: "1.0.4",
|
|
206
206
|
publishConfig: {
|
|
207
207
|
access: "public"
|
|
208
208
|
},
|
|
@@ -7092,13 +7092,24 @@ function translateMessages(messages, npm, openAiPromptCacheBreakpoints = false)
|
|
|
7092
7092
|
}
|
|
7093
7093
|
return out;
|
|
7094
7094
|
}
|
|
7095
|
-
function
|
|
7095
|
+
function sanitizeToolInput(input, requiredProps) {
|
|
7096
7096
|
const out = {};
|
|
7097
7097
|
for (const [k, v] of Object.entries(input)) {
|
|
7098
|
-
if (v
|
|
7098
|
+
if (v === null) continue;
|
|
7099
|
+
if (Array.isArray(v) && v.length === 0 && !requiredProps?.has(k)) continue;
|
|
7100
|
+
out[k] = v;
|
|
7099
7101
|
}
|
|
7100
7102
|
return out;
|
|
7101
7103
|
}
|
|
7104
|
+
function toolRequiredProps(tools) {
|
|
7105
|
+
const map = /* @__PURE__ */ new Map();
|
|
7106
|
+
for (const [name, t] of Object.entries(tools ?? {})) {
|
|
7107
|
+
const schema = t.inputSchema?.jsonSchema;
|
|
7108
|
+
const required = Array.isArray(schema?.required) ? schema.required : [];
|
|
7109
|
+
map.set(name, new Set(required.filter((r) => typeof r === "string")));
|
|
7110
|
+
}
|
|
7111
|
+
return map;
|
|
7112
|
+
}
|
|
7102
7113
|
function translateTools(anthropicTools) {
|
|
7103
7114
|
if (!anthropicTools?.length) return void 0;
|
|
7104
7115
|
const tools = {};
|
|
@@ -7208,13 +7219,17 @@ function forwardAbortSignal(source, target) {
|
|
|
7208
7219
|
source.addEventListener("abort", forward, { once: true });
|
|
7209
7220
|
return () => source.removeEventListener("abort", forward);
|
|
7210
7221
|
}
|
|
7211
|
-
async function writeAnthropicStream(stream, modelId, write, log12, observer) {
|
|
7222
|
+
async function writeAnthropicStream(stream, modelId, write, log12, observer, tools) {
|
|
7212
7223
|
const messageId = "msg_" + Date.now();
|
|
7224
|
+
const requiredProps = toolRequiredProps(tools);
|
|
7213
7225
|
let blockIndex = -1;
|
|
7214
7226
|
let started = false;
|
|
7215
7227
|
let openType = null;
|
|
7216
7228
|
let pendingThinkingSig;
|
|
7217
7229
|
const idToBlock = /* @__PURE__ */ new Map();
|
|
7230
|
+
const toolJsonBuffer = /* @__PURE__ */ new Map();
|
|
7231
|
+
const flushedTools = /* @__PURE__ */ new Set();
|
|
7232
|
+
let openToolId = null;
|
|
7218
7233
|
let finishReason = "end_turn";
|
|
7219
7234
|
let usage = {
|
|
7220
7235
|
input_tokens: 0,
|
|
@@ -7254,8 +7269,20 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer) {
|
|
|
7254
7269
|
});
|
|
7255
7270
|
pendingThinkingSig = void 0;
|
|
7256
7271
|
}
|
|
7272
|
+
if (openType === "tool" && openToolId !== null && !flushedTools.has(openToolId)) {
|
|
7273
|
+
const buffered = toolJsonBuffer.get(openToolId);
|
|
7274
|
+
if (buffered) {
|
|
7275
|
+
emit("content_block_delta", {
|
|
7276
|
+
type: "content_block_delta",
|
|
7277
|
+
index: blockIndex,
|
|
7278
|
+
delta: { type: "input_json_delta", partial_json: buffered }
|
|
7279
|
+
});
|
|
7280
|
+
}
|
|
7281
|
+
flushedTools.add(openToolId);
|
|
7282
|
+
}
|
|
7257
7283
|
if (openType) emit("content_block_stop", { type: "content_block_stop", index: blockIndex });
|
|
7258
7284
|
openType = null;
|
|
7285
|
+
openToolId = null;
|
|
7259
7286
|
};
|
|
7260
7287
|
const openBlock = (type, contentBlock) => {
|
|
7261
7288
|
ensureStart();
|
|
@@ -7317,32 +7344,45 @@ async function writeAnthropicStream(stream, modelId, write, log12, observer) {
|
|
|
7317
7344
|
input: {}
|
|
7318
7345
|
});
|
|
7319
7346
|
idToBlock.set(part.id ?? "", blockIndex);
|
|
7347
|
+
openToolId = part.id ?? "";
|
|
7320
7348
|
break;
|
|
7321
7349
|
}
|
|
7322
|
-
case "tool-input-delta":
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
index: idToBlock.get(part.id ?? "") ?? blockIndex,
|
|
7326
|
-
delta: { type: "input_json_delta", partial_json: part.delta ?? part.text ?? "" }
|
|
7327
|
-
});
|
|
7350
|
+
case "tool-input-delta": {
|
|
7351
|
+
const id = part.id ?? "";
|
|
7352
|
+
toolJsonBuffer.set(id, (toolJsonBuffer.get(id) ?? "") + (part.delta ?? part.text ?? ""));
|
|
7328
7353
|
break;
|
|
7354
|
+
}
|
|
7329
7355
|
case "tool-input-end":
|
|
7330
7356
|
break;
|
|
7331
7357
|
case "tool-call": {
|
|
7332
7358
|
finishReason = "tool_use";
|
|
7333
|
-
|
|
7359
|
+
const id = part.toolCallId ?? "";
|
|
7360
|
+
if (idToBlock.has(id)) {
|
|
7361
|
+
if (!flushedTools.has(id)) {
|
|
7362
|
+
const json = part.input !== void 0 && part.input !== null ? JSON.stringify(sanitizeToolInput(part.input, requiredProps.get(part.toolName ?? ""))) : toolJsonBuffer.get(id) ?? "";
|
|
7363
|
+
if (json) {
|
|
7364
|
+
emit("content_block_delta", {
|
|
7365
|
+
type: "content_block_delta",
|
|
7366
|
+
index: idToBlock.get(id) ?? blockIndex,
|
|
7367
|
+
delta: { type: "input_json_delta", partial_json: json }
|
|
7368
|
+
});
|
|
7369
|
+
}
|
|
7370
|
+
flushedTools.add(id);
|
|
7371
|
+
}
|
|
7372
|
+
} else if (openType !== "tool") {
|
|
7334
7373
|
const sig = grabRoundTripSignature(part);
|
|
7335
7374
|
openBlock("tool", {
|
|
7336
7375
|
type: "tool_use",
|
|
7337
|
-
id: encodeToolUseId(
|
|
7376
|
+
id: encodeToolUseId(id, sig),
|
|
7338
7377
|
name: part.toolName,
|
|
7339
7378
|
input: {}
|
|
7340
7379
|
});
|
|
7341
7380
|
emit("content_block_delta", {
|
|
7342
7381
|
type: "content_block_delta",
|
|
7343
7382
|
index: blockIndex,
|
|
7344
|
-
delta: { type: "input_json_delta", partial_json: JSON.stringify(
|
|
7383
|
+
delta: { type: "input_json_delta", partial_json: JSON.stringify(sanitizeToolInput(part.input ?? {}, requiredProps.get(part.toolName ?? ""))) }
|
|
7345
7384
|
});
|
|
7385
|
+
flushedTools.add(id);
|
|
7346
7386
|
}
|
|
7347
7387
|
break;
|
|
7348
7388
|
}
|
|
@@ -7407,7 +7447,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log12, obs
|
|
|
7407
7447
|
}
|
|
7408
7448
|
})();
|
|
7409
7449
|
try {
|
|
7410
|
-
await writeAnthropicStream(watchedStream, modelId, write, log12, { ...observer, abortSignal });
|
|
7450
|
+
await writeAnthropicStream(watchedStream, modelId, write, log12, { ...observer, abortSignal }, params.tools);
|
|
7411
7451
|
} finally {
|
|
7412
7452
|
stopForwardingAbort();
|
|
7413
7453
|
clearTimeout(idleTimer);
|
|
@@ -7498,6 +7538,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
7498
7538
|
if (!generateAbort.signal.aborted) generateAbort.abort();
|
|
7499
7539
|
}
|
|
7500
7540
|
}
|
|
7541
|
+
const requiredProps = toolRequiredProps(params.tools);
|
|
7501
7542
|
return {
|
|
7502
7543
|
id: "msg_" + Date.now(),
|
|
7503
7544
|
type: "message",
|
|
@@ -7509,7 +7550,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
7509
7550
|
type: "tool_use",
|
|
7510
7551
|
id: encodeToolUseId(tc.toolCallId, grabRoundTripSignature(tc)),
|
|
7511
7552
|
name: tc.toolName,
|
|
7512
|
-
input:
|
|
7553
|
+
input: sanitizeToolInput(tc.input ?? {}, requiredProps.get(tc.toolName))
|
|
7513
7554
|
}))
|
|
7514
7555
|
],
|
|
7515
7556
|
stop_reason: finishReason === "tool-calls" ? "tool_use" : "end_turn",
|
|
@@ -7558,6 +7599,8 @@ function anthropicPromptTooLongMessage(body, contextWindow) {
|
|
|
7558
7599
|
}
|
|
7559
7600
|
|
|
7560
7601
|
// src/proxy.ts
|
|
7602
|
+
var STREAM_KEEPALIVE_INTERVAL_MS = 2e4;
|
|
7603
|
+
var STREAM_KEEPALIVE_PING = 'event: ping\ndata: {"type":"ping"}\n\n';
|
|
7561
7604
|
function createTranslationLifecycle(logPath, requestId, modelId, provider) {
|
|
7562
7605
|
if (!logPath || !requestId) return void 0;
|
|
7563
7606
|
const startedAt = Date.now();
|
|
@@ -7901,6 +7944,9 @@ function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPa
|
|
|
7901
7944
|
});
|
|
7902
7945
|
translationLifecycle?.dispatched();
|
|
7903
7946
|
if (clientWantsStream) {
|
|
7947
|
+
const keepAliveMs = Number(process.env.CLODEX_STREAM_KEEPALIVE_INTERVAL_MS) || STREAM_KEEPALIVE_INTERVAL_MS;
|
|
7948
|
+
let lastDownstreamWriteAt = Date.now();
|
|
7949
|
+
let lastUpstreamPartAt = Date.now();
|
|
7904
7950
|
const writeStreamChunk = (chunk) => {
|
|
7905
7951
|
translationLifecycle?.onOutput(chunk);
|
|
7906
7952
|
if (!res.headersSent) {
|
|
@@ -7910,23 +7956,43 @@ function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPa
|
|
|
7910
7956
|
"Connection": "keep-alive"
|
|
7911
7957
|
});
|
|
7912
7958
|
}
|
|
7959
|
+
lastDownstreamWriteAt = Date.now();
|
|
7913
7960
|
res.write(chunk);
|
|
7914
7961
|
};
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
{
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7962
|
+
const keepAlive = setInterval(() => {
|
|
7963
|
+
if (res.writableEnded || !res.headersSent) return;
|
|
7964
|
+
const now = Date.now();
|
|
7965
|
+
const outputIdleMs = now - lastDownstreamWriteAt;
|
|
7966
|
+
const upstreamIdleMs = now - lastUpstreamPartAt;
|
|
7967
|
+
if (outputIdleMs >= keepAliveMs && upstreamIdleMs < keepAliveMs) {
|
|
7968
|
+
lastDownstreamWriteAt = now;
|
|
7969
|
+
res.write(STREAM_KEEPALIVE_PING);
|
|
7970
|
+
plog(() => `stream keepalive ping: output idle ${outputIdleMs}ms, upstream active (${upstreamIdleMs}ms since last part)`);
|
|
7971
|
+
}
|
|
7972
|
+
}, keepAliveMs);
|
|
7973
|
+
keepAlive.unref();
|
|
7974
|
+
try {
|
|
7975
|
+
await withResponsesWebSocketDiagnosticContext(
|
|
7976
|
+
{ requestId: relayRequestId, claudeSessionId },
|
|
7977
|
+
() => streamAnthropicResponse(
|
|
7978
|
+
model,
|
|
7979
|
+
params,
|
|
7980
|
+
originalModel,
|
|
7981
|
+
writeStreamChunk,
|
|
7982
|
+
plog,
|
|
7983
|
+
{
|
|
7984
|
+
onPart: (partType) => {
|
|
7985
|
+
lastUpstreamPartAt = Date.now();
|
|
7986
|
+
translationLifecycle?.onPart(partType);
|
|
7987
|
+
},
|
|
7988
|
+
initialInputTokens: estimateAnthropicInputTokens(anthropicBody),
|
|
7989
|
+
abortSignal: clientAbort.signal
|
|
7990
|
+
}
|
|
7991
|
+
)
|
|
7992
|
+
);
|
|
7993
|
+
} finally {
|
|
7994
|
+
clearInterval(keepAlive);
|
|
7995
|
+
}
|
|
7930
7996
|
translationLifecycle?.complete();
|
|
7931
7997
|
if (!res.headersSent) writeStreamChunk("");
|
|
7932
7998
|
res.end();
|