@bman654/clodex 1.0.3 → 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 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.3",
205
+ version: "1.0.4",
206
206
  publishConfig: {
207
207
  access: "public"
208
208
  },
@@ -7599,6 +7599,8 @@ function anthropicPromptTooLongMessage(body, contextWindow) {
7599
7599
  }
7600
7600
 
7601
7601
  // src/proxy.ts
7602
+ var STREAM_KEEPALIVE_INTERVAL_MS = 2e4;
7603
+ var STREAM_KEEPALIVE_PING = 'event: ping\ndata: {"type":"ping"}\n\n';
7602
7604
  function createTranslationLifecycle(logPath, requestId, modelId, provider) {
7603
7605
  if (!logPath || !requestId) return void 0;
7604
7606
  const startedAt = Date.now();
@@ -7942,6 +7944,9 @@ function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPa
7942
7944
  });
7943
7945
  translationLifecycle?.dispatched();
7944
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();
7945
7950
  const writeStreamChunk = (chunk) => {
7946
7951
  translationLifecycle?.onOutput(chunk);
7947
7952
  if (!res.headersSent) {
@@ -7951,23 +7956,43 @@ function startProxyCatalog(routes, defaultAliasId, debug = false, inferenceLogPa
7951
7956
  "Connection": "keep-alive"
7952
7957
  });
7953
7958
  }
7959
+ lastDownstreamWriteAt = Date.now();
7954
7960
  res.write(chunk);
7955
7961
  };
7956
- await withResponsesWebSocketDiagnosticContext(
7957
- { requestId: relayRequestId, claudeSessionId },
7958
- () => streamAnthropicResponse(
7959
- model,
7960
- params,
7961
- originalModel,
7962
- writeStreamChunk,
7963
- plog,
7964
- {
7965
- onPart: (partType) => translationLifecycle?.onPart(partType),
7966
- initialInputTokens: estimateAnthropicInputTokens(anthropicBody),
7967
- abortSignal: clientAbort.signal
7968
- }
7969
- )
7970
- );
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
+ }
7971
7996
  translationLifecycle?.complete();
7972
7997
  if (!res.headersSent) writeStreamChunk("");
7973
7998
  res.end();