feat(costrict): 根据模型列表动态设置 max_tokens 参数

从 /ai-gateway/api/v1/models 返回的模型信息中读取 maxTokensKey
和 maxTokens 字段,发送请求时动态注入对应参数名和参数值。
maxTokensKey 缺失时默认使用 max_tokens,maxTokens 无值时不传该参数。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Askhz 2026-05-11 21:23:28 +08:00
parent d9bc165378
commit 1970c698ea
2 changed files with 46 additions and 22 deletions

View File

@ -33,6 +33,7 @@ import { resolveCoStrictModel } from './modelMapping.js'
import { getCoStrictBaseURL } from './auth.js' import { getCoStrictBaseURL } from './auth.js'
import { loadCoStrictCredentials } from './credentials.js' import { loadCoStrictCredentials } from './credentials.js'
import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js' import { isOpenAIThinkingEnabled } from '../../services/api/openai/requestBody.js'
import { fetchCoStrictModels } from './models.js'
/** /**
* CoStrict * CoStrict
@ -57,10 +58,28 @@ export async function* queryModelCoStrict(
const baseUrl = getCoStrictBaseURL(creds?.base_url) const baseUrl = getCoStrictBaseURL(creds?.base_url)
const chatBaseURL = `${baseUrl}/chat-rag/api/v1` 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) const messagesForAPI = normalizeMessagesForAPI(messages, tools)
// 4. 构建工具 schema // 5. 构建工具 schema
const toolSchemas = await Promise.all( const toolSchemas = await Promise.all(
tools.map(tool => tools.map(tool =>
toolToAPISchema(tool, { toolToAPISchema(tool, {
@ -81,7 +100,7 @@ export async function* queryModelCoStrict(
}, },
) )
// 5. 转换为 OpenAI 格式 // 6. 转换为 OpenAI 格式
// 根据模型名称自动检测是否启用thinking模式 // 根据模型名称自动检测是否启用thinking模式
const enableThinking = isOpenAIThinkingEnabled(costrictModel) const enableThinking = isOpenAIThinkingEnabled(costrictModel)
const openaiMessages = anthropicMessagesToOpenAI( const openaiMessages = anthropicMessagesToOpenAI(
@ -92,7 +111,7 @@ export async function* queryModelCoStrict(
const openaiTools = anthropicToolsToOpenAI(standardTools) const openaiTools = anthropicToolsToOpenAI(standardTools)
const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice) const openaiToolChoice = anthropicToolChoiceToOpenAI(options.toolChoice)
// 6. 创建专用的 CoStrict OpenAI 客户端(不缓存,每次使用新的 fetch // 7. 创建专用的 CoStrict OpenAI 客户端(不缓存,每次使用新的 fetch
const costrictFetch = createCoStrictFetch() const costrictFetch = createCoStrictFetch()
const client = new OpenAI({ const client = new OpenAI({
apiKey: 'costrict-managed', // 实际 token 由 createCoStrictFetch 注入 apiKey: 'costrict-managed', // 实际 token 由 createCoStrictFetch 注入
@ -111,28 +130,32 @@ export async function* queryModelCoStrict(
`[CoStrict] model=${costrictModel}, baseURL=${chatBaseURL}, messages=${openaiMessages.length}, tools=${openaiTools.length}`, `[CoStrict] model=${costrictModel}, baseURL=${chatBaseURL}, messages=${openaiMessages.length}, tools=${openaiTools.length}`,
) )
// 7. 调用 API流式 // 8. 调用 API流式
const requestBody: Record<string, unknown> = {
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( const stream = await client.chat.completions.create(
{ requestBody as unknown as OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming,
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,
}),
},
{ signal }, { signal },
) )
// 8. 转换流并 yield 事件 // 9. 转换流并 yield 事件
const adaptedStream = adaptOpenAIStreamToAnthropic(stream, costrictModel) const adaptedStream = adaptOpenAIStreamToAnthropic(stream, costrictModel)
const contentBlocks: Record<number, any> = {} const contentBlocks: Record<number, any> = {}

View File

@ -12,6 +12,7 @@ export interface CoStrictModel {
supportsImages?: boolean supportsImages?: boolean
contextWindow?: number contextWindow?: number
maxTokens?: number maxTokens?: number
maxTokensKey?: string
creditConsumption?: number creditConsumption?: number
creditDiscount?: number creditDiscount?: number
[key: string]: any [key: string]: any