From b7e2bdcb1c88c3a68912a30ceb21ac810822cc65 Mon Sep 17 00:00:00 2001 From: xiongxiao Date: Mon, 16 Mar 2026 02:13:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=8E=B7=E5=8F=96=20?= =?UTF-8?q?OpenCode=20=E5=8F=AF=E7=94=A8=E6=A8=A1=E5=9E=8B=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=88=9B=E5=BB=BA=E5=AE=A2=E6=88=B7=E7=AB=AF=E7=9A=84?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assistant/src/routes/opencode/cnb.ts | 69 +++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/assistant/src/routes/opencode/cnb.ts b/assistant/src/routes/opencode/cnb.ts index e9c7d8e..3f6167c 100644 --- a/assistant/src/routes/opencode/cnb.ts +++ b/assistant/src/routes/opencode/cnb.ts @@ -4,6 +4,50 @@ import { getClient } from './module/client.ts'; import dayjs from 'dayjs'; import { Session } from '@opencode-ai/sdk'; +app.route({ + path: 'opencode-cnb', + key: 'models', + middleware: ['auth-admin'], + description: '获取 OpenCode 可用模型列表,返回 providerID 和 modelID', + metadata: { + args: { + baseUrl: z.string().optional().describe('OpenCode 服务地址,默认为 http://localhost:4096'), + } + } +}).define(async (ctx) => { + const { baseUrl } = ctx.query; + const client = await getClient({ baseUrl }); + if (!client) { + ctx.body = { content: '获取 OpenCode 客户端失败' }; + return; + } + const res = await client.provider.list(); + const providerData = (res as any)?.data ?? res; + const all: any[] = providerData?.all ?? []; + const connected: string[] = providerData?.connected ?? []; + const defaultModels: Record = providerData?.default ?? {}; + + const models: Array<{ providerID: string; providerName: string; modelID: string; modelName: string; isDefault: boolean; isConnected: boolean }> = []; + for (const provider of all) { + const isConnected = connected.includes(provider.id); + for (const [modelKey, model] of Object.entries(provider.models ?? {})) { + models.push({ + providerID: provider.id, + providerName: provider.name, + modelID: modelKey, + modelName: model.name ?? modelKey, + isDefault: defaultModels[provider.id] === modelKey, + isConnected, + }); + } + } + + ctx.body = { + content: `共 ${models.length} 个模型,已连接提供商: ${connected.join(', ') || '无'}`, + data: { models, connected, default: defaultModels }, + }; +}).addTo(app); + app.route({ path: 'opencode-cnb', key: 'question', @@ -14,13 +58,16 @@ app.route({ question: z.string().describe('问题'), baseUrl: z.string().optional().describe('OpenCode 服务地址,默认为 http://localhost:4096'), directory: z.string().optional().describe('运行目录,默认为根目录'), - messageID: z.string().optional().describe('消息 ID,选填'), + messageId: z.string().optional().describe('消息 ID,选填'), sessionId: z.string().optional().describe('会话 ID,选填'), + providerId: z.string().optional().describe('指定使用的提供商 ID,默认为空,表示使用默认提供商'), + modelId: z.string().optional().describe('指定使用的模型 ID,默认为空,表示使用默认模型'), parts: z.array(z.any()).optional().describe('消息内容的分块,优先于 question 参数'), + awaitAnswer: z.boolean().optional().describe('是否等待回答完成,默认为 false,开启后会在回答完成后返回完整回答内容') } } }).define(async (ctx) => { - const { question, baseUrl, directory = '/workspace', messageID, sessionId, parts } = ctx.query; + const { question, baseUrl, directory = '/workspace', messageId, sessionId, parts, awaitAnswer = false, providerId, modelId } = ctx.query; const client = await getClient({ baseUrl: baseUrl }); if (!client) { ctx.body = { content: `OpenCode 客户端获取失败` }; @@ -53,16 +100,24 @@ app.route({ session = createSession.data; } let _parts: any[] = parts ?? [{ type: "text", text: question }]; - const message = await client.session.prompt({ + let data: any = null; + let model = null; + if (providerId && modelId) { + model = { providerID: providerId, modelID: modelId }; + } + const promptPromise = client.session.prompt({ body: { - messageID: messageID, + messageID: messageId, parts: _parts, + model }, path: { id: sessionId || session.id, }, }) - const data = message.data; - - ctx.body = { content: `已经启动`, data }; + if (awaitAnswer) { + const message = await promptPromise; + data = message.data; + } + ctx.body = { content: awaitAnswer ? `已经完成` : `运行中`, data, sessionId: session.id, messageId: messageId }; }).addTo(app); \ No newline at end of file