mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Defer OpenRouter-specific attribution headers
This commit is contained in:
@@ -65,7 +65,7 @@ Do NOT write one for a mechanical or local choice (a variable name, a one-file r
|
||||
| [Extract a generic long-running tool runtime](proposed/architecture/2026-06-20-generic-long-running-tool-runtime.md) | 2026-06-20 |
|
||||
| [Extract example apps into packages](proposed/architecture/2026-06-20-extract-example-app-packages.md) | 2026-06-20 |
|
||||
| [Branded IDs everywhere they belong](proposed/architecture/2026-06-20-branded-ids.md) | 2026-06-20 |
|
||||
| [Mandatory app-attribution headers for provider requests](proposed/architecture/2026-06-21-mandatory-app-attribution-headers.md) | 2026-06-21 |
|
||||
| [Mandatory `User-Agent` attribution for provider requests](proposed/architecture/2026-06-21-mandatory-app-attribution-headers.md) | 2026-06-21 |
|
||||
|
||||
### Process
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# RFC: Mandatory app-attribution headers for provider requests
|
||||
# RFC: Mandatory `User-Agent` attribution for provider requests
|
||||
|
||||
Status: proposed
|
||||
|
||||
## Problem
|
||||
|
||||
LLM provider requests should identify the product making them. That is useful for provider-side support, abuse investigation, compatibility debugging, traffic analytics, and public app attribution where a provider exposes it. The harness only partially does this today: the hand-rolled DeepSeek adapter sends `User-Agent: deepseek-harness/0.0.1` (`packages/llm/llm-deepseek/src/adapter.ts`), while the pi-ai-backed twin has no harness-owned header path visible in this repo (`packages/llm/llm-pi-ai/src/adapter.ts`). New adapters can therefore omit attribution silently, and a library-backed adapter can drift from the hand-rolled adapter even though [the twin-adapter RFC](../../implemented/architecture/2026-06-13-twin-llm-adapters.md) exists to keep the provider seam honest across both implementations.
|
||||
LLM provider requests should identify the product making them. That is useful for provider-side support, abuse investigation, compatibility debugging, and traffic analytics. The harness only partially does this today: the hand-rolled DeepSeek adapter sends `User-Agent: deepseek-harness/0.0.1` (`packages/llm/llm-deepseek/src/adapter.ts`), while the pi-ai-backed twin has no harness-owned header path visible in this repo (`packages/llm/llm-pi-ai/src/adapter.ts`). New adapters can therefore omit attribution silently, and a library-backed adapter can drift from the hand-rolled adapter even though [the twin-adapter RFC](../../implemented/architecture/2026-06-13-twin-llm-adapters.md) exists to keep the provider seam honest across both implementations.
|
||||
|
||||
The immediate prompt came from OpenRouter's [App Attribution](https://openrouter.ai/docs/app-attribution) docs. OpenRouter creates app pages and rankings from `HTTP-Referer` plus display/category headers. That is valuable, but it is not the HTTP standard for application identity. The risk is adopting OpenRouter's exact header set as if it were universal, then leaking provider-specific headers to direct DeepSeek requests, future OpenAI/Anthropic/Vertex adapters, test servers, or proxies that log unknown fields indefinitely.
|
||||
|
||||
@@ -21,30 +21,28 @@ The immediate prompt came from OpenRouter's [App Attribution](https://openrouter
|
||||
|
||||
## Proposal
|
||||
|
||||
Make provider request attribution mandatory at the LLM adapter boundary, with a provider-neutral app identity and provider-specific wire mappings. The rule is: every product LLM adapter must send a static, non-secret application identity on every provider HTTP request, and every adapter must have tests proving the identity reaches the wire or, for a library-backed adapter, proving the configured library hook emits equivalent headers.
|
||||
Make provider request attribution mandatory at the LLM adapter boundary, using the standard `User-Agent` header only. The rule is: every product LLM adapter must send a static, non-secret application identity on every provider HTTP request, and every adapter must have tests proving the identity reaches the wire or, for a library-backed adapter, proving the configured library hook emits an equivalent `User-Agent`.
|
||||
|
||||
For OpenRouter specifically, mandatory attribution means sending **both** the provider-neutral `User-Agent` and OpenRouter's required app identifier, `HTTP-Referer`. `User-Agent` identifies the client software in the standard HTTP way; `HTTP-Referer` is the OpenRouter-specific app URL key that creates the app page and ranking entry. `X-OpenRouter-Title` and `X-OpenRouter-Categories` refine that same OpenRouter app identity.
|
||||
Do **not** implement OpenRouter app attribution in this RFC. `HTTP-Referer`, `X-OpenRouter-Title`, and `X-OpenRouter-Categories` are OpenRouter-specific product-surface headers, not provider-neutral model-request attribution. They can be proposed later by an OpenRouter adapter or explicit OpenRouter mode, with its own privacy/product decision, tests, and docs. Until then, even requests pointed at OpenRouter send only the shared `User-Agent` attribution from this RFC.
|
||||
|
||||
The provider-neutral identity should be owned outside individual adapters, ideally in `dsh-llm` or a tiny support package if importing package metadata from `dsh-llm` is too awkward. It should contain only public product facts:
|
||||
|
||||
- product token for `User-Agent`: `deepseek-code` or `deepseek-harness` (settle this when implementation chooses the public product name)
|
||||
- version: the package/root version, not a manually duplicated constant
|
||||
- app title: `DeepSeek Code`
|
||||
- app URL: the public product or repository URL, not a local workspace path
|
||||
- optional category list for providers that support public app marketplaces, initially `cli-agent`
|
||||
|
||||
The default is mandatory and non-empty. Deployments may override the title/URL/category values for white-label products or forks, but omission must fall back to the harness default rather than suppress attribution. There is no per-request API for the model, user prompt, session id, cwd, user email, API key owner, or local machine identity to influence these fields.
|
||||
The default is mandatory and non-empty. Deployments may override the public product token/version/comment values for white-label products or forks, but omission must fall back to the harness default rather than suppress attribution. There is no per-request API for the model, user prompt, session id, cwd, user email, API key owner, or local machine identity to influence these fields.
|
||||
|
||||
Wire mapping:
|
||||
|
||||
| Target | Required mapping |
|
||||
|---|---|
|
||||
| All HTTP-based adapters | Send `User-Agent` with the product token and version. Include the app URL as a comment only if the final value stays within the conservative syntax in RFC 9110. |
|
||||
| OpenRouter endpoints | Send `HTTP-Referer`, `X-OpenRouter-Title`, and, when configured, `X-OpenRouter-Categories` in addition to `User-Agent`. Use `X-OpenRouter-Title`, not legacy `X-Title`, for new code. |
|
||||
| Direct DeepSeek endpoint | Send `User-Agent`; do not send OpenRouter-only headers unless DeepSeek documents an equivalent contract. |
|
||||
| Future providers | Add a small provider-specific mapper only when that provider documents an app attribution mechanism. Do not reuse `HTTP-Referer` by analogy. |
|
||||
| OpenRouter endpoints | Send `User-Agent` only for now. Do not send `HTTP-Referer`, `X-OpenRouter-Title`, `X-Title`, or `X-OpenRouter-Categories` under this RFC. |
|
||||
| Future providers | Send `User-Agent` only unless a later provider-specific RFC accepts additional headers. Do not reuse `HTTP-Referer` by analogy. |
|
||||
|
||||
Endpoint detection should be explicit. If the adapter has an OpenRouter provider package later, that package always applies the OpenRouter mapper. If an existing OpenAI-compatible adapter can be pointed at arbitrary `baseURL` values, it may recognize `https://openrouter.ai/api/v1` exactly or expose an explicit `provider: 'openrouter'`/`attributionTarget: 'openrouter'` config. It should not infer OpenRouter from arbitrary path fragments or model names.
|
||||
Endpoint detection is not part of this RFC because no endpoint-specific mapping is accepted here. If OpenRouter support lands later, detection must be explicit: either a dedicated OpenRouter provider package or an explicit `provider: 'openrouter'` / `attributionTarget: 'openrouter'` config, not arbitrary path fragments or model names.
|
||||
|
||||
For the current twin adapters, this means the pi-ai-backed adapter cannot remain a silent exception. Either configure `@earendil-works/pi-ai` with request headers if the library supports that, wrap or contribute the missing hook upstream, or retire the library-backed adapter from product use until it can honor the same attribution contract. The value of the twin is comparing real implementations under one contract; attribution is now part of that contract.
|
||||
|
||||
@@ -53,16 +51,16 @@ For the current twin adapters, this means the pi-ai-backed adapter cannot remain
|
||||
- `dsh-llm` documents the mandatory app-attribution contract for `LlmAdapter` authors.
|
||||
- A shared helper constructs the default app identity and the standard `User-Agent` value from package metadata, so adapters do not hand-copy `deepseek-harness/0.0.1` constants.
|
||||
- `dsh-llm-deepseek` sends the shared `User-Agent` on direct DeepSeek requests and keeps the existing mock-server assertion, updated to the shared value.
|
||||
- The OpenRouter mapping, wherever implemented, sends `HTTP-Referer`, `X-OpenRouter-Title`, and optional `X-OpenRouter-Categories`, with a test that uses an OpenRouter base URL or explicit OpenRouter target and asserts the exact headers.
|
||||
- `dsh-llm-pi-ai` either sends the same attribution headers through a real library hook or is removed from adapter registration paths with a follow-up RFC explaining why the twin contract no longer justifies the maintenance cost.
|
||||
- No adapter sends OpenRouter-specific attribution headers (`HTTP-Referer`, `X-OpenRouter-Title`, `X-Title`, `X-OpenRouter-Categories`) as part of this RFC.
|
||||
- `dsh-llm-pi-ai` either sends the same `User-Agent` through a real library hook or is removed from adapter registration paths with a follow-up RFC explaining why the twin contract no longer justifies the maintenance cost.
|
||||
- No app-attribution field carries secrets, local paths, session ids, prompt text, model output, user email, or per-user stable identifiers.
|
||||
- The relevant adapter READMEs mention the attribution policy and the OpenRouter-specific mapping only where that mapping can actually be enabled.
|
||||
- The relevant adapter READMEs mention the `User-Agent` attribution policy and explicitly avoid documenting OpenRouter app attribution as implemented behavior.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**OpenRouter headers everywhere.** Rejected. It would satisfy OpenRouter rankings, but it treats a custom OpenRouter contract as a universal standard and sends fields with misleading semantics to providers that did not ask for them. It also risks using `HTTP-Referer` as a generic app URL field even though standard HTTP already has `User-Agent` for product identity and `Referer` for a different browsing-context concept.
|
||||
**OpenRouter app attribution now.** Rejected for this RFC. Sending `HTTP-Referer` plus `X-OpenRouter-Title` would satisfy OpenRouter rankings, but those headers are a provider-specific product feature, not the provider-neutral model-request attribution this RFC is trying to standardize. Supporting them should be an explicit OpenRouter adapter/mode decision later, not hidden inside the first shared attribution helper.
|
||||
|
||||
**Only `User-Agent`.** Rejected as incomplete. It is the correct baseline and the only standard mechanism, but it cannot create OpenRouter app pages or marketplace rankings because OpenRouter requires `HTTP-Referer` for that product feature.
|
||||
**OpenRouter headers everywhere.** Rejected. It would treat a custom OpenRouter contract as a universal standard and send fields with misleading semantics to providers that did not ask for them. It also risks using `HTTP-Referer` as a generic app URL field even though standard HTTP already has `User-Agent` for product identity and `Referer` for a different browsing-context concept.
|
||||
|
||||
**Only provider account/project identity.** Rejected. Organization/project headers, API keys, cloud accounts, and billing projects identify who pays or owns the request, not which application is sending traffic. They also expose no public app title/category and do not help gateways like OpenRouter build app rankings.
|
||||
|
||||
@@ -78,4 +76,4 @@ For the current twin adapters, this means the pi-ai-backed adapter cannot remain
|
||||
|
||||
**Version sourcing needs a clean implementation.** The existing `USER_AGENT = 'deepseek-harness/0.0.1'` constant is intentionally manual. Replacing it with package metadata may need a small build-time or runtime helper. That helper is worth it because stale attribution is a low-grade lie that tests can otherwise miss.
|
||||
|
||||
**OpenRouter categorization might go stale.** `cli-agent` is correct for the coding-agent demos and terminal use, but future editor-only or cloud-hosted products might deserve `ide-extension` or `cloud-agent`. Keep categories overrideable and treat them as provider-specific presentation, not the core identity.
|
||||
**OpenRouter rankings do not benefit yet.** `User-Agent` is the correct baseline for provider-neutral HTTP identity, but it will not create OpenRouter app pages or rankings because OpenRouter requires `HTTP-Referer` for that product feature. That is deliberate: public app marketplace participation is a separate product decision, not a prerequisite for mandatory request attribution.
|
||||
|
||||
Reference in New Issue
Block a user