2026-06-13 00:28:29 +08:00
|
|
|
/**
|
|
|
|
|
* DeepSeek LLM adapter plugin: registers a {@link DeepSeekAdapter} for the
|
2026-07-14 21:57:52 +08:00
|
|
|
* `deepseek` provider route on `ctx.llm`.
|
2026-06-13 00:28:29 +08:00
|
|
|
*
|
|
|
|
|
* Config is cordis-native (schemastery). Secrets flow per the repo policy:
|
|
|
|
|
* `apiKey` from cordis.yml via the `!!js` tag (`!!js process.env.DEEPSEEK_API_KEY`)
|
|
|
|
|
* or from the environment directly; never from ad-hoc files.
|
|
|
|
|
*
|
|
|
|
|
* ```yaml
|
|
|
|
|
* - id: llm-deepseek
|
|
|
|
|
* name: '@deepseek-ai/dsh-llm-deepseek'
|
|
|
|
|
* config:
|
|
|
|
|
* apiKey: !!js process.env.DEEPSEEK_API_KEY
|
|
|
|
|
* baseURL: !!js process.env.DEEPSEEK_BASE_URL
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* @module @deepseek-ai/dsh-llm-deepseek
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import type { Context } from 'cordis'
|
|
|
|
|
import z from 'schemastery'
|
|
|
|
|
import type {} from '@deepseek-ai/dsh-llm'
|
|
|
|
|
import { DeepSeekAdapter } from './adapter.ts'
|
|
|
|
|
|
|
|
|
|
export { DeepSeekAdapter, httpErrorCode } from './adapter.ts'
|
|
|
|
|
export type { DeepSeekAdapterOptions } from './adapter.ts'
|
|
|
|
|
export { serializeMessages, serializeRequest } from './serialize.ts'
|
|
|
|
|
export type { RequestDefaults } from './serialize.ts'
|
|
|
|
|
export { DONE, parseSse } from './sse.ts'
|
|
|
|
|
export { mapFinishReason, mapUsage, translate } from './translate.ts'
|
|
|
|
|
export type * from './types.ts'
|
|
|
|
|
|
|
|
|
|
export const name = 'llm-deepseek'
|
|
|
|
|
export const inject = ['llm']
|
|
|
|
|
|
2026-07-06 22:09:30 +08:00
|
|
|
/**
|
|
|
|
|
* Plugin config, validated by the same-named schemastery schema. Every field
|
|
|
|
|
* is optional in yml: credentials/endpoint fall back to the environment (a
|
|
|
|
|
* missing API key fails plugin load, not the first call), and omitted
|
|
|
|
|
* thinking fields send nothing on the wire, so the provider default applies.
|
|
|
|
|
*/
|
2026-06-13 00:28:29 +08:00
|
|
|
export interface Config {
|
|
|
|
|
/** API key; falls back to $DEEPSEEK_API_KEY. Required one way or the other. */
|
|
|
|
|
apiKey?: string
|
|
|
|
|
/** Endpoint base; falls back to $DEEPSEEK_BASE_URL, then the public API. */
|
|
|
|
|
baseURL?: string
|
|
|
|
|
/** Thinking-mode default for every request (provider default: enabled). */
|
|
|
|
|
thinking?: 'enabled' | 'disabled'
|
|
|
|
|
/** Thinking effort (only meaningful with thinking enabled). */
|
|
|
|
|
reasoningEffort?: 'high' | 'max'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const Config: z<Config> = z.object({
|
|
|
|
|
apiKey: z.string(),
|
|
|
|
|
baseURL: z.string(),
|
|
|
|
|
thinking: z.union(['enabled', 'disabled']),
|
|
|
|
|
reasoningEffort: z.union(['high', 'max']),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/** Public API default; the internal endpoint comes from $DEEPSEEK_BASE_URL. */
|
|
|
|
|
export const PUBLIC_BASE_URL = 'https://api.deepseek.com'
|
|
|
|
|
|
|
|
|
|
export function apply(ctx: Context, config: Config): void {
|
|
|
|
|
const apiKey = config.apiKey ?? process.env.DEEPSEEK_API_KEY
|
|
|
|
|
if (apiKey === undefined || apiKey.length === 0) {
|
|
|
|
|
throw new Error('llm-deepseek: an API key is required (Config.apiKey or $DEEPSEEK_API_KEY)')
|
|
|
|
|
}
|
|
|
|
|
const baseURL = config.baseURL ?? process.env.DEEPSEEK_BASE_URL ?? PUBLIC_BASE_URL
|
2026-07-14 21:57:52 +08:00
|
|
|
ctx.llm.registerAdapter(['deepseek'], new DeepSeekAdapter({
|
2026-06-13 00:28:29 +08:00
|
|
|
apiKey,
|
|
|
|
|
baseURL,
|
|
|
|
|
defaults: {
|
|
|
|
|
thinking: config.thinking,
|
|
|
|
|
reasoningEffort: config.reasoningEffort,
|
|
|
|
|
},
|
|
|
|
|
}))
|
|
|
|
|
}
|