@dianshuv/copilot-api 0.16.0 → 0.17.0

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.
Files changed (2) hide show
  1. package/dist/main.mjs +89 -20
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -1010,7 +1010,7 @@ const logout = defineCommand({
1010
1010
 
1011
1011
  //#endregion
1012
1012
  //#region package.json
1013
- var version = "0.16.0";
1013
+ var version = "0.17.0";
1014
1014
 
1015
1015
  //#endregion
1016
1016
  //#region src/lib/event-loop-lag.ts
@@ -2715,6 +2715,20 @@ function formatTokens(input, output) {
2715
2715
  return `${formatNumber(input)}/${formatNumber(output)}`;
2716
2716
  }
2717
2717
  /**
2718
+ * Cache-hit segment for the complete line, e.g. `cache=90%` (or
2719
+ * `cache=90% (1.6K/1.8K)` under --verbose). Returns undefined when there is no
2720
+ * usable denominator or the request had zero cache hits, so non-cached and
2721
+ * non-LLM requests stay terse (minimize-noise). `total` is the full prompt size
2722
+ * normalized across API families; see completeTracking / the per-path extraction.
2723
+ */
2724
+ function formatCacheHitRate(cached, total) {
2725
+ if (total === void 0 || total <= 0) return void 0;
2726
+ if (cached === void 0 || cached <= 0) return void 0;
2727
+ const pct = Math.round(cached / total * 100);
2728
+ if (consola.level >= 5) return `cache=${pct}% (${formatNumber(cached)}/${formatNumber(total)})`;
2729
+ return `cache=${pct}%`;
2730
+ }
2731
+ /**
2718
2732
  * Console renderer that shows request lifecycle with apt-get style footer
2719
2733
  *
2720
2734
  * Log format:
@@ -2816,7 +2830,7 @@ var ConsoleRenderer = class {
2816
2830
  * Format a complete log line with colored parts
2817
2831
  */
2818
2832
  formatLogLine(parts) {
2819
- const { prefix, time, method, path, model, status, duration, tokens, queueWait, phases, extra, isError, isDim } = parts;
2833
+ const { prefix, time, method, path, model, status, duration, tokens, cache, queueWait, phases, extra, isError, isDim } = parts;
2820
2834
  if (isDim) {
2821
2835
  const modelPart = model ? ` ${model}` : "";
2822
2836
  const extraPart = extra ? ` ${extra}` : "";
@@ -2830,6 +2844,7 @@ var ConsoleRenderer = class {
2830
2844
  if (duration) result += ` ${pc.yellow(duration)}`;
2831
2845
  if (queueWait) result += ` ${pc.dim(`(queued ${queueWait})`)}`;
2832
2846
  if (tokens) result += ` ${pc.blue(tokens)}`;
2847
+ if (cache) result += ` ${pc.dim(cache)}`;
2833
2848
  if (phases) result += ` ${pc.dim(phases)}`;
2834
2849
  if (extra) result += isError ? pc.red(extra) : extra;
2835
2850
  return result;
@@ -2901,6 +2916,7 @@ var ConsoleRenderer = class {
2901
2916
  const status = request.statusCode ?? 0;
2902
2917
  const isError = request.status === "error" || status >= 400;
2903
2918
  const tokens = request.model ? formatTokens(request.inputTokens, request.outputTokens) : void 0;
2919
+ const cache = formatCacheHitRate(request.cachedInputTokens, request.totalInputTokens);
2904
2920
  const queueWait = request.queueWaitMs && request.queueWaitMs > 100 ? formatDuration(request.queueWaitMs) : void 0;
2905
2921
  const message = this.formatLogLine({
2906
2922
  prefix: isError ? "[FAIL]" : "[ OK ]",
@@ -2912,6 +2928,7 @@ var ConsoleRenderer = class {
2912
2928
  duration: formatDuration(request.durationMs ?? 0),
2913
2929
  queueWait,
2914
2930
  tokens,
2931
+ cache,
2915
2932
  phases: this.formatPhases(request),
2916
2933
  extra: isError && request.error ? `: ${request.error}` : void 0,
2917
2934
  isError,
@@ -2980,6 +2997,8 @@ var RequestTracker = class {
2980
2997
  if (update.inputTokens !== void 0) request.inputTokens = update.inputTokens;
2981
2998
  if (update.outputTokens !== void 0) request.outputTokens = update.outputTokens;
2982
2999
  if (update.reasoningTokens !== void 0) request.reasoningTokens = update.reasoningTokens;
3000
+ if (update.cachedInputTokens !== void 0) request.cachedInputTokens = update.cachedInputTokens;
3001
+ if (update.totalInputTokens !== void 0) request.totalInputTokens = update.totalInputTokens;
2983
3002
  if (update.error !== void 0) request.error = update.error;
2984
3003
  if (update.queuePosition !== void 0) request.queuePosition = update.queuePosition;
2985
3004
  if (update.queueWaitMs !== void 0) request.queueWaitMs = update.queueWaitMs;
@@ -4147,13 +4166,15 @@ function updateTrackerStatus(trackingId, status) {
4147
4166
  requestTracker.updateRequest(trackingId, { status });
4148
4167
  }
4149
4168
  /** Complete TUI tracking and send PostHog analytics */
4150
- function completeTracking(trackingId, inputTokens, outputTokens, queueWaitMs, reasoningTokens, analytics, timings) {
4169
+ function completeTracking(trackingId, inputTokens, outputTokens, queueWaitMs, reasoningTokens, analytics, timings, cache) {
4151
4170
  if (!trackingId) return;
4152
4171
  requestTracker.updateRequest(trackingId, {
4153
4172
  inputTokens,
4154
4173
  outputTokens,
4155
4174
  queueWaitMs,
4156
4175
  reasoningTokens,
4176
+ cachedInputTokens: cache?.cachedInputTokens,
4177
+ totalInputTokens: cache?.totalInputTokens,
4157
4178
  ...timingsToUpdate(timings)
4158
4179
  });
4159
4180
  requestTracker.completeRequest(trackingId, 200, {
@@ -4265,6 +4286,9 @@ function formatClientTruncationMarker(result) {
4265
4286
  function getReasoningTokensFromOpenAIUsage(usage) {
4266
4287
  return usage?.completion_tokens_details?.reasoning_tokens;
4267
4288
  }
4289
+ function getCachedTokensFromOpenAIUsage(usage) {
4290
+ return usage?.prompt_tokens_details?.cached_tokens;
4291
+ }
4268
4292
  async function handleCompletion$1(c) {
4269
4293
  const rawPayload = await c.req.json();
4270
4294
  consola.debug("Request payload:", JSON.stringify(rawPayload).slice(-400));
@@ -4383,7 +4407,9 @@ function handleNonStreamingResponse$1(c, originalResponse, ctx, payload) {
4383
4407
  inputTokens: usage.prompt_tokens,
4384
4408
  outputTokens: usage.completion_tokens,
4385
4409
  queueWaitMs: ctx.queueWaitMs,
4386
- reasoningTokens
4410
+ reasoningTokens,
4411
+ cachedInputTokens: getCachedTokensFromOpenAIUsage(usage),
4412
+ totalInputTokens: usage.prompt_tokens
4387
4413
  });
4388
4414
  captureRequest({
4389
4415
  model: response.model,
@@ -4424,6 +4450,7 @@ function createStreamAccumulator() {
4424
4450
  model: "",
4425
4451
  inputTokens: 0,
4426
4452
  outputTokens: 0,
4453
+ cachedTokens: 0,
4427
4454
  reasoningTokens: 0,
4428
4455
  finishReason: "",
4429
4456
  content: "",
@@ -4467,7 +4494,10 @@ async function handleStreamingResponse$1(opts) {
4467
4494
  durationMs: Date.now() - ctx.startTime,
4468
4495
  stopReason: acc.finishReason || void 0,
4469
4496
  toolCount: payload.tools?.length ?? 0
4470
- }, ctx.timings);
4497
+ }, ctx.timings, {
4498
+ cachedInputTokens: acc.cachedTokens,
4499
+ totalInputTokens: acc.inputTokens
4500
+ });
4471
4501
  } catch (error) {
4472
4502
  if (isAbortError(error)) {
4473
4503
  consola.debug("[ChatCompletions] client disconnected mid-stream; upstream aborted");
@@ -4536,6 +4566,7 @@ function accumulateParsedChunk(parsed, acc, checkRepetition) {
4536
4566
  if (parsed.usage) {
4537
4567
  acc.inputTokens = parsed.usage.prompt_tokens;
4538
4568
  acc.outputTokens = parsed.usage.completion_tokens;
4569
+ acc.cachedTokens = parsed.usage.prompt_tokens_details?.cached_tokens ?? 0;
4539
4570
  acc.reasoningTokens = getReasoningTokensFromOpenAIUsage(parsed.usage) ?? 0;
4540
4571
  }
4541
4572
  const choice = parsed.choices?.[0];
@@ -7149,6 +7180,8 @@ function createAnthropicStreamAccumulator() {
7149
7180
  model: "",
7150
7181
  inputTokens: 0,
7151
7182
  outputTokens: 0,
7183
+ cacheReadInputTokens: 0,
7184
+ cacheCreationInputTokens: 0,
7152
7185
  stopReason: "",
7153
7186
  content: "",
7154
7187
  toolCalls: [],
@@ -7158,6 +7191,9 @@ function createAnthropicStreamAccumulator() {
7158
7191
  }
7159
7192
  function processAnthropicEvent(event, acc) {
7160
7193
  switch (event.type) {
7194
+ case "message_start":
7195
+ handleMessageStart(event.message.usage, acc);
7196
+ break;
7161
7197
  case "content_block_delta":
7162
7198
  handleContentBlockDelta(event.delta, acc);
7163
7199
  break;
@@ -7193,11 +7229,20 @@ function handleContentBlockStop(acc) {
7193
7229
  acc.currentToolCall = null;
7194
7230
  }
7195
7231
  }
7232
+ function applyPromptSideUsage(usage, acc) {
7233
+ if (usage.input_tokens !== void 0) acc.inputTokens = usage.input_tokens;
7234
+ if (usage.cache_read_input_tokens !== void 0) acc.cacheReadInputTokens = usage.cache_read_input_tokens;
7235
+ if (usage.cache_creation_input_tokens !== void 0) acc.cacheCreationInputTokens = usage.cache_creation_input_tokens;
7236
+ }
7237
+ function handleMessageStart(usage, acc) {
7238
+ if (!usage) return;
7239
+ applyPromptSideUsage(usage, acc);
7240
+ }
7196
7241
  function handleMessageDelta(delta, usage, acc) {
7197
7242
  if (delta.stop_reason) acc.stopReason = delta.stop_reason;
7198
7243
  if (usage) {
7199
- acc.inputTokens = usage.input_tokens ?? 0;
7200
7244
  acc.outputTokens = usage.output_tokens;
7245
+ applyPromptSideUsage(usage, acc);
7201
7246
  }
7202
7247
  }
7203
7248
  function recordAnthropicStreamingResponse(acc, fallbackModel, ctx) {
@@ -8400,11 +8445,17 @@ function handleDirectAnthropicNonStreamingResponse(c, response, ctx, truncateRes
8400
8445
  },
8401
8446
  toolCalls: extractToolCallsFromContent(response.content)
8402
8447
  }, Date.now() - ctx.startTime);
8403
- if (ctx.trackingId) requestTracker.updateRequest(ctx.trackingId, {
8404
- inputTokens: response.usage.input_tokens,
8405
- outputTokens: response.usage.output_tokens,
8406
- queueWaitMs: ctx.queueWaitMs
8407
- });
8448
+ if (ctx.trackingId) {
8449
+ const cacheRead = response.usage.cache_read_input_tokens ?? 0;
8450
+ const cacheCreation = response.usage.cache_creation_input_tokens ?? 0;
8451
+ requestTracker.updateRequest(ctx.trackingId, {
8452
+ inputTokens: response.usage.input_tokens,
8453
+ outputTokens: response.usage.output_tokens,
8454
+ queueWaitMs: ctx.queueWaitMs,
8455
+ cachedInputTokens: cacheRead,
8456
+ totalInputTokens: response.usage.input_tokens + cacheRead + cacheCreation
8457
+ });
8458
+ }
8408
8459
  captureRequest({
8409
8460
  model: response.model,
8410
8461
  inputTokens: response.usage.input_tokens,
@@ -8498,7 +8549,10 @@ async function handleDirectAnthropicStreamingResponse(opts) {
8498
8549
  durationMs: Date.now() - ctx.startTime,
8499
8550
  stopReason: acc.stopReason || void 0,
8500
8551
  toolCount: anthropicPayload.tools?.length ?? 0
8501
- }, ctx.timings);
8552
+ }, ctx.timings, {
8553
+ cachedInputTokens: acc.cacheReadInputTokens,
8554
+ totalInputTokens: acc.inputTokens + acc.cacheReadInputTokens + acc.cacheCreationInputTokens
8555
+ });
8502
8556
  } catch (error) {
8503
8557
  if (isAbortError(error)) {
8504
8558
  consola.debug("[Anthropic] client disconnected mid-stream; upstream aborted");
@@ -8713,11 +8767,17 @@ function handleNonStreamingResponse(opts) {
8713
8767
  },
8714
8768
  toolCalls: extractToolCallsFromContent(anthropicResponse.content)
8715
8769
  }, Date.now() - ctx.startTime);
8716
- if (ctx.trackingId) requestTracker.updateRequest(ctx.trackingId, {
8717
- inputTokens: anthropicResponse.usage.input_tokens,
8718
- outputTokens: anthropicResponse.usage.output_tokens,
8719
- queueWaitMs: ctx.queueWaitMs
8720
- });
8770
+ if (ctx.trackingId) {
8771
+ const cacheRead = anthropicResponse.usage.cache_read_input_tokens ?? 0;
8772
+ const cacheCreation = anthropicResponse.usage.cache_creation_input_tokens ?? 0;
8773
+ requestTracker.updateRequest(ctx.trackingId, {
8774
+ inputTokens: anthropicResponse.usage.input_tokens,
8775
+ outputTokens: anthropicResponse.usage.output_tokens,
8776
+ queueWaitMs: ctx.queueWaitMs,
8777
+ cachedInputTokens: cacheRead,
8778
+ totalInputTokens: anthropicResponse.usage.input_tokens + cacheRead + cacheCreation
8779
+ });
8780
+ }
8721
8781
  captureRequest({
8722
8782
  model: anthropicResponse.model,
8723
8783
  inputTokens: anthropicResponse.usage.input_tokens,
@@ -8762,7 +8822,10 @@ async function handleStreamingResponse(opts) {
8762
8822
  durationMs: Date.now() - ctx.startTime,
8763
8823
  stopReason: acc.stopReason || void 0,
8764
8824
  toolCount: anthropicPayload.tools?.length ?? 0
8765
- }, ctx.timings);
8825
+ }, ctx.timings, {
8826
+ cachedInputTokens: acc.cacheReadInputTokens,
8827
+ totalInputTokens: acc.inputTokens + acc.cacheReadInputTokens + acc.cacheCreationInputTokens
8828
+ });
8766
8829
  } catch (error) {
8767
8830
  if (isAbortError(error)) {
8768
8831
  consola.debug("[Translated] client disconnected mid-stream; upstream aborted");
@@ -9395,7 +9458,10 @@ const handleResponses = async (c) => {
9395
9458
  stream: true,
9396
9459
  durationMs: Date.now() - startTime,
9397
9460
  toolCount: tools.length
9398
- }, ctx.timings);
9461
+ }, ctx.timings, {
9462
+ cachedInputTokens: usage?.input_tokens_details?.cached_tokens ?? 0,
9463
+ totalInputTokens: usage?.input_tokens ?? 0
9464
+ });
9399
9465
  } else if (streamErrorMessage) {
9400
9466
  recordResponse(historyId, {
9401
9467
  success: false,
@@ -9446,7 +9512,10 @@ const handleResponses = async (c) => {
9446
9512
  stream: false,
9447
9513
  durationMs: Date.now() - startTime,
9448
9514
  toolCount: tools.length
9449
- }, ctx.timings);
9515
+ }, ctx.timings, {
9516
+ cachedInputTokens: usage?.input_tokens_details?.cached_tokens ?? 0,
9517
+ totalInputTokens: usage?.input_tokens ?? 0
9518
+ });
9450
9519
  consola.debug("Forwarding native Responses result:", JSON.stringify(result).slice(-400));
9451
9520
  return c.json(echoResponseBody(result, ctx));
9452
9521
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dianshuv/copilot-api",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!",
5
5
  "author": "dianshuv",
6
6
  "type": "module",