diff --git a/src/renderer/components/ArtifactPreview/office/__tests__/WordPreviewPanel.test.tsx b/src/renderer/components/ArtifactPreview/office/__tests__/WordPreviewPanel.test.tsx
index 55a35cddc68..39af2448cf4 100644
--- a/src/renderer/components/ArtifactPreview/office/__tests__/WordPreviewPanel.test.tsx
+++ b/src/renderer/components/ArtifactPreview/office/__tests__/WordPreviewPanel.test.tsx
@@ -211,6 +211,7 @@ describe('WordPreviewPanel', () => {
render()
await waitFor(() => expect(screen.getByTestId('docx-preview-page-indicator')).toHaveTextContent('1 / 2'))
+ await waitFor(() => expect(mocks.intersectionObserverInstances.at(-1)).toBeDefined())
const observer = mocks.intersectionObserverInstances.at(-1)
expect(observer).toBeDefined()
diff --git a/src/renderer/hooks/agent/__tests__/useAgentModelFilter.test.ts b/src/renderer/hooks/agent/__tests__/useAgentModelFilter.test.ts
index 7714aec549d..7b11724cc0f 100644
--- a/src/renderer/hooks/agent/__tests__/useAgentModelFilter.test.ts
+++ b/src/renderer/hooks/agent/__tests__/useAgentModelFilter.test.ts
@@ -43,6 +43,12 @@ describe('useAgentModelFilter', () => {
id: 'vertex',
defaultChatEndpoint: 'google-generate-content',
authType: 'iam-gcp'
+ },
+ {
+ id: 'cherryai',
+ presetProviderId: 'cherryai',
+ defaultChatEndpoint: 'openai-chat-completions',
+ authType: 'api-key'
}
]
})
@@ -63,6 +69,14 @@ describe('useAgentModelFilter', () => {
expect(result.current({ ...model(), providerId: 'google-custom', id: 'google-custom::gemini-2.5-pro' })).toBe(false)
})
+ it('filters the managed CherryAI default model for Claude Code agents', () => {
+ const { result } = renderHook(() => useAgentModelFilter('claude-code'))
+
+ expect(
+ result.current({ ...model(), providerId: 'cherryai', id: 'cherryai::qwen', apiModelId: 'qwen', name: 'Qwen' })
+ ).toBe(false)
+ })
+
it('marks its predicate as an agent picker so selectors surface agent-only providers', () => {
const { result } = renderHook(() => useAgentModelFilter('claude-code'))
diff --git a/src/renderer/hooks/agent/useAgentModelFilter.ts b/src/renderer/hooks/agent/useAgentModelFilter.ts
index 4df7ac7ee25..bab1597b85e 100644
--- a/src/renderer/hooks/agent/useAgentModelFilter.ts
+++ b/src/renderer/hooks/agent/useAgentModelFilter.ts
@@ -13,8 +13,7 @@
import { useProviders } from '@renderer/hooks/useProvider'
import type { AgentType } from '@shared/data/types/agent'
import type { Model } from '@shared/data/types/model'
-import { isNonChatModel } from '@shared/utils/model'
-import { isGeminiProvider } from '@shared/utils/provider'
+import { isAgentRuntimeSupportedModel, isNonChatModel } from '@shared/utils/model'
import { useMemo } from 'react'
const baseAgentFilter = (model: Model): boolean => !isNonChatModel(model)
@@ -40,25 +39,17 @@ export function modelFilterIncludesAgentOnlyProviders(filter?: (model: Model) =>
export function useAgentModelFilter(agentType: AgentType | undefined): (model: Model) => boolean {
const { providers } = useProviders()
- const geminiProviderIds = useMemo(() => {
- const ids = new Set()
- for (const provider of providers) {
- if (isGeminiProvider(provider)) {
- ids.add(provider.id)
- }
- }
- return ids
- }, [providers])
+ const providersById = useMemo(() => new Map(providers.map((provider) => [provider.id, provider])), [providers])
return useMemo(() => {
const predicate: AgentModelFilter = (model: Model) => {
if (!baseAgentFilter(model)) return false
if (agentType === 'claude-code') {
- return !geminiProviderIds.has(model.providerId)
+ return isAgentRuntimeSupportedModel(model, providersById.get(model.providerId))
}
return true
}
predicate[AGENT_ONLY_FILTER] = true
return predicate
- }, [agentType, geminiProviderIds])
+ }, [agentType, providersById])
}
diff --git a/src/renderer/i18n/locales/en-us.json b/src/renderer/i18n/locales/en-us.json
index 735d137482c..878a5b1138b 100644
--- a/src/renderer/i18n/locales/en-us.json
+++ b/src/renderer/i18n/locales/en-us.json
@@ -8,7 +8,7 @@
},
"model": {
"supported_providers": "Supported Providers",
- "tooltip": "Currently, only models that support Anthropic endpoints are available for the Agent feature.",
+ "tooltip": "Most chat models are available for Agents. Gemini providers are not supported yet.",
"view_providers": "View supported providers"
},
"title": "Add Agent",
diff --git a/src/renderer/i18n/locales/zh-cn.json b/src/renderer/i18n/locales/zh-cn.json
index 1175bc96d61..df36879b7de 100644
--- a/src/renderer/i18n/locales/zh-cn.json
+++ b/src/renderer/i18n/locales/zh-cn.json
@@ -8,7 +8,7 @@
},
"model": {
"supported_providers": "支持的服务商",
- "tooltip": "目前,只有支持 Anthropic 端点的模型可用于智能体功能。",
+ "tooltip": "目前大多数聊天模型可用于智能体,暂不支持 Gemini 服务商。",
"view_providers": "查看支持的服务商"
},
"title": "添加智能体",
diff --git a/src/renderer/i18n/locales/zh-tw.json b/src/renderer/i18n/locales/zh-tw.json
index dce9ffc6cb5..b19819ab544 100644
--- a/src/renderer/i18n/locales/zh-tw.json
+++ b/src/renderer/i18n/locales/zh-tw.json
@@ -8,7 +8,7 @@
},
"model": {
"supported_providers": "支援的服務商",
- "tooltip": "目前,僅支援 Anthropic 端點的模型可供 Agent 功能使用。",
+ "tooltip": "目前大多數聊天模型可供 Agent 使用,暫不支援 Gemini 服務商。",
"view_providers": "檢視支援的服務商"
},
"title": "新增 Agent",
diff --git a/src/renderer/pages/settings/ProviderSettings/ProviderList/ProviderList.tsx b/src/renderer/pages/settings/ProviderSettings/ProviderList/ProviderList.tsx
index 31733f0b7d4..fb82f9a8c13 100644
--- a/src/renderer/pages/settings/ProviderSettings/ProviderList/ProviderList.tsx
+++ b/src/renderer/pages/settings/ProviderSettings/ProviderList/ProviderList.tsx
@@ -8,7 +8,7 @@ import {
matchKeywordsInProvider
} from '@renderer/pages/settings/ProviderSettings/utils/providerDisplay'
import type { Provider } from '@shared/data/types/provider'
-import { canManageProvider, isAnthropicSupportedProvider } from '@shared/utils/provider'
+import { canManageProvider, isAgentSupportedProvider } from '@shared/utils/provider'
import { Plus } from 'lucide-react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -112,7 +112,7 @@ export default function ProviderList({ selectedProviderId, filterModeHint, onSel
if (filterMode === 'disabled' && provider.isEnabled) {
return false
}
- if (filterMode === 'agent' && !isAnthropicSupportedProvider(provider)) {
+ if (filterMode === 'agent' && !isAgentSupportedProvider(provider)) {
return false
}
const keywords = searchText.toLowerCase().split(/\s+/).filter(Boolean)
diff --git a/src/renderer/pages/settings/ProviderSettings/ProviderList/providerFilterMode.ts b/src/renderer/pages/settings/ProviderSettings/ProviderList/providerFilterMode.ts
index 7a4568a931b..d8524384a09 100644
--- a/src/renderer/pages/settings/ProviderSettings/ProviderList/providerFilterMode.ts
+++ b/src/renderer/pages/settings/ProviderSettings/ProviderList/providerFilterMode.ts
@@ -5,7 +5,7 @@
* - `enabled`: only `isEnabled === true`
* - `disabled`: only `isEnabled === false`
* - `all` (default): every provider
- * - `agent`: only providers that speak the Anthropic protocol (orthogonal to
- * the enabled/disabled axis)
+ * - `agent`: only providers supported by the agent runtime (orthogonal to the
+ * enabled/disabled axis)
*/
export type ProviderFilterMode = 'enabled' | 'disabled' | 'all' | 'agent'
diff --git a/src/renderer/pages/settings/ProviderSettings/__tests__/ProviderList.test.tsx b/src/renderer/pages/settings/ProviderSettings/__tests__/ProviderList.test.tsx
index 3aa746c99dd..53f83e0bc49 100644
--- a/src/renderer/pages/settings/ProviderSettings/__tests__/ProviderList.test.tsx
+++ b/src/renderer/pages/settings/ProviderSettings/__tests__/ProviderList.test.tsx
@@ -314,17 +314,33 @@ describe('ProviderList', () => {
})
})
- it('applies an external filter hint without making the page own list filter state', () => {
+ it('applies the agent filter hint without hiding gateway-routable providers', () => {
const onSelectProvider = vi.fn()
+ useProvidersMock.mockReturnValue({
+ providers: [
+ ...providers,
+ {
+ id: 'gemini',
+ name: 'Gemini',
+ presetProviderId: 'gemini',
+ defaultChatEndpoint: ENDPOINT_TYPE.GOOGLE_GENERATE_CONTENT,
+ authType: 'api-key',
+ isEnabled: true
+ }
+ ],
+ createProvider: vi.fn()
+ })
const { rerender } = render()
expect(screen.getByText('OpenAI')).toBeInTheDocument()
expect(screen.getByText('Anthropic')).toBeInTheDocument()
+ expect(screen.getByText('Gemini')).toBeInTheDocument()
rerender()
- expect(screen.queryByText('OpenAI')).not.toBeInTheDocument()
+ expect(screen.getByText('OpenAI')).toBeInTheDocument()
expect(screen.getByText('Anthropic')).toBeInTheDocument()
+ expect(screen.queryByText('Gemini')).not.toBeInTheDocument()
const filterButton = screen.getByRole('button', { name: '筛选服务商' })
expect(filterButton).not.toHaveClass('bg-primary/10')
expect(filterButton.querySelector('svg')).toHaveClass('text-primary!')
diff --git a/src/shared/utils/model.ts b/src/shared/utils/model.ts
index c972dddee47..20cfd93ec19 100644
--- a/src/shared/utils/model.ts
+++ b/src/shared/utils/model.ts
@@ -12,9 +12,12 @@
*/
import { MODALITY, VENDOR_PATTERNS } from '@cherrystudio/provider-registry'
-import { CHERRYAI_PROVIDER_ID } from '@shared/data/presets/cherryai'
+import { CHERRYAI_PROVIDER_ID, isManagedCherryAiDefaultModel } from '@shared/data/presets/cherryai'
import type { Model, RuntimeReasoning, ThinkingTokenLimits } from '@shared/data/types/model'
import { MODEL_CAPABILITY, parseUniqueModelId } from '@shared/data/types/model'
+import type { Provider } from '@shared/data/types/provider'
+
+import { isAgentSupportedProvider } from './provider'
/** Check if model has reasoning capability */
export const isReasoningModel = (model: Model): boolean =>
@@ -94,6 +97,12 @@ export const isNonChatModel = (model: Model): boolean =>
isTextToSpeechModel(model) ||
isSpeechToTextModel(model)
+export const isAgentRuntimeSupportedModel = (model: Model, provider?: Provider): boolean => {
+ if (isNonChatModel(model)) return false
+ if (provider && !isAgentSupportedProvider(provider)) return false
+ return !isManagedCherryAiDefaultModel(model.providerId, getRawModelId(model))
+}
+
// ---------------------------------------------------------------------------
// Reasoning configuration
// ---------------------------------------------------------------------------
diff --git a/src/shared/utils/provider.ts b/src/shared/utils/provider.ts
index 30d04bdc822..b62415b9768 100644
--- a/src/shared/utils/provider.ts
+++ b/src/shared/utils/provider.ts
@@ -145,6 +145,10 @@ export function isAnthropicSupportedProvider(provider: Provider): boolean {
return getProviderHostTopology(provider).hasAnthropicEndpoint
}
+export function isAgentSupportedProvider(provider: Provider): boolean {
+ return !isGeminiProvider(provider)
+}
+
export function isSupportUrlContextProvider(provider: Provider): boolean {
return (
isGeminiProvider(provider) ||