/** * Register a DeepSeek-backed provider in `ctx.web`. It calls the Anthropic-compatible Messages API * with native `web_search_20250305`. The provider reuses `DEEPSEEK_API_KEY` but not * `DEEPSEEK_BASE_URL`, because search and chat-completions use different bases. * @module @deepseek-ai/dsh-web-search-deepseek */ import type { Context } from 'cordis' import z from 'schemastery' import type {} from '@deepseek-ai/dsh-agent' import { credentialRef } from '@deepseek-ai/dsh-credentials' import type {} from '@deepseek-ai/dsh-session' import type {} from '@deepseek-ai/dsh-web' import { DeepSeekSearchProvider, DEEPSEEK_DEFAULT_API_VERSION, DEEPSEEK_DEFAULT_BASE_URL, DEEPSEEK_DEFAULT_MAX_TOKENS, DEEPSEEK_DEFAULT_MAX_USES, DEEPSEEK_DEFAULT_MODEL, } from './provider.ts' export { DeepSeekSearchProvider, DEEPSEEK_DEFAULT_API_VERSION, DEEPSEEK_DEFAULT_BASE_URL, DEEPSEEK_DEFAULT_MAX_TOKENS, DEEPSEEK_DEFAULT_MAX_USES, DEEPSEEK_DEFAULT_MODEL, DEEPSEEK_PROVIDER_ID, } from './provider.ts' export type { DeepSeekSearchLlmRequest, DeepSeekSearchProviderOptions } from './provider.ts' /** Cordis plugin name used by loader diagnostics. */ export const name = 'web-search-deepseek' /** The web seam this provider registers into. */ export const inject = ['web'] const DEFAULT_API_KEY_ENV = 'DEEPSEEK_API_KEY' /** Plugin config (all optional — `apply` fills env-var and constant defaults). */ export interface Config { /** Literal DeepSeek API key; prefer {@link apiKeyEnv} so no secret enters configuration files. */ apiKey?: string /** Credential reference resolved for each search; defaults to `DEEPSEEK_API_KEY`. */ apiKeyEnv?: string /** Anthropic-compatible endpoint base; `/messages` is appended. */ baseURL?: string /** Anthropic-format model name. Defaults to `deepseek-v4-flash`. */ model?: string /** `anthropic-version` header value. Defaults to `2023-06-01`. */ apiVersion?: string /** Upper bound on generated tokens for the Messages request. Defaults to 4096. */ maxTokens?: number /** Maximum `web_search` server-tool uses per request. Defaults to 5. */ maxUses?: number } export const Config: z = z.object({ apiKey: z.string().role('secret'), apiKeyEnv: z.string().role('credential-ref').default(DEFAULT_API_KEY_ENV), baseURL: z.string(), model: z.string(), apiVersion: z.string(), maxTokens: z.number().step(1).min(1), maxUses: z.number().step(1).min(1), }) /** Register the DeepSeek search provider with `ctx.web`. */ export function apply(ctx: Context, config: Config): void { const maxTokens = config.maxTokens ?? DEEPSEEK_DEFAULT_MAX_TOKENS const maxUses = config.maxUses ?? DEEPSEEK_DEFAULT_MAX_USES const apiKeyEnv = credentialRef(config.apiKeyEnv ?? DEFAULT_API_KEY_ENV) const literalApiKey = config.apiKey !== undefined && config.apiKey.length > 0 ? config.apiKey : undefined ctx.web.registerSearchProvider(new DeepSeekSearchProvider({ ...literalApiKey === undefined ? {} : { apiKey: literalApiKey }, resolveApiKey: async () => { const credentials = ctx.get('credentials') if (credentials !== undefined) return (await credentials.resolve(apiKeyEnv))?.value const ambient = process.env[apiKeyEnv] return ambient !== undefined && ambient.length > 0 ? ambient : undefined }, apiKeyEnv, baseURL: config.baseURL ?? DEEPSEEK_DEFAULT_BASE_URL, model: config.model ?? DEEPSEEK_DEFAULT_MODEL, apiVersion: config.apiVersion ?? DEEPSEEK_DEFAULT_API_VERSION, maxTokens, maxUses, recordRequest: (request) => { ctx.get('agents')?.currentInitiator()?.session.append( 'web/deepseek-search-llm-request', request, ) }, })) }