Implement mandatory app-attribution headers per the RFC

dsh-llm owns the vocabulary (attribution.ts): AppIdentity with the version
read from the package manifest, userAgent(), and attributionHeaders(target,
identity) over a closed AttributionTarget union ('generic' | 'openrouter').
Both adapters send the headers on every provider request — llm-deepseek in
its fetch headers, llm-pi-ai through pi-ai's StreamOptions.headers — behind
an explicit attributionTarget config (never inferred from baseURL), with
mock-server tests asserting exact wire arrival and the absence of the
OpenRouter set by default.

The RFC moves to implemented/ amended with the settled identity (the
deepseek-harness token, the DeepSeek Harness title, the planned
deepseek-ai/deepseek-harness-sdk URL behind a FIXME until that repo exists)
and the explicit-config OpenRouter decision.
This commit is contained in:
Tianyi Cui
2026-07-04 18:14:42 +08:00
parent 7e6edb7474
commit 0ebb86e70f
18 changed files with 403 additions and 100 deletions

View File

@@ -29,6 +29,10 @@ Messages are arrays of typed content blocks: `text`, `reasoning`, `tool-call`, `
Streaming is a raw chunk protocol (`block-start`, `text-delta`, `reasoning-delta`, `tool-call-delta`, `block-end`, `usage`, `finish`). `BlockAssembler` is the single shared implementation that assembles chunks into blocks/messages.
### App attribution (`attribution.ts`)
Every product adapter must identify the application on every provider HTTP request — attribution is part of the adapter contract, not an adapter-local nicety. `attributionHeaders(target?, identity?)` builds the headers to send: the standard `User-Agent` baseline (`product/version (+url)`, from `userAgent()`) for every request, plus a provider-specific set only for an explicitly configured `AttributionTarget` (`'openrouter'` adds OpenRouter's documented `HTTP-Referer` / `X-OpenRouter-Title` / `X-OpenRouter-Categories`; the target is adapter config, never inferred from a base URL). The default `APP_IDENTITY` carries only static public product facts (its version is read from this package's manifest); a white-label deployment passes its own `AppIdentity`, and omission falls back to the default — nothing can suppress attribution. An adapter proves compliance with a wire-level test: a mock server asserting the received headers (or, for a library-backed adapter, that the library's header hook delivers the same values). Policy and rationale: [Mandatory app-attribution headers](../../../docs/rfc/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md).
### Classes
- `LlmAdapter` — abstract base class for provider adapters. The only required method is `stream()`.