feat: sideQuery 支持第三方 provider 路由 (OpenAI/Grok/Gemini)
- 新增 getProviderPrimaryModel() 从环境变量解析 provider 主模型 - getDefaultOpus/Sonnet/HaikuModel 在第三方 provider 下回退到用户配置的主模型 - sideQuery 根据 provider 类型分发到对应的 API 适配器 - 新增 sideQueryViaOpenAICompatible (OpenAI + Grok) 和 sideQueryViaGemini 适配函数 - 避免 sideQuery 后台任务在配置第三方端点时仍请求 Anthropic API
This commit is contained in:
parent
b9fcd991cc
commit
9ae258f917
|
|
@ -123,6 +123,19 @@ export function getBestModel(): ModelName {
|
||||||
return getDefaultOpusModel()
|
return getDefaultOpusModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the provider's primary model from its env var (e.g. OPENAI_MODEL).
|
||||||
|
* Returns undefined for providers that don't have a primary-model env var
|
||||||
|
* (Bedrock, Vertex, Foundry, firstParty).
|
||||||
|
*/
|
||||||
|
function getProviderPrimaryModel(): ModelName | undefined {
|
||||||
|
const provider = getAPIProvider()
|
||||||
|
if (provider === 'openai') return process.env.OPENAI_MODEL
|
||||||
|
if (provider === 'gemini') return process.env.GEMINI_MODEL
|
||||||
|
if (provider === 'grok') return process.env.GROK_MODEL
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
// @[MODEL LAUNCH]: Update the default Opus model (3P providers may lag so keep defaults unchanged).
|
// @[MODEL LAUNCH]: Update the default Opus model (3P providers may lag so keep defaults unchanged).
|
||||||
export function getDefaultOpusModel(): ModelName {
|
export function getDefaultOpusModel(): ModelName {
|
||||||
const provider = getAPIProvider()
|
const provider = getAPIProvider()
|
||||||
|
|
@ -138,10 +151,15 @@ export function getDefaultOpusModel(): ModelName {
|
||||||
if (process.env.ANTHROPIC_DEFAULT_OPUS_MODEL) {
|
if (process.env.ANTHROPIC_DEFAULT_OPUS_MODEL) {
|
||||||
return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
|
return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
|
||||||
}
|
}
|
||||||
// Fall back to user's configured model — custom providers may not
|
// 3P providers: if user set a primary model (e.g. OPENAI_MODEL=glm-5.1),
|
||||||
// recognize hardcoded Anthropic model IDs.
|
// fall back to it instead of a hardcoded Anthropic model. This prevents
|
||||||
// Skip if the user setting is a model alias (e.g. "opus", "opus[1m]") to
|
// sideQuery / background tasks from sending requests to Anthropic's API
|
||||||
// avoid infinite recursion: parseUserSpecifiedModel(alias) → getDefaultOpusModel().
|
// when the user configured a third-party provider.
|
||||||
|
const primaryModel = getProviderPrimaryModel()
|
||||||
|
if (primaryModel) return primaryModel
|
||||||
|
// Fall back to user's configured model — custom providers may not recognize
|
||||||
|
// hardcoded Anthropic model IDs. Skip aliases to avoid infinite recursion:
|
||||||
|
// parseUserSpecifiedModel(alias) → getDefaultOpusModel().
|
||||||
const userSpecifiedOpus = getUserSpecifiedModelSetting()
|
const userSpecifiedOpus = getUserSpecifiedModelSetting()
|
||||||
if (userSpecifiedOpus && !isAliasOrAliasWithSuffix(userSpecifiedOpus)) {
|
if (userSpecifiedOpus && !isAliasOrAliasWithSuffix(userSpecifiedOpus)) {
|
||||||
return parseUserSpecifiedModel(userSpecifiedOpus)
|
return parseUserSpecifiedModel(userSpecifiedOpus)
|
||||||
|
|
@ -170,9 +188,12 @@ export function getDefaultSonnetModel(): ModelName {
|
||||||
if (process.env.ANTHROPIC_DEFAULT_SONNET_MODEL) {
|
if (process.env.ANTHROPIC_DEFAULT_SONNET_MODEL) {
|
||||||
return process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
|
return process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
|
||||||
}
|
}
|
||||||
// Fall back to user's configured model (ANTHROPIC_MODEL / settings) —
|
// 3P providers: fall back to user's primary model instead of a hardcoded
|
||||||
// custom providers (proxies, national clouds) may not recognize the
|
// Anthropic model name. Prevents background API calls from being routed to
|
||||||
// hardcoded Anthropic model IDs.
|
// Anthropic when the user configured a third-party endpoint.
|
||||||
|
const primaryModel = getProviderPrimaryModel()
|
||||||
|
if (primaryModel) return primaryModel
|
||||||
|
// Fall back to user's configured model (ANTHROPIC_MODEL / settings).
|
||||||
// Skip if the user setting is a model alias to avoid infinite recursion.
|
// Skip if the user setting is a model alias to avoid infinite recursion.
|
||||||
const userSpecified = getUserSpecifiedModelSetting()
|
const userSpecified = getUserSpecifiedModelSetting()
|
||||||
if (userSpecified && !isAliasOrAliasWithSuffix(userSpecified)) {
|
if (userSpecified && !isAliasOrAliasWithSuffix(userSpecified)) {
|
||||||
|
|
@ -200,9 +221,12 @@ export function getDefaultHaikuModel(): ModelName {
|
||||||
if (process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL) {
|
if (process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL) {
|
||||||
return process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
return process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
||||||
}
|
}
|
||||||
// Fall back to user's configured model — custom providers may not
|
// 3P providers: fall back to user's primary model instead of a hardcoded
|
||||||
// recognize hardcoded Anthropic model IDs.
|
// Anthropic model name.
|
||||||
// Skip if the user setting is a model alias to avoid infinite recursion.
|
const primaryModel = getProviderPrimaryModel()
|
||||||
|
if (primaryModel) return primaryModel
|
||||||
|
// Fall back to user's configured model — custom providers may not recognize
|
||||||
|
// hardcoded Anthropic model IDs. Skip aliases to avoid infinite recursion.
|
||||||
const userSpecifiedHaiku = getUserSpecifiedModelSetting()
|
const userSpecifiedHaiku = getUserSpecifiedModelSetting()
|
||||||
if (userSpecifiedHaiku && !isAliasOrAliasWithSuffix(userSpecifiedHaiku)) {
|
if (userSpecifiedHaiku && !isAliasOrAliasWithSuffix(userSpecifiedHaiku)) {
|
||||||
return parseUserSpecifiedModel(userSpecifiedHaiku)
|
return parseUserSpecifiedModel(userSpecifiedHaiku)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ import { normalizeModelStringForAPI } from './model/model.js'
|
||||||
import { getAPIProvider } from './model/providers.js'
|
import { getAPIProvider } from './model/providers.js'
|
||||||
import { getOpenAIClient } from '../services/api/openai/client.js'
|
import { getOpenAIClient } from '../services/api/openai/client.js'
|
||||||
import { getGrokClient } from '../services/api/grok/client.js'
|
import { getGrokClient } from '../services/api/grok/client.js'
|
||||||
import { anthropicMessagesToOpenAI } from '../services/api/openai/convertMessages.js'
|
|
||||||
import { resolveOpenAIModel } from '../services/api/openai/modelMapping.js'
|
import { resolveOpenAIModel } from '../services/api/openai/modelMapping.js'
|
||||||
import { resolveGrokModel } from '../services/api/grok/modelMapping.js'
|
import { resolveGrokModel } from '../services/api/grok/modelMapping.js'
|
||||||
import {
|
import {
|
||||||
|
|
@ -222,10 +221,9 @@ async function sideQueryViaOpenAICompatible(
|
||||||
if (openaiToolChoice) requestParams.tool_choice = openaiToolChoice
|
if (openaiToolChoice) requestParams.tool_choice = openaiToolChoice
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await client.chat.completions.create(
|
const response = await client.chat.completions.create(requestParams as any, {
|
||||||
requestParams as any,
|
signal,
|
||||||
{ signal },
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const choice = response.choices[0]
|
const choice = response.choices[0]
|
||||||
const message = choice?.message
|
const message = choice?.message
|
||||||
|
|
@ -303,9 +301,14 @@ async function sideQueryViaOpenAICompatible(
|
||||||
* Use this instead of direct client.beta.messages.create() calls to ensure
|
* Use this instead of direct client.beta.messages.create() calls to ensure
|
||||||
* proper OAuth token validation with fingerprint attribution headers.
|
* proper OAuth token validation with fingerprint attribution headers.
|
||||||
*
|
*
|
||||||
* Third-party provider routing (OpenAI, Grok, Gemini) is handled transparently —
|
* This handles:
|
||||||
* when the user configures a third-party provider, sideQuery automatically
|
* - Fingerprint computation for OAuth validation
|
||||||
* routes to the correct API adapter instead of sending requests to Anthropic.
|
* - Attribution header injection
|
||||||
|
* - CLI system prompt prefix
|
||||||
|
* - Proper betas for the model
|
||||||
|
* - API metadata
|
||||||
|
* - Model string normalization (strips [1m] suffix for API)
|
||||||
|
* - Third-party provider routing (OpenAI, Grok, Gemini)
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Permission explainer
|
* // Permission explainer
|
||||||
|
|
@ -603,7 +606,8 @@ async function sideQueryViaGemini(
|
||||||
const body: Record<string, unknown> = {
|
const body: Record<string, unknown> = {
|
||||||
contents,
|
contents,
|
||||||
...(systemInstruction && { systemInstruction }),
|
...(systemInstruction && { systemInstruction }),
|
||||||
...(geminiTools && (geminiTools as unknown[]).length > 0 && { tools: geminiTools }),
|
...(geminiTools &&
|
||||||
|
(geminiTools as unknown[]).length > 0 && { tools: geminiTools }),
|
||||||
...(geminiToolConfig && {
|
...(geminiToolConfig && {
|
||||||
toolConfig: { functionCallingConfig: geminiToolConfig },
|
toolConfig: { functionCallingConfig: geminiToolConfig },
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user