feat(usage): add feature attribution header to API requests#407
feat(usage): add feature attribution header to API requests#407pedroheyerdahl wants to merge 2 commits intodevfrom
Conversation
Introduce X-KILOCODE-FEATURE header for tracking request origin across different entry points (CLI, VSCode extension, autocomplete). - Add HEADER_FEATURE, DEFAULT_FEATURE, and ENV_FEATURE constants - Add getFeatureHeader() helper reading from KILOCODE_FEATURE env var - Include feature header in buildKiloHeaders() when env var is set - Set feature to "autocomplete" for FIM/completion requests - Set KILOCODE_FEATURE="vscode-extension" when spawning CLI from VSCode - Default to "cli" feature in opencode entry point - Add unit tests for feature header logic
Code Review SummaryStatus: 2 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (7 files)
|
| export const HEADER_FEATURE = "X-KILOCODE-FEATURE" | ||
|
|
||
| /** Default feature value (CLI usage) */ | ||
| export const DEFAULT_FEATURE = "cli" |
There was a problem hiding this comment.
I am wondering if we should instead of a default use 'unknown' so that when new products roll out and we forget this change, we see it in the data
There was a problem hiding this comment.
agreed, and I thought I had changed that. thanks for catching!
There was a problem hiding this comment.
The way I've found to derive cli feature while keeping misattributed new products as unkjonwn is to check if env var is not set and there's no serve command. does that make sense to you?
…rom command context
Remove the hardcoded DEFAULT_FEATURE ("cli") constant from kilo-gateway
and instead derive the feature value at runtime in opencode. When the
KILOCODE_FEATURE env var is not set, the "serve" command is now tagged
as "unknown" (to surface misconfiguration when a caller forgets to set
the env var), while all other commands default to "cli".
| // (any command other than 'serve') is tagged as 'cli'. If 'serve' is spawned without | ||
| // the env var, it gets 'unknown' so the misconfiguration is visible in data. | ||
| if (!process.env[ENV_FEATURE]) { | ||
| const isServe = process.argv.includes("serve") |
There was a problem hiding this comment.
WARNING: process.argv.includes("serve") matches "serve" anywhere in the argument list, not just as the yargs command. If a future flag or argument value happens to be "serve", this would incorrectly tag the process as "unknown" instead of "cli".
A more precise check:
| const isServe = process.argv.includes("serve") | |
| const isServe = process.argv[2] === "serve" |
This targets the yargs command position directly (argv[0] = node, argv[1] = script, argv[2] = command).
| ...process.env, | ||
| KILO_SERVER_PASSWORD: password, | ||
| KILO_CLIENT: "vscode", | ||
| KILOCODE_FEATURE: "vscode-extension", // kilocode_change - feature tracking |
There was a problem hiding this comment.
SUGGESTION: The env var name "KILOCODE_FEATURE" is hardcoded here as a string literal, while other packages use the ENV_FEATURE constant from @kilocode/kilo-gateway. If the env var name ever changes, this would need a manual update.
Consider importing and using the constant for consistency, or leave a comment noting the intentional duplication if importing from kilo-gateway isn't desirable in this package.
Summary
Adds the
X-KILOCODE-FEATUREheader to all LLM requests going through kilo-gateway, enabling per-feature attribution of token usage inmicrodollar_usage. This is Step 7 of the Microdollar Feature Tracking plan.Problem
There's no way to distinguish which feature generates each token usage record in
microdollar_usage. All requests from the CLI, VS Code extension, and cloud features look identical at the gateway level.Solution
Every LLM request now includes an
X-KILOCODE-FEATUREheader identifying the calling feature. The value is determined by theKILOCODE_FEATUREenvironment variable, which callers set before spawning the kilo CLI process.Changes
packages/kilo-gateway/src/api/constants.tsHEADER_FEATURE,DEFAULT_FEATURE,ENV_FEATUREconstantspackages/kilo-gateway/src/headers.tsgetFeatureHeader()— readsprocess.env.KILOCODE_FEATURE, returnsundefinedwhen not setbuildKiloHeaders()conditionally includes the feature header only when the env var is setgetFeatureHeader()to prevent misattribution — missing env var = no header = NULL in DBpackages/kilo-gateway/src/server/routes.ts/kilo/fim) now includesbuildKiloHeaders()+ hardcoded[HEADER_FEATURE]: "autocomplete"overridefetch()calls without any kilo headerspackages/kilo-vscode/src/services/cli-backend/server-manager.tsKILOCODE_FEATURE: "vscode-extension"to the spawn env when launching the kilo CLI processpackages/opencode/src/index.tsprocess.env.KILOCODE_FEATURE = "cli"if not already set by a callerpackages/kilo-gateway/src/index.tsgetFeatureHeaderpackages/kilo-gateway/src/headers.test.ts(new)getFeatureHeader()andbuildKiloHeaders()feature header behaviorTesting
bun test src/headers.test.ts)