cyberine
UsageRun recipes

DeepSeek request isolation — user_id

Every DeepSeek-direct dispatch now carries DeepSeek's userid isolation field, which buys content-safety, KVCache, and scheduling isolation for sub-identities under one API account. On an account...

Every DeepSeek-direct dispatch now carries DeepSeek's user_id isolation field, which buys content-safety, KVCache, and scheduling isolation for sub-identities under one API account. On an account with increased quota it also buys PER-user_id concurrency at the full limits (500 pro / 2500 flash / 2500 vision), so one id exhausting its limit 429s only that id instead of the whole account. Nothing to configure — it is derived automatically:

Call siteIdWhy that key
LoopAgentAdapter (only when deepSeekDirect)buildRunUserId(task.id) — one id per RUNKVCache isolation pays off when fan-out workers share a long prompt prefix, so one dispatch's workers reuse each other's cached prefix; a per-worker id would fragment it. Scheduling isolation still holds: one run cannot starve another.
makeOpenAICompletion (judges, gated on an api.deepseek.com baseURL)buildJudgeUserId() — per-call nonceJudge calls are single-turn with no shared prefix, so there is no cache to preserve; the value is only the separate scheduling bucket.

Helpers live in src/core/userId.ts; the id is a UUID behind a cyberine- prefix, sanitized to DeepSeek's [a-zA-Z0-9\-_]+ / max-512 contract, so it carries nothing user-identifying by construction. Non-DeepSeek providers (OpenRouter, the LiteLLM gateway) never receive the field.

The field is emitted as a TOP-LEVEL body key, never wrapped in extra_body — that envelope is a Python-SDK concept and openai-node 4.x has no equivalent, so nesting it there would put a literal extra_body object on the wire and DeepSeek would never see the id. Verify with:

npm run selftest:user-id-wire   # offline; no key, no network

Known limitation / deferred work: the isolation is real but its BENEFIT is unmeasured — nothing here proves a KVCache hit-rate or a concurrency improvement, and on a regular (non-increased-quota) account every id still pools into one concurrency count, so the scheduling half is inert until the account is upgraded. The 10-minute no-inference server disconnect DeepSeek documents alongside this field is written down in docs/providers/deepseek.md next to the IdleStreamWatchdog (CYBERINE_STREAM_WATCHDOG_MS, default 5 minutes) but has never been exercised against a live 10-minute stall, and whether an SSE : keep-alive comment resets that watchdog is untested. The Anthropic-SDK transport for the same field (metadata: {"user_id": ...}) is not wired.