Files
deepseek-harness/packages/subagent/tool-subagent/src/list-models.ts
T
Dudu-0223 f76a225a7d Merge pull request #2663 from deepseek-harness/feat/subagent-provider
让 subagent 按需发现并选择子 Agent 模型
2026-08-24 18:23:42 +08:00

95 lines
4.1 KiB
TypeScript

/** Model-facing discovery of LLM routes available to child Agents. */
import type { Context } from '@deepseek-ai/cordis'
import type LlmRuntime from '@deepseek-ai/dsh-llm'
import type { LlmProviderInfo } from '@deepseek-ai/dsh-llm'
import { defineTool } from '@deepseek-ai/dsh-tools'
interface ListSubagentModelsRequest {
readonly provider?: string
readonly model?: string
}
/** Resolve one registered provider with a model-correctable diagnostic. */
function registeredProvider(llm: LlmRuntime, providerId: string): LlmProviderInfo {
const providers = llm.listProviders()
const provider = providers.find(candidate => candidate.id === providerId)
if (provider !== undefined) return provider
const available = providers.map(candidate => candidate.id).join(', ') || '(none)'
throw new Error(`LLM provider "${providerId}" is not registered; available providers: ${available}`)
}
/** Render one advertised or resolved model. */
function modelLine(provider: string, model: { id: string; name: string; description?: string }): string {
return `${provider}/${model.id}${model.name}${model.description === undefined ? '' : `: ${model.description}`}`
}
/** Read the requested provider, advertised models, or exact-model efforts. */
async function listSubagentModels(
ctx: Context,
request: ListSubagentModelsRequest,
signal: AbortSignal,
): Promise<string> {
const llm = ctx.get('llm')
if (llm === undefined) {
throw new Error('cannot discover child LLM routes because the `llm` service is unavailable')
}
if (request.model !== undefined && request.provider === undefined) {
throw new Error('`model` requires `provider`')
}
if (request.provider === undefined) {
const providers = llm.listProviders()
return providers.length === 0
? '(no LLM providers)'
: providers.map(provider => `${provider.id}${provider.name}`).join('\n')
}
if (request.provider.length === 0) throw new Error('`provider` must be non-empty')
const provider = registeredProvider(llm, request.provider)
if (request.model === undefined) {
const models = await llm.listModels(provider.id)
return models.length === 0
? `(no advertised models for ${provider.id})`
: models.map(model => modelLine(provider.id, model)).join('\n')
}
if (request.model.length === 0) throw new Error('`model` must be non-empty')
const model = await llm.resolveModelInfo(provider.id, request.model, signal)
const efforts = model.reasoning?.efforts.map(effort => (
`${effort.id}${model.reasoning?.defaultEffort === effort.id ? ' (default)' : ''}${effort.name}`
+ (effort.description === undefined ? '' : `: ${effort.description}`)
)).join('\n') || '(no advertised reasoning efforts)'
return `${modelLine(provider.id, model)}\nReasoning efforts:\n${efforts}`
}
/**
* Register `list_subagent_models` for one owning delegation-tool instance.
* @param ctx - Context whose tool registry owns the fixed discovery definition.
*/
export function registerListSubagentModels(ctx: Context): void {
ctx.tools.register(defineTool({
name: 'list_subagent_models',
description:
'Discover LLM routes for subagents without changing the current Agent. Call with no arguments to list '
+ 'registered providers, with `provider` to list its advertised models, or with `provider` and `model` '
+ 'to inspect that exact model and its reasoning efforts. Catalog membership is advisory: an adapter may '
+ 'accept an unlisted model id. Use the returned ids with a delegation tool\'s `provider`, `model`, and '
+ '`reasoning_effort` fields.',
parameters: {
provider: {
type: 'string',
description: 'Registered LLM provider id. Omit to list providers.',
},
model: {
type: 'string',
description: 'Exact model id to inspect. Requires provider; omit to list that provider\'s advertised models.',
},
},
output: {
schema: { type: 'string' },
render: (_args, result) => [{ type: 'text', text: result }],
},
execute(args, exec) {
return listSubagentModels(ctx, args, exec.signal)
},
}))
}