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:
@@ -5,8 +5,8 @@
|
||||
* @module dsh-llm-deepseek/adapter
|
||||
*/
|
||||
|
||||
import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { attributionHeaders, LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
|
||||
import type { AttributionTarget, GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { serializeRequest } from './serialize.ts'
|
||||
import type { RequestDefaults } from './serialize.ts'
|
||||
import { parseSse } from './sse.ts'
|
||||
@@ -19,15 +19,15 @@ export interface DeepSeekAdapterOptions {
|
||||
baseURL: string
|
||||
/** Request defaults applied to every call (thinking mode, effort). */
|
||||
defaults?: RequestDefaults
|
||||
/**
|
||||
* Provider-specific attribution mapping on top of the mandatory
|
||||
* `User-Agent` baseline (dsh-llm's `attributionHeaders`). Set to
|
||||
* `'openrouter'` when `baseURL` points at OpenRouter; never inferred
|
||||
* from the URL.
|
||||
*/
|
||||
attributionTarget?: AttributionTarget | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribution header sent on every request so the provider can identify the
|
||||
* client. Bump in lockstep with this package's version (no build-time version
|
||||
* injection is wired in this repo yet).
|
||||
*/
|
||||
const USER_AGENT = 'deepseek-harness/0.0.1'
|
||||
|
||||
/** Map an HTTP status to a stable LlmError code. */
|
||||
export function httpErrorCode(status: number): string {
|
||||
if (status === 401 || status === 403) return 'AUTH'
|
||||
@@ -67,7 +67,7 @@ export class DeepSeekAdapter extends LlmAdapter {
|
||||
'authorization': `Bearer ${this.options.apiKey}`,
|
||||
'content-type': 'application/json',
|
||||
'accept': 'text/event-stream',
|
||||
'user-agent': USER_AGENT,
|
||||
...attributionHeaders(this.options.attributionTarget),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
...options.signal ? { signal: options.signal } : {},
|
||||
|
||||
@@ -45,6 +45,12 @@ export interface Config {
|
||||
thinking?: 'enabled' | 'disabled'
|
||||
/** Thinking effort (only meaningful with thinking enabled). */
|
||||
reasoningEffort?: 'high' | 'max'
|
||||
/**
|
||||
* Provider-specific attribution set to send alongside the mandatory
|
||||
* `User-Agent`: `'openrouter'` when `baseURL` points at OpenRouter.
|
||||
* Omitted = the provider-neutral baseline.
|
||||
*/
|
||||
attributionTarget?: 'generic' | 'openrouter'
|
||||
}
|
||||
|
||||
export const Config: z<Config> = z.object({
|
||||
@@ -53,6 +59,7 @@ export const Config: z<Config> = z.object({
|
||||
models: z.array(z.string()).default(['deepseek-v4-flash', 'deepseek-v4-pro']),
|
||||
thinking: z.union(['enabled', 'disabled']),
|
||||
reasoningEffort: z.union(['high', 'max']),
|
||||
attributionTarget: z.union(['generic', 'openrouter']),
|
||||
})
|
||||
|
||||
/** Public API default; the internal endpoint comes from $DEEPSEEK_BASE_URL. */
|
||||
@@ -74,5 +81,6 @@ export function apply(ctx: Context, config: Config): void {
|
||||
thinking: config.thinking,
|
||||
reasoningEffort: config.reasoningEffort,
|
||||
},
|
||||
attributionTarget: config.attributionTarget,
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user