Skip to content

[Refactoring] Decompose 238-line trackTokenUsage in containers/api-proxy/token-tracker-http.js into testable sub-functions #4934

Closed

Description

@github-actions

Refactoring Opportunity

Summary

  • File: containers/api-proxy/token-tracker-http.js
  • Current size: 305 lines
  • Responsibilities identified: 3 distinct concerns collapsed into a single 238-line function
  • Score: 5 (3 mixed responsibilities + 2 functions >80 lines)

Evidence

trackTokenUsage is 238 lines (lines 67–303)

The entire file is one exported function containing two large inner functions and three distinct phases:

trackTokenUsage(proxyRes, opts)
  ├─ Phase 1: Stream setup (~40 lines, lines 85–181)
  │   ├─ State variable initialization (chunks, streaming flags, partial-line buffer)
  │   ├─ Decompressor creation (conditional)
  │   └─ Event listener wiring (data/end on proxyRes or decompressor)
  │
  ├─ Inner fn handleDecodedChunk (~38 lines, lines 111–148) [embedded]
  │   ├─ Streaming path: SSE line assembly, extractUsageFromSseLine, usage accumulation
  │   └─ Non-streaming path: buffer with overflow guard
  │
  └─ Inner fn finalizeTracking (~120 lines, lines 183–302) [embedded]
      ├─ Skip non-2xx responses
      ├─ Finalize SSE partial line or parse buffered JSON
      ├─ normalizeUsage + cache-read rollup warning
      ├─ onUsage callback
      ├─ incrementTokenMetrics
      ├─ buildTokenUsageRecord + mergeBudgetFields
      ├─ writeTokenUsage (JSONL log)
      ├─ logRequest summary
      └─ onSpanEnd callback

finalizeTracking alone is ~120 lines, contains 8 distinct actions, and is currently untestable in isolation because it closes over all the phase-1 state variables.

The companion file token-tracker-ws.js uses the same inner-function pattern, confirmed by issue #4711 (now closed/completed) which fixed the duplicate budgetResult + cache-read rollup logic. The closed-over inner function approach was the root cause of that duplication.

Proposed Split

containers/api-proxy/token-tracker-http.js (refactored, ~150 lines):

// Extracted inner → top-level, accepting explicit state
function createChunkHandler({ requestId, streaming, chunks, ... }) {
  // returns handleDecodedChunk — no closure over outer scope
}

function finalizeHttpTracking(state, opts) {
  // explicit args instead of closed-over vars
  // testable without standing up a full HTTP response stream
}

function trackTokenUsage(proxyRes, opts) {
  const state = initState(opts); // ~10 lines
  const onChunk = createChunkHandler(state);
  wireListeners(proxyRes, state, onChunk, () => finalizeHttpTracking(state, opts));
}

This pattern:

  1. Makes finalizeHttpTracking unit-testable by passing explicit state instead of relying on closure
  2. Mirrors the shape of token-tracker-ws.js to reduce future duplication risk (see closed issue [Duplicate Code] budgetResult fields merge and cache-read rollup warning duplicated in HTTP and WebSocket token trackers #4711)
  3. Keeps the public API (trackTokenUsage) unchanged for the single caller token-tracker.js

Affected Callers

containers/api-proxy/token-tracker.js — the only caller; imports { trackTokenUsage }

Public API is unchanged — no caller updates needed.

Effort Estimate

Low — mechanical extraction of the two inner functions; the main risk is preserving the variable capture semantics while switching from closure to explicit state passing.

Benefits


Detected by Refactoring Scanner workflow. Run date: 2026-06-14

Generated by Refactoring Opportunity Scanner · ◷

  • expires on Aug 13, 2026, 3:41 PM UTC

Activity

  1. added a commit that references this issue on Jun 14, 2026
    4533f6d
  2. added a commit that references this issue on Jun 14, 2026
    1a4fde6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions