@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
53 lines (52 loc) • 3.34 kB
TypeScript
/**
* Reads token usage out of Qwen Code's own session transcripts.
*
* Layout, confirmed on a real machine: `~/.qwen/projects/<sanitized-cwd>/chats/
* <sessionId>.jsonl`, where the sanitized cwd is the absolute working-directory
* path with every `/` replaced by `-` — the same convention Claude Code uses
* for its own `~/.claude/projects` layout, which is unsurprising since Qwen
* Code's chat-recording model traces back to the same lineage as Gemini CLI's.
* `~/.qwen/projects/<slug>/` also holds a sibling `memory/` directory with a
* `MEMORY.md` file and no JSONL, so the walk below is scoped to `chats/`
* specifically rather than the whole project directory.
*
* The bundled CLI (`@qwen-code/qwen-code`, installed copy inspected directly)
* references `parentSessionId` the same way Gemini CLI's engine does for
* subagent transcripts, so this reader recurses under each project's `chats/`
* directory at any depth rather than globbing one level — the same lesson
* `claudeCodeReader.ts` already paid for.
*
* Each line is a flat record with `type`, `uuid`, and (for `type: "assistant"`)
* a `model` and `usageMetadata` object shaped like Google's GenAI
* `usageMetadata`: `promptTokenCount`, `candidatesTokenCount`,
* `thoughtsTokenCount`, `totalTokenCount`, `cachedContentTokenCount`. On a real
* machine, `totalTokenCount` equalled `promptTokenCount + candidatesTokenCount
* + thoughtsTokenCount` exactly across all 21 real assistant records sampled —
* so `thoughtsTokenCount` is additive into output, not a separate bucket.
*
* `cachedContentTokenCount` is a SUBSET of `promptTokenCount`, not disjoint —
* confirmed by reading the installed CLI's own converters rather than
* inferring from samples, because every real sample on this machine had zero
* cache tokens. `buildAnthropicUsageMetadata` in
* `chunks/anthropicContentGenerator-*.js` and the OpenAI-compatible converter
* in `chunks/chunk-*.js` both derive `cachedContentTokenCount` from the
* upstream response's own cache-read field and leave `promptTokenCount` as the
* upstream's full prompt count — the cached portion is never added on top. So
* `inputTokens` here is `prompt - cached`, matching the convention this
* subsystem's `LocalUsageTotals` type expects (`inputTokens` +
* `cacheReadTokens` must sum to the true prompt size without double-counting),
* the same subtraction `codexReader.ts` does for the same reason.
*
* Cost is deliberately `unavailable`. Two independent reasons, either one
* sufficient on its own: (1) no provider identifier is logged alongside the
* model name, and Qwen Code is pluggable to arbitrary OpenAI- or
* Anthropic-compatible backends — on the machine this was written against, the
* model names logged were `claude-sonnet-4-5` and `claude-haiku-4-5`, i.e. this
* particular install was routed through a custom/litellm endpoint rather than
* Alibaba's own Qwen models, so even the model name does not reliably identify
* a vendor to price against; (2) Qwen Code's own hosted models are commonly
* used via a subscription/OAuth flow rather than metered API keys, the same
* situation that keeps Codex's confidence `unavailable`.
*/
import type { LocalUsageReader } from "../types/index.js";
export declare function createQwenCodeReader(): Promise<LocalUsageReader>;