From 1970c698ea60387a8a81a8146562ca2be461b6e0 Mon Sep 17 00:00:00 2001 From: Askhz <1361267452@qq.com> Date: Mon, 11 May 2026 21:23:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(costrict):=20=E6=A0=B9=E6=8D=AE=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E5=88=97=E8=A1=A8=E5=8A=A8=E6=80=81=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=20max=5Ftokens=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 从 /ai-gateway/api/v1/models 返回的模型信息中读取 maxTokensKey 和 maxTokens 字段,发送请求时动态注入对应参数名和参数值。 maxTokensKey 缺失时默认使用 max_tokens,maxTokens 无值时不传该参数。 Co-Authored-By: Claude Opus 4.7 --- src/costrict/provider/index.ts | 67 ++++++++++++++++++++++----------- src/costrict/provider/models.ts | 1 + 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/costrict/provider/index.ts b/src/costrict/provider/index.ts index 0db3ebe0a..cddd78aa1 100644 --- a/src/costrict/provider/index.ts +++ b/src/costrict/provider/index.ts @@ -33,6 +33,7 @@ import { resolveCoStrictModel } from './modelMapping.js' import { getCoStrictBaseURL } from './auth.js' import { loadCoStrictCredentials } from './credentials.js' import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js' +import { fetchCoStrictModels } from './models.js' /** * CoStrict 查询路径 @@ -57,10 +58,28 @@ export async function* queryModelCoStrict( const baseUrl = getCoStrictBaseURL(creds?.base_url) const chatBaseURL = `${baseUrl}/chat-rag/api/v1` - // 3. 规范化消息 + // 3. 从模型列表获取 maxTokens 相关参数 + let maxTokensParamKey: string = 'max_tokens' + let maxTokensValue: number | undefined + if (creds?.access_token) { + try { + const modelList = await fetchCoStrictModels(baseUrl, creds.access_token) + const modelInfo = modelList.find(m => m.id === costrictModel) + if (modelInfo) { + maxTokensParamKey = modelInfo.maxTokensKey || 'max_tokens' + if (modelInfo.maxTokens != null) { + maxTokensValue = modelInfo.maxTokens + } + } + } catch { + // 获取模型列表失败,使用默认值 + } + } + + // 4. 规范化消息 const messagesForAPI = normalizeMessagesForAPI(messages, tools) - // 4. 构建工具 schema + // 5. 构建工具 schema const toolSchemas = await Promise.all( tools.map(tool => toolToAPISchema(tool, { @@ -81,7 +100,7 @@ export async function* queryModelCoStrict( }, ) - // 5. 转换为 OpenAI 格式 + // 6. 转换为 OpenAI 格式 // 根据模型名称自动检测是否启用thinking模式 const enableThinking = isOpenAIThinkingEnabled(costrictModel) const openaiMessages = anthropicMessagesToOpenAI( @@ -92,7 +111,7 @@ export async function* queryModelCoStrict( const openaiTools = anthropicToolsToOpenAI(standardTools) const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice) - // 6. 创建专用的 CoStrict OpenAI 客户端(不缓存,每次使用新的 fetch) + // 7. 创建专用的 CoStrict OpenAI 客户端(不缓存,每次使用新的 fetch) const costrictFetch = createCoStrictFetch() const client = new OpenAI({ apiKey: 'costrict-managed', // 实际 token 由 createCoStrictFetch 注入 @@ -111,28 +130,32 @@ export async function* queryModelCoStrict( `[CoStrict] model=${costrictModel}, baseURL=${chatBaseURL}, messages=${openaiMessages.length}, tools=${openaiTools.length}`, ) - // 7. 调用 API(流式) + // 8. 调用 API(流式) + const requestBody: Record = { + model: costrictModel, + messages: openaiMessages, + ...(openaiTools.length > 0 && { + tools: openaiTools, + ...(openaiToolChoice && { + tool_choice: + openaiToolChoice as OpenAI.Chat.Completions.ChatCompletionToolChoiceOption, + }), + }), + stream: true, + stream_options: { include_usage: true }, + ...(options.temperatureOverride !== undefined && { + temperature: options.temperatureOverride, + }), + ...(maxTokensValue != null && { + [maxTokensParamKey]: maxTokensValue, + }), + } const stream = await client.chat.completions.create( - { - model: costrictModel, - messages: openaiMessages, - ...(openaiTools.length > 0 && { - tools: openaiTools, - ...(openaiToolChoice && { - tool_choice: - openaiToolChoice as OpenAI.Chat.Completions.ChatCompletionToolChoiceOption, - }), - }), - stream: true, - stream_options: { include_usage: true }, - ...(options.temperatureOverride !== undefined && { - temperature: options.temperatureOverride, - }), - }, + requestBody as unknown as OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming, { signal }, ) - // 8. 转换流并 yield 事件 + // 9. 转换流并 yield 事件 const adaptedStream = adaptOpenAIStreamToAnthropic(stream, costrictModel) const contentBlocks: Record = {} diff --git a/src/costrict/provider/models.ts b/src/costrict/provider/models.ts index 16743ba5d..0cae89621 100644 --- a/src/costrict/provider/models.ts +++ b/src/costrict/provider/models.ts @@ -12,6 +12,7 @@ export interface CoStrictModel { supportsImages?: boolean contextWindow?: number maxTokens?: number + maxTokensKey?: string creditConsumption?: number creditDiscount?: number [key: string]: any