mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-12 00:49:14 +08:00
fix: test
This commit is contained in:
parent
f225fbe3e3
commit
36ed062b84
@ -41,8 +41,3 @@ const SILICON_ANTHROPIC_COMPATIBLE_MODEL_SET = new Set(SILICON_ANTHROPIC_COMPATI
|
||||
export function isSiliconAnthropicCompatibleModel(modelId: string): boolean {
|
||||
return SILICON_ANTHROPIC_COMPATIBLE_MODEL_SET.has(modelId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Silicon provider's Anthropic API host URL.
|
||||
*/
|
||||
export const SILICON_ANTHROPIC_API_HOST = 'https://api.siliconflow.cn'
|
||||
|
||||
@ -6,17 +6,34 @@ import express from 'express'
|
||||
|
||||
import { messagesService } from '../services/messages'
|
||||
import { generateUnifiedMessage, streamUnifiedMessages } from '../services/unified-messages'
|
||||
import { getProviderById, validateModelId } from '../utils'
|
||||
import { getProviderById, isModelAnthropicCompatible, validateModelId } from '../utils'
|
||||
|
||||
/**
|
||||
* Check if provider should use direct Anthropic SDK
|
||||
* Check if a specific model on a provider should use direct Anthropic SDK
|
||||
*
|
||||
* A provider is considered "Anthropic-compatible" if:
|
||||
* A provider+model combination is considered "Anthropic-compatible" if:
|
||||
* 1. It's a native Anthropic provider (type === 'anthropic'), OR
|
||||
* 2. It has anthropicApiHost configured (aggregated providers routing to Anthropic-compatible endpoints)
|
||||
* 2. It has anthropicApiHost configured AND the specific model supports Anthropic API
|
||||
* (for aggregated providers like Silicon, only certain models support Anthropic endpoint)
|
||||
*
|
||||
* @param provider - The provider to check
|
||||
* @param modelId - The model ID to check (without provider prefix)
|
||||
* @returns true if should use direct Anthropic SDK, false for unified SDK
|
||||
*/
|
||||
function shouldUseDirectAnthropic(provider: Provider): boolean {
|
||||
return provider.type === 'anthropic' || !!(provider.anthropicApiHost && provider.anthropicApiHost.trim())
|
||||
function shouldUseDirectAnthropic(provider: Provider, modelId: string): boolean {
|
||||
// Native Anthropic provider - always use direct SDK
|
||||
if (provider.type === 'anthropic') {
|
||||
return true
|
||||
}
|
||||
|
||||
// No anthropicApiHost configured - use unified SDK
|
||||
if (!provider.anthropicApiHost?.trim()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Has anthropicApiHost - check model-level compatibility
|
||||
// For aggregated providers, only specific models support Anthropic API
|
||||
return isModelAnthropicCompatible(provider, modelId)
|
||||
}
|
||||
|
||||
const logger = loggerService.withContext('ApiServerMessagesRoutes')
|
||||
@ -169,11 +186,12 @@ async function handleUnifiedProcessing({
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle message processing - routes to appropriate handler based on provider
|
||||
* Handle message processing - routes to appropriate handler based on provider and model
|
||||
*
|
||||
* Routing logic:
|
||||
* - Providers with anthropicApiHost OR type 'anthropic': Direct Anthropic SDK (no conversion)
|
||||
* - Other providers: Unified AI SDK with Anthropic SSE conversion
|
||||
* - Native Anthropic providers (type === 'anthropic'): Direct Anthropic SDK
|
||||
* - Providers with anthropicApiHost AND model supports Anthropic API: Direct Anthropic SDK
|
||||
* - Other providers/models: Unified AI SDK with Anthropic SSE conversion
|
||||
*/
|
||||
async function handleMessageProcessing({
|
||||
res,
|
||||
@ -181,7 +199,8 @@ async function handleMessageProcessing({
|
||||
request,
|
||||
modelId
|
||||
}: HandleMessageProcessingOptions): Promise<void> {
|
||||
if (shouldUseDirectAnthropic(provider)) {
|
||||
const actualModelId = modelId || request.model
|
||||
if (shouldUseDirectAnthropic(provider, actualModelId)) {
|
||||
return handleDirectAnthropicProcessing({ res, provider, request, modelId })
|
||||
}
|
||||
return handleUnifiedProcessing({ res, provider, request, modelId })
|
||||
|
||||
@ -295,3 +295,32 @@ export const getProviderAnthropicModelChecker = (providerId: string): ((m: Model
|
||||
return () => true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific model is compatible with Anthropic API for a given provider.
|
||||
*
|
||||
* This is used for fine-grained routing decisions at the model level.
|
||||
* For aggregated providers (like Silicon), only certain models support the Anthropic API endpoint.
|
||||
*
|
||||
* @param provider - The provider to check
|
||||
* @param modelId - The model ID to check (without provider prefix)
|
||||
* @returns true if the model supports Anthropic API endpoint
|
||||
*/
|
||||
export function isModelAnthropicCompatible(provider: Provider, modelId: string): boolean {
|
||||
const checker = getProviderAnthropicModelChecker(provider.id)
|
||||
|
||||
const model = provider.models?.find((m) => m.id === modelId)
|
||||
|
||||
if (model) {
|
||||
return checker(model)
|
||||
}
|
||||
|
||||
const minimalModel: Model = {
|
||||
id: modelId,
|
||||
name: modelId,
|
||||
provider: provider.id,
|
||||
group: ''
|
||||
}
|
||||
|
||||
return checker(minimalModel)
|
||||
}
|
||||
|
||||
@ -24,7 +24,17 @@ vi.mock('@renderer/services/AssistantService', () => ({
|
||||
|
||||
vi.mock('@renderer/store', () => ({
|
||||
default: {
|
||||
getState: () => ({ copilot: { defaultHeaders: {} } })
|
||||
getState: () => ({
|
||||
copilot: { defaultHeaders: {} },
|
||||
llm: {
|
||||
settings: {
|
||||
vertexai: {
|
||||
projectId: 'test-project',
|
||||
location: 'us-central1'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
||||
@ -33,7 +43,7 @@ vi.mock('@renderer/utils/api', () => ({
|
||||
if (isSupportedAPIVersion === false) {
|
||||
return host // Return host as-is when isSupportedAPIVersion is false
|
||||
}
|
||||
return `${host}/v1` // Default behavior when isSupportedAPIVersion is true
|
||||
return host ? `${host}/v1` : '' // Default behavior when isSupportedAPIVersion is true
|
||||
}),
|
||||
routeToEndpoint: vi.fn((host) => ({
|
||||
baseURL: host,
|
||||
@ -41,6 +51,20 @@ vi.mock('@renderer/utils/api', () => ({
|
||||
}))
|
||||
}))
|
||||
|
||||
// Also mock @shared/api since formatProviderApiHost uses it directly
|
||||
vi.mock('@shared/api', async (importOriginal) => {
|
||||
const actual = (await importOriginal()) as any
|
||||
return {
|
||||
...actual,
|
||||
formatApiHost: vi.fn((host, isSupportedAPIVersion = true) => {
|
||||
if (isSupportedAPIVersion === false) {
|
||||
return host || '' // Return host as-is when isSupportedAPIVersion is false
|
||||
}
|
||||
return host ? `${host}/v1` : '' // Default behavior when isSupportedAPIVersion is true
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@renderer/utils/provider', async (importOriginal) => {
|
||||
const actual = (await importOriginal()) as any
|
||||
return {
|
||||
@ -73,8 +97,8 @@ vi.mock('@renderer/services/AssistantService', () => ({
|
||||
|
||||
import { getProviderByModel } from '@renderer/services/AssistantService'
|
||||
import type { Model, Provider } from '@renderer/types'
|
||||
import { formatApiHost } from '@renderer/utils/api'
|
||||
import { isCherryAIProvider, isPerplexityProvider } from '@renderer/utils/provider'
|
||||
import { formatApiHost } from '@shared/api'
|
||||
|
||||
import { COPILOT_DEFAULT_HEADERS, COPILOT_EDITOR_VERSION, isCopilotResponsesModel } from '../constants'
|
||||
import { getActualProvider, providerToAiSdkConfig } from '../providerConfig'
|
||||
|
||||
@ -300,18 +300,7 @@ describe('api', () => {
|
||||
})
|
||||
|
||||
it('uses global endpoint when location equals global', () => {
|
||||
getStateMock.mockReturnValueOnce({
|
||||
llm: {
|
||||
settings: {
|
||||
vertexai: {
|
||||
projectId: 'global-project',
|
||||
location: 'global'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
expect(formatVertexApiHost(createVertexProvider(''))).toBe(
|
||||
expect(formatVertexApiHost(createVertexProvider(''), 'global-project', 'global')).toBe(
|
||||
'https://aiplatform.googleapis.com/v1/projects/global-project/locations/global'
|
||||
)
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user