diff --git a/src/renderer/src/aiCore/provider/__tests__/providerConfig.test.ts b/src/renderer/src/aiCore/provider/__tests__/providerConfig.test.ts index cc5f20c63e..39786231e6 100644 --- a/src/renderer/src/aiCore/provider/__tests__/providerConfig.test.ts +++ b/src/renderer/src/aiCore/provider/__tests__/providerConfig.test.ts @@ -39,6 +39,7 @@ vi.mock('@renderer/config/providers', async (importOriginal) => { return { ...actual, isCherryAIProvider: vi.fn(), + isPerplexityProvider: vi.fn(), isAnthropicProvider: vi.fn(() => false), isAzureOpenAIProvider: vi.fn(() => false), isGeminiProvider: vi.fn(() => false), @@ -52,7 +53,7 @@ vi.mock('@renderer/hooks/useVertexAI', () => ({ createVertexProvider: vi.fn() })) -import { isCherryAIProvider } from '@renderer/config/providers' +import { isCherryAIProvider, isPerplexityProvider } from '@renderer/config/providers' import { getProviderByModel } from '@renderer/services/AssistantService' import type { Model, Provider } from '@renderer/types' import { formatApiHost } from '@renderer/utils/api' @@ -97,6 +98,16 @@ const createCherryAIProvider = (): Provider => ({ isSystem: false }) +const createPerplexityProvider = (): Provider => ({ + id: 'perplexity', + type: 'openai', + name: 'Perplexity', + apiKey: 'test-key', + apiHost: 'https://api.perplexity.ai', + models: [], + isSystem: false +}) + describe('Copilot responses routing', () => { beforeEach(() => { ;(globalThis as any).window = { @@ -195,3 +206,70 @@ describe('CherryAI provider configuration', () => { expect(actualProvider.apiHost).toBe('') }) }) + +describe('Perplexity provider configuration', () => { + beforeEach(() => { + ;(globalThis as any).window = { + ...(globalThis as any).window, + keyv: createWindowKeyv() + } + vi.clearAllMocks() + }) + + it('formats Perplexity provider apiHost with false parameter', () => { + const provider = createPerplexityProvider() + const model = createModel('sonar', 'Sonar', 'perplexity') + + // Mock the functions to simulate Perplexity provider detection + vi.mocked(isCherryAIProvider).mockReturnValue(false) + vi.mocked(isPerplexityProvider).mockReturnValue(true) + vi.mocked(getProviderByModel).mockReturnValue(provider) + + // Call getActualProvider which should trigger formatProviderApiHost + const actualProvider = getActualProvider(model) + + // Verify that formatApiHost was called with false as the second parameter + expect(formatApiHost).toHaveBeenCalledWith('https://api.perplexity.ai', false) + expect(actualProvider.apiHost).toBe('https://api.perplexity.ai') + }) + + it('does not format non-Perplexity provider with false parameter', () => { + const provider = { + id: 'openai', + type: 'openai', + name: 'OpenAI', + apiKey: 'test-key', + apiHost: 'https://api.openai.com', + models: [], + isSystem: false + } as Provider + const model = createModel('gpt-4', 'GPT-4', 'openai') + + // Mock the functions to simulate non-Perplexity provider + vi.mocked(isCherryAIProvider).mockReturnValue(false) + vi.mocked(isPerplexityProvider).mockReturnValue(false) + vi.mocked(getProviderByModel).mockReturnValue(provider) + + // Call getActualProvider + const actualProvider = getActualProvider(model) + + // Verify that formatApiHost was called with default parameters (true) + expect(formatApiHost).toHaveBeenCalledWith('https://api.openai.com') + expect(actualProvider.apiHost).toBe('https://api.openai.com/v1') + }) + + it('handles Perplexity provider with empty apiHost', () => { + const provider = createPerplexityProvider() + provider.apiHost = '' + const model = createModel('sonar', 'Sonar', 'perplexity') + + vi.mocked(isCherryAIProvider).mockReturnValue(false) + vi.mocked(isPerplexityProvider).mockReturnValue(true) + vi.mocked(getProviderByModel).mockReturnValue(provider) + + const actualProvider = getActualProvider(model) + + expect(formatApiHost).toHaveBeenCalledWith('', false) + expect(actualProvider.apiHost).toBe('') + }) +}) diff --git a/src/renderer/src/aiCore/provider/providerConfig.ts b/src/renderer/src/aiCore/provider/providerConfig.ts index 4669d4c851..7f279a3898 100644 --- a/src/renderer/src/aiCore/provider/providerConfig.ts +++ b/src/renderer/src/aiCore/provider/providerConfig.ts @@ -11,7 +11,8 @@ import { isAzureOpenAIProvider, isCherryAIProvider, isGeminiProvider, - isNewApiProvider + isNewApiProvider, + isPerplexityProvider } from '@renderer/config/providers' import { getAwsBedrockAccessKeyId, @@ -103,6 +104,8 @@ function formatProviderApiHost(provider: Provider): Provider { formatted.apiHost = formatVertexApiHost(formatted) } else if (isCherryAIProvider(formatted)) { formatted.apiHost = formatApiHost(formatted.apiHost, false) + } else if (isPerplexityProvider(formatted)) { + formatted.apiHost = formatApiHost(formatted.apiHost, false) } else { formatted.apiHost = formatApiHost(formatted.apiHost) } diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index 5fbae73dbf..50b0dbaece 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -1490,6 +1490,10 @@ export function isCherryAIProvider(provider: Provider): boolean { return provider.id === 'cherryai' } +export function isPerplexityProvider(provider: Provider): boolean { + return provider.id === 'perplexity' +} + /** * 判断是否为 OpenAI 兼容的提供商 * @param {Provider} provider 提供商对象 @@ -1515,7 +1519,7 @@ export function isGeminiProvider(provider: Provider): boolean { return provider.type === 'gemini' } -const NOT_SUPPORT_API_VERSION_PROVIDERS = ['github', 'copilot'] as const satisfies SystemProviderId[] +const NOT_SUPPORT_API_VERSION_PROVIDERS = ['github', 'copilot', 'perplexity'] as const satisfies SystemProviderId[] export const isSupportAPIVersionProvider = (provider: Provider) => { if (isSystemProvider(provider)) {