diff --git a/src/renderer/components/Avatar/ModelAvatar.tsx b/src/renderer/components/Avatar/ModelAvatar.tsx index da14278c3d..2ce5255e1c 100644 --- a/src/renderer/components/Avatar/ModelAvatar.tsx +++ b/src/renderer/components/Avatar/ModelAvatar.tsx @@ -1,5 +1,5 @@ import { Avatar, AvatarFallback } from '@cherrystudio/ui' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogo } from '@renderer/utils/model' import { cn } from '@renderer/utils/style' import { first } from 'lodash' import type { FC } from 'react' diff --git a/src/renderer/components/Icons/MiniAppIcon.tsx b/src/renderer/components/Icons/MiniAppIcon.tsx index 1e5bc0cdb8..fa091f9d00 100644 --- a/src/renderer/components/Icons/MiniAppIcon.tsx +++ b/src/renderer/components/Icons/MiniAppIcon.tsx @@ -1,4 +1,4 @@ -import { getMiniAppsLogo } from '@renderer/config/miniApps' +import { getMiniAppsLogo } from '@renderer/components/Icons/miniAppsLogo' import type { MiniApp } from '@shared/data/types/miniApp' import type { FC } from 'react' diff --git a/src/renderer/components/Icons/__tests__/MiniAppIcon.test.tsx b/src/renderer/components/Icons/__tests__/MiniAppIcon.test.tsx index efd1417928..61c400018d 100644 --- a/src/renderer/components/Icons/__tests__/MiniAppIcon.test.tsx +++ b/src/renderer/components/Icons/__tests__/MiniAppIcon.test.tsx @@ -4,25 +4,7 @@ import { describe, expect, it, vi } from 'vitest' import MiniAppIcon from '../MiniAppIcon' -vi.mock('@renderer/config/miniApps', () => ({ - allMiniApps: [ - { - id: 'test-app-1', - name: 'Test App 1', - logo: '/test-logo-1.png', - url: 'https://test1.com', - bordered: true, - background: '#f0f0f0' - }, - { - id: 'test-app-2', - name: 'Test App 2', - logo: '/test-logo-2.png', - url: 'https://test2.com', - bordered: false, - background: undefined - } - ], +vi.mock('@renderer/components/Icons/miniAppsLogo', () => ({ getMiniAppsLogo: (logo: unknown) => { if (logo !== 'compound-logo') return logo const CompoundLogo = ({ diff --git a/src/renderer/config/miniApps.ts b/src/renderer/components/Icons/miniAppsLogo.ts similarity index 76% rename from src/renderer/config/miniApps.ts rename to src/renderer/components/Icons/miniAppsLogo.ts index 7ce662d361..ef07396b03 100644 --- a/src/renderer/config/miniApps.ts +++ b/src/renderer/components/Icons/miniAppsLogo.ts @@ -64,45 +64,6 @@ import { Zhida, Zhipu } from '@cherrystudio/ui/icons' -import { PRESETS_MINI_APPS as SHARED_PRESETS } from '@shared/data/presets/miniApps' - -/** - * Legacy mini-app entity type used by the deprecated Redux slice and config layer. - * The v2 MiniApp entity lives in @shared/data/types/miniApp. - */ -export type MiniAppType = { - id: string - name: string - nameKey?: string - supportedRegions?: string[] - logo?: string - url: string - bordered?: boolean - background?: string - style?: Record - addTime?: string - type?: 'Custom' | 'Default' -} - -// Renderer preset list, derived from the shared source of truth. -// Custom mini apps are no longer file-backed in v2 — they live in the -// `mini_app` SQLite table and are accessed via DataApi. Anything in v1's -// `custom-miniApps.json` reaches v2 only through MiniAppMigrator. -const PRESETS_MINI_APPS: MiniAppType[] = SHARED_PRESETS.map((app) => ({ - id: app.id, - name: app.name, - nameKey: app.nameKey, - url: app.url, - logo: app.logo, - bordered: app.bordered, - background: app.background, - supportedRegions: app.supportedRegions, - style: app.style -})) - -const allMiniApps = PRESETS_MINI_APPS - -export { allMiniApps, PRESETS_MINI_APPS } export function getMiniAppsLogo(LogoId: string | undefined): CompoundIcon | undefined { if (!LogoId) { diff --git a/src/renderer/components/Selector/__tests__/ModelSelector.test.tsx b/src/renderer/components/Selector/__tests__/ModelSelector.test.tsx index dfb474f0e4..3223d994ed 100644 --- a/src/renderer/components/Selector/__tests__/ModelSelector.test.tsx +++ b/src/renderer/components/Selector/__tests__/ModelSelector.test.tsx @@ -1,3 +1,4 @@ +import type * as ModelModule from '@renderer/utils/model' import { type Model, MODEL_CAPABILITY, type UniqueModelId } from '@shared/data/types/model' import type { Provider } from '@shared/data/types/provider' import { fireEvent, render, screen, waitFor } from '@testing-library/react' @@ -59,7 +60,8 @@ vi.mock('@cherrystudio/ui/icons', () => ({ resolveIcon: () => null })) -vi.mock('@renderer/config/models/reasoning', () => ({ +vi.mock('@renderer/utils/model', async (importOriginal) => ({ + ...(await importOriginal()), getModelSupportedReasoningEffortOptions: () => undefined })) diff --git a/src/renderer/components/Selector/__tests__/ModelSelectorDetailCard.test.tsx b/src/renderer/components/Selector/__tests__/ModelSelectorDetailCard.test.tsx index 7321fd68e5..81a3a184b0 100644 --- a/src/renderer/components/Selector/__tests__/ModelSelectorDetailCard.test.tsx +++ b/src/renderer/components/Selector/__tests__/ModelSelectorDetailCard.test.tsx @@ -1,3 +1,4 @@ +import type * as ModelModule from '@renderer/utils/model' import type { Model, UniqueModelId } from '@shared/data/types/model' import type { Provider } from '@shared/data/types/provider' import { render, screen } from '@testing-library/react' @@ -11,7 +12,8 @@ const { mockGetModelSupportedReasoningEffortOptions } = vi.hoisted(() => ({ mockGetModelSupportedReasoningEffortOptions: vi.fn() })) -vi.mock('@renderer/config/models/reasoning', () => ({ +vi.mock('@renderer/utils/model', async (importOriginal) => ({ + ...(await importOriginal()), getModelSupportedReasoningEffortOptions: mockGetModelSupportedReasoningEffortOptions })) diff --git a/src/renderer/components/Selector/model/ModelSelectorDetailCard.tsx b/src/renderer/components/Selector/model/ModelSelectorDetailCard.tsx index 1ebac9ca1c..ecd38dc23a 100644 --- a/src/renderer/components/Selector/model/ModelSelectorDetailCard.tsx +++ b/src/renderer/components/Selector/model/ModelSelectorDetailCard.tsx @@ -1,6 +1,6 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from '@cherrystudio/ui' import { getModelDisplayTags, ModelTag } from '@renderer/components/Tags/Model' -import { getModelSupportedReasoningEffortOptions } from '@renderer/config/models/reasoning' +import { getModelSupportedReasoningEffortOptions } from '@renderer/utils/model' import type { Model } from '@shared/data/types/model' import type { Provider } from '@shared/data/types/provider' import type { TFunction } from 'i18next' diff --git a/src/renderer/components/Selector/model/useModelSelectorData.ts b/src/renderer/components/Selector/model/useModelSelectorData.ts index c34520364b..9e19b36db8 100644 --- a/src/renderer/components/Selector/model/useModelSelectorData.ts +++ b/src/renderer/components/Selector/model/useModelSelectorData.ts @@ -2,7 +2,7 @@ import { modelMatchesDisplayTag } from '@renderer/components/Tags/Model' import { useModels } from '@renderer/hooks/useModel' import { usePins } from '@renderer/hooks/usePins' import { useProviders } from '@renderer/hooks/useProvider' -import { getSearchMatchScore } from '@renderer/utils/modelSearch' +import { getSearchMatchScore } from '@renderer/utils/model' import { CHERRYAI_PROVIDER_ID } from '@shared/data/presets/cherryai' import { isUniqueModelId, type Model, parseUniqueModelId, type UniqueModelId } from '@shared/data/types/model' import type { Provider } from '@shared/data/types/provider' diff --git a/src/renderer/components/app/Sidebar.tsx b/src/renderer/components/app/Sidebar.tsx index 5f73ee9dc0..ed4768bb7c 100644 --- a/src/renderer/components/app/Sidebar.tsx +++ b/src/renderer/components/app/Sidebar.tsx @@ -1,20 +1,16 @@ import { usePersistCache } from '@data/hooks/useCache' import { usePreference } from '@data/hooks/usePreference' +import { SIDEBAR_ICON_COMPONENTS } from '@renderer/components/app/sidebarIcons' import { emitResourceListReveal, type ResourceListRevealSource } from '@renderer/components/chat/resources/resourceListRevealEvents' -import { - getOrderedVisibleSidebarIcons, - getSidebarMenuPath, - resolveSidebarActiveItem, - SIDEBAR_ICON_COMPONENTS -} from '@renderer/config/sidebar' -import { clearTabInstanceMetadata } from '@renderer/config/tabInstanceMetadata' import { useTabs } from '@renderer/hooks/tab' import useAvatar from '@renderer/hooks/useAvatar' import { getSidebarIconLabelKey } from '@renderer/i18n/label' import { getDefaultRouteTitle } from '@renderer/utils/routeTitle' +import { getOrderedVisibleSidebarIcons, getSidebarMenuPath, resolveSidebarActiveItem } from '@renderer/utils/sidebar' +import { clearTabInstanceMetadata } from '@renderer/utils/tabInstanceMetadata' import type { SidebarIcon as SidebarIconType } from '@shared/data/preference/preferenceTypes' import type { Ref } from 'react' import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'react' diff --git a/src/renderer/components/app/__tests__/Sidebar.test.tsx b/src/renderer/components/app/__tests__/Sidebar.test.tsx index 0710fec019..d176e9461b 100644 --- a/src/renderer/components/app/__tests__/Sidebar.test.tsx +++ b/src/renderer/components/app/__tests__/Sidebar.test.tsx @@ -193,7 +193,7 @@ vi.mock('react-i18next', () => ({ }) })) -import { resolveSidebarAppTabEntryUrl } from '@renderer/config/sidebar' +import { resolveSidebarAppTabEntryUrl } from '@renderer/utils/sidebar' import Sidebar from '../Sidebar' diff --git a/src/renderer/components/app/sidebarIcons.tsx b/src/renderer/components/app/sidebarIcons.tsx new file mode 100644 index 0000000000..99cb95be37 --- /dev/null +++ b/src/renderer/components/app/sidebarIcons.tsx @@ -0,0 +1,35 @@ +import { OpenClawSidebarIcon } from '@renderer/components/Icons/SvgIcon' +import type { SidebarMenuItem } from '@renderer/components/Sidebar/types' +import type { SidebarIcon } from '@shared/data/preference/preferenceTypes' +import { + Code, + FileSearch, + Folder, + Languages, + LayoutGrid, + Library, + MessageSquare, + MousePointerClick, + NotepadText, + Palette +} from 'lucide-react' + +/** + * Icon component for each sidebar app. Keyed by the `SidebarIcon` union so the + * compiler enforces full coverage — adding a new sidebar app id without an icon + * here is a type error. Kept in the component layer because the values are React + * components; the navigation data and logic live in `@renderer/utils/sidebar`. + */ +export const SIDEBAR_ICON_COMPONENTS: Record = { + assistants: MessageSquare, + agents: MousePointerClick, + paintings: Palette, + translate: Languages, + store: Library, + mini_app: LayoutGrid, + knowledge: FileSearch, + files: Folder, + code_tools: Code, + notes: NotepadText, + openclaw: OpenClawSidebarIcon +} diff --git a/src/renderer/components/chat/messages/frame/MessageHeader.tsx b/src/renderer/components/chat/messages/frame/MessageHeader.tsx index 9a37064bab..df6a485776 100644 --- a/src/renderer/components/chat/messages/frame/MessageHeader.tsx +++ b/src/renderer/components/chat/messages/frame/MessageHeader.tsx @@ -1,7 +1,7 @@ import { Checkbox, Tooltip } from '@cherrystudio/ui' -import { getModelLogo } from '@renderer/config/models' import { useTheme } from '@renderer/hooks/useTheme' import type { Model } from '@renderer/types/model' +import { getModelLogo } from '@renderer/utils/model' import { firstLetter, removeLeadingEmoji } from '@renderer/utils/naming' import dayjs from 'dayjs' import { Sparkle } from 'lucide-react' diff --git a/src/renderer/components/chat/messages/frame/MessageMenuBar.tsx b/src/renderer/components/chat/messages/frame/MessageMenuBar.tsx index 807db35dc7..a3c469f603 100644 --- a/src/renderer/components/chat/messages/frame/MessageMenuBar.tsx +++ b/src/renderer/components/chat/messages/frame/MessageMenuBar.tsx @@ -1,5 +1,8 @@ -import type { MessageMenuBarScope } from '@renderer/config/registry/messageMenuBar' -import { DEFAULT_MESSAGE_MENUBAR_SCOPE, getMessageMenuBarConfig } from '@renderer/config/registry/messageMenuBar' +import type { MessageMenuBarScope } from '@renderer/components/chat/messages/frame/messageMenuBarConfig' +import { + DEFAULT_MESSAGE_MENUBAR_SCOPE, + getMessageMenuBarConfig +} from '@renderer/components/chat/messages/frame/messageMenuBarConfig' import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue' import type { Topic } from '@renderer/types/topic' import { getComposerTextFromParts } from '@renderer/utils/message/composerTokens' diff --git a/src/renderer/components/chat/messages/frame/__tests__/MessageHeader.test.tsx b/src/renderer/components/chat/messages/frame/__tests__/MessageHeader.test.tsx index 9092bf30d2..bcb598bb73 100644 --- a/src/renderer/components/chat/messages/frame/__tests__/MessageHeader.test.tsx +++ b/src/renderer/components/chat/messages/frame/__tests__/MessageHeader.test.tsx @@ -19,7 +19,7 @@ vi.mock('@cherrystudio/ui', () => ({ Tooltip: ({ children }: { children: ReactNode }) => <>{children} })) -vi.mock('@renderer/config/models', () => ({ +vi.mock('@renderer/utils/model', () => ({ getModelLogo: () => null })) diff --git a/src/renderer/components/chat/messages/frame/__tests__/messageMenuBarActions.test.tsx b/src/renderer/components/chat/messages/frame/__tests__/messageMenuBarActions.test.tsx index 3d2c61bbeb..a945bf1bf9 100644 --- a/src/renderer/components/chat/messages/frame/__tests__/messageMenuBarActions.test.tsx +++ b/src/renderer/components/chat/messages/frame/__tests__/messageMenuBarActions.test.tsx @@ -1,5 +1,8 @@ +import { + DEFAULT_MESSAGE_MENUBAR_BUTTON_IDS, + getMessageMenuBarConfig +} from '@renderer/components/chat/messages/frame/messageMenuBarConfig' import { defaultMessageMenuConfig, type MessageListActions } from '@renderer/components/chat/messages/types' -import { DEFAULT_MESSAGE_MENUBAR_BUTTON_IDS, getMessageMenuBarConfig } from '@renderer/config/registry/messageMenuBar' import { COMPOSER_CLIPBOARD_FRAGMENT_MIME } from '@renderer/utils/message/composerClipboard' import { fireEvent, render, screen } from '@testing-library/react' import type { ComponentProps, MouseEvent, ReactElement, ReactNode } from 'react' diff --git a/src/renderer/config/registry/__tests__/messageMenuBar.test.ts b/src/renderer/components/chat/messages/frame/__tests__/messageMenuBarConfig.test.ts similarity index 94% rename from src/renderer/config/registry/__tests__/messageMenuBar.test.ts rename to src/renderer/components/chat/messages/frame/__tests__/messageMenuBarConfig.test.ts index eb2eebec4c..efdd453160 100644 --- a/src/renderer/config/registry/__tests__/messageMenuBar.test.ts +++ b/src/renderer/components/chat/messages/frame/__tests__/messageMenuBarConfig.test.ts @@ -1,7 +1,7 @@ import { TopicType } from '@renderer/types/topic' import { describe, expect, it } from 'vitest' -import { getMessageMenuBarConfig } from '../messageMenuBar' +import { getMessageMenuBarConfig } from '../messageMenuBarConfig' describe('messageMenuBar registry', () => { // Regression: agent sessions don't mount the translation-overlay provider, diff --git a/src/renderer/components/chat/messages/frame/messageMenuBarActions.tsx b/src/renderer/components/chat/messages/frame/messageMenuBarActions.tsx index b0443a42de..2e1109dd9e 100644 --- a/src/renderer/components/chat/messages/frame/messageMenuBarActions.tsx +++ b/src/renderer/components/chat/messages/frame/messageMenuBarActions.tsx @@ -1,10 +1,10 @@ import { loggerService } from '@logger' -import { CopyIcon, DeleteIcon, EditIcon, RefreshIcon } from '@renderer/components/Icons' import { DEFAULT_MESSAGE_MENUBAR_BUTTON_IDS, type MessageMenuBarButtonId, STREAMING_DISABLED_BUTTON_IDS -} from '@renderer/config/registry/messageMenuBar' +} from '@renderer/components/chat/messages/frame/messageMenuBarConfig' +import { CopyIcon, DeleteIcon, EditIcon, RefreshIcon } from '@renderer/components/Icons' import { getMessageTitle } from '@renderer/services/MessagesService' import type { MessageExportView } from '@renderer/types/messageExport' import { formatErrorMessageWithPrefix } from '@renderer/utils/error' diff --git a/src/renderer/config/registry/messageMenuBar.ts b/src/renderer/components/chat/messages/frame/messageMenuBarConfig.ts similarity index 100% rename from src/renderer/config/registry/messageMenuBar.ts rename to src/renderer/components/chat/messages/frame/messageMenuBarConfig.ts diff --git a/src/renderer/components/chat/messages/list/MessageAnchorLine.tsx b/src/renderer/components/chat/messages/list/MessageAnchorLine.tsx index 4292462d63..30838edb8d 100644 --- a/src/renderer/components/chat/messages/list/MessageAnchorLine.tsx +++ b/src/renderer/components/chat/messages/list/MessageAnchorLine.tsx @@ -1,9 +1,9 @@ import { Avatar, AvatarFallback, AvatarImage, EmojiAvatar } from '@cherrystudio/ui' -import { getModelLogo } from '@renderer/config/models' import { useTheme } from '@renderer/hooks/useTheme' import { useTimer } from '@renderer/hooks/useTimer' import { scrollIntoView } from '@renderer/utils/dom' import { getTextFromParts } from '@renderer/utils/message/partsHelpers' +import { getModelLogo } from '@renderer/utils/model' import { firstLetter, isEmoji, removeLeadingEmoji } from '@renderer/utils/naming' import { CircleChevronDown } from 'lucide-react' import { type FC, type Ref, useCallback, useEffect, useRef, useState } from 'react' diff --git a/src/renderer/components/composer/tools/__tests__/toolVisibility.test.tsx b/src/renderer/components/composer/tools/__tests__/toolVisibility.test.tsx index 020bfb0e73..da1854b888 100644 --- a/src/renderer/components/composer/tools/__tests__/toolVisibility.test.tsx +++ b/src/renderer/components/composer/tools/__tests__/toolVisibility.test.tsx @@ -7,7 +7,7 @@ const { mockIsGenerateImageModel, mockIsReasoningModel, mockIsSupportedToolUse } mockIsSupportedToolUse: vi.fn() })) -vi.mock('@renderer/config/models', () => ({ +vi.mock('@renderer/utils/model', () => ({ isGenerateImageModel: (...args: unknown[]) => mockIsGenerateImageModel(...args), isReasoningModel: (...args: unknown[]) => mockIsReasoningModel(...args) })) diff --git a/src/renderer/components/composer/tools/components/ThinkingButton.tsx b/src/renderer/components/composer/tools/components/ThinkingButton.tsx index 0609b4162e..af31e5f612 100644 --- a/src/renderer/components/composer/tools/components/ThinkingButton.tsx +++ b/src/renderer/components/composer/tools/components/ThinkingButton.tsx @@ -9,6 +9,9 @@ import { MdiLightbulbOn90, MdiLightbulbQuestion } from '@renderer/components/Icons/SvgIcon' +import { cacheService } from '@renderer/data/CacheService' +import { useAssistant } from '@renderer/hooks/useAssistant' +import type { ThinkingOption } from '@renderer/types/reasoning' import { getThinkModelType, isDoubaoThinkingAutoModel, @@ -17,10 +20,7 @@ import { isOpenAIWebSearchModel, isReasoningModel, MODEL_SUPPORTED_OPTIONS -} from '@renderer/config/models' -import { cacheService } from '@renderer/data/CacheService' -import { useAssistant } from '@renderer/hooks/useAssistant' -import type { ThinkingOption } from '@renderer/types/reasoning' +} from '@renderer/utils/model' import type { Model } from '@shared/data/types/model' import type { FC, SVGProps } from 'react' import { useCallback, useEffect, useMemo } from 'react' diff --git a/src/renderer/components/composer/tools/components/WebSearchButton.tsx b/src/renderer/components/composer/tools/components/WebSearchButton.tsx index cc73b8135e..3927427950 100644 --- a/src/renderer/components/composer/tools/components/WebSearchButton.tsx +++ b/src/renderer/components/composer/tools/components/WebSearchButton.tsx @@ -5,7 +5,7 @@ import { useAssistant } from '@renderer/hooks/useAssistant' import { useProvider } from '@renderer/hooks/useProvider' import { useWebSearchProviders } from '@renderer/hooks/useWebSearch' import { getEffectiveMcpMode } from '@renderer/utils/mcpMode' -import { canModelUseAssistantWebSearch, hasModelBuiltinWebSearch } from '@renderer/utils/modelReconcile' +import { canModelUseAssistantWebSearch, hasModelBuiltinWebSearch } from '@renderer/utils/model' import { getWebSearchProviderLogo } from '@renderer/utils/webSearchProviderMeta' import type { WebSearchProviderId } from '@shared/data/preference/preferenceTypes' import { isGemini3Model, isGeminiModel, isGPT5SeriesReasoningModel, isOpenAIWebSearchModel } from '@shared/utils/model' diff --git a/src/renderer/components/composer/tools/components/__tests__/ThinkingButton.test.tsx b/src/renderer/components/composer/tools/components/__tests__/ThinkingButton.test.tsx index ff12e76c5b..9f2ca1b5aa 100644 --- a/src/renderer/components/composer/tools/components/__tests__/ThinkingButton.test.tsx +++ b/src/renderer/components/composer/tools/components/__tests__/ThinkingButton.test.tsx @@ -50,7 +50,7 @@ vi.mock('@renderer/data/CacheService', () => ({ } })) -vi.mock('@renderer/config/models', () => ({ +vi.mock('@renderer/utils/model', () => ({ getThinkModelType: (...args: unknown[]) => mocks.getThinkModelType(...args), isDoubaoThinkingAutoModel: (...args: unknown[]) => mocks.isDoubaoThinkingAutoModel(...args), isFixedReasoningModel: (...args: unknown[]) => mocks.isFixedReasoningModel(...args), diff --git a/src/renderer/components/composer/tools/components/__tests__/WebSearchButton.test.tsx b/src/renderer/components/composer/tools/components/__tests__/WebSearchButton.test.tsx index 962ee43296..671ed1b83d 100644 --- a/src/renderer/components/composer/tools/components/__tests__/WebSearchButton.test.tsx +++ b/src/renderer/components/composer/tools/components/__tests__/WebSearchButton.test.tsx @@ -66,32 +66,29 @@ vi.mock('@renderer/utils/api', () => ({ splitApiKeyString: (value: string) => value.split(',').map((item) => item.trim()) })) -vi.mock('@renderer/config/models', () => { - const qwenModel = { - id: 'qwen', - name: 'Qwen', - provider: 'cherryai', - group: 'Qwen' - } +vi.mock('@renderer/utils/model', () => { + const isFunctionCallingModel = (model?: Model) => + model?.capabilities.includes(MODEL_CAPABILITY.FUNCTION_CALL) ?? false + const isOpenRouterBuiltInWebSearchModel = () => false + const isWebSearchModel = (model?: Model) => model?.capabilities.includes(MODEL_CAPABILITY.WEB_SEARCH) ?? false + // Mirror the real reconcile composition over the mocked predicates above. + const hasModelBuiltinWebSearch = (model?: Model) => isWebSearchModel(model) || isOpenRouterBuiltInWebSearchModel() + const canModelUseAssistantWebSearch = (model?: Model) => + hasModelBuiltinWebSearch(model) || isFunctionCallingModel(model) return { - qwenModel, - SYSTEM_MODELS: new Proxy( - { defaultModel: [qwenModel] }, - { - get: (target, prop) => (prop in target ? target[prop as keyof typeof target] : []) - } - ), + canModelUseAssistantWebSearch, getThinkModelType: () => 'default', - isFunctionCallingModel: (model?: Model) => model?.capabilities.includes(MODEL_CAPABILITY.FUNCTION_CALL) ?? false, + hasModelBuiltinWebSearch, + isFunctionCallingModel, isGemini3Model: () => false, isGeminiModel: () => false, isGPT5SeriesReasoningModel: () => false, - isOpenRouterBuiltInWebSearchModel: () => false, + isOpenRouterBuiltInWebSearchModel, isOpenAIWebSearchModel: () => false, isSupportedReasoningEffortModel: () => false, isSupportedThinkingTokenModel: () => false, - isWebSearchModel: (model?: Model) => model?.capabilities.includes(MODEL_CAPABILITY.WEB_SEARCH) ?? false, + isWebSearchModel, MODEL_SUPPORTED_OPTIONS: { default: ['none'] }, MODEL_SUPPORTED_REASONING_EFFORT: { default: ['none'] } } diff --git a/src/renderer/components/composer/tools/definitions/__tests__/generateImageTool.test.tsx b/src/renderer/components/composer/tools/definitions/__tests__/generateImageTool.test.tsx index 1c6244db07..2ff24d4300 100644 --- a/src/renderer/components/composer/tools/definitions/__tests__/generateImageTool.test.tsx +++ b/src/renderer/components/composer/tools/definitions/__tests__/generateImageTool.test.tsx @@ -6,7 +6,7 @@ const { mockIsGenerateImageModel } = vi.hoisted(() => ({ mockIsGenerateImageModel: vi.fn() })) -vi.mock('@renderer/config/models', () => ({ +vi.mock('@renderer/utils/model', () => ({ isGenerateImageModel: (...args: unknown[]) => mockIsGenerateImageModel(...args) })) diff --git a/src/renderer/components/composer/tools/definitions/generateImageTool.tsx b/src/renderer/components/composer/tools/definitions/generateImageTool.tsx index 9db9f0b5e1..4fe88c2690 100644 --- a/src/renderer/components/composer/tools/definitions/generateImageTool.tsx +++ b/src/renderer/components/composer/tools/definitions/generateImageTool.tsx @@ -1,5 +1,5 @@ import { defineTool, registerTool, TopicType } from '@renderer/components/composer/tools/types' -import { isGenerateImageModel } from '@renderer/config/models' +import { isGenerateImageModel } from '@renderer/utils/model' import { Image } from 'lucide-react' import { useCallback, useEffect, useState } from 'react' diff --git a/src/renderer/components/composer/tools/definitions/permissionModeTool.tsx b/src/renderer/components/composer/tools/definitions/permissionModeTool.tsx index d4bbb0d9ca..64d131cb9a 100644 --- a/src/renderer/components/composer/tools/definitions/permissionModeTool.tsx +++ b/src/renderer/components/composer/tools/definitions/permissionModeTool.tsx @@ -1,9 +1,9 @@ import { defineTool, registerTool, type ToolRenderContext, TopicType } from '@renderer/components/composer/tools/types' -import { permissionModeCards } from '@renderer/config/agent' import { defaultConfiguration } from '@renderer/hooks/agents/agentConfiguration' import { useAgent } from '@renderer/hooks/agents/useAgent' import { useUpdateAgent } from '@renderer/hooks/agents/useAgent' import type { PermissionMode } from '@renderer/types/agent' +import { permissionModeCards } from '@renderer/utils/agent' import { FolderPen, Pointer, RefreshCcw, Route } from 'lucide-react' import type { ReactNode } from 'react' import { useCallback, useEffect, useMemo } from 'react' diff --git a/src/renderer/components/composer/variants/ChatComposer.tsx b/src/renderer/components/composer/variants/ChatComposer.tsx index 2d2deb39d8..98702e9c45 100644 --- a/src/renderer/components/composer/variants/ChatComposer.tsx +++ b/src/renderer/components/composer/variants/ChatComposer.tsx @@ -34,7 +34,7 @@ import { type Topic, TopicType } from '@renderer/types/topic' import { buildFilePartsForAttachments } from '@renderer/utils/file/buildFileParts' import { getSendMessageShortcutLabel } from '@renderer/utils/input' import type { ComposerAttachment } from '@renderer/utils/message/composerAttachment' -import { canModelUseAssistantWebSearch } from '@renderer/utils/modelReconcile' +import { canModelUseAssistantWebSearch } from '@renderer/utils/model' import { getLeadingEmoji } from '@renderer/utils/naming' import { cn } from '@renderer/utils/style' import type { ComposerQueuedMessagePayload } from '@shared/ai/transport' diff --git a/src/renderer/components/composer/variants/__tests__/ChatComposer.test.tsx b/src/renderer/components/composer/variants/__tests__/ChatComposer.test.tsx index 8f142181b6..77e51e3080 100644 --- a/src/renderer/components/composer/variants/__tests__/ChatComposer.test.tsx +++ b/src/renderer/components/composer/variants/__tests__/ChatComposer.test.tsx @@ -327,7 +327,12 @@ vi.mock('@renderer/components/resource', () => ({ ) })) -vi.mock('@renderer/config/models', () => ({ +vi.mock('@renderer/utils/model', () => ({ + // Mirrors the real reconcile logic using the mocked predicates below: + // canModelUseAssistantWebSearch = isWebSearchModel || isOpenRouterBuiltInWebSearchModel || isFunctionCallingModel. + // The first two predicates are stubbed to false here, so it reduces to the function-call check. + canModelUseAssistantWebSearch: (currentModel?: Model) => + currentModel?.capabilities.includes(MODEL_CAPABILITY.FUNCTION_CALL) ?? false, getThinkModelType: () => 'default', isEmbeddingModel: () => false, isFunctionCallingModel: (currentModel?: Model) => diff --git a/src/renderer/components/composer/variants/shared/__tests__/useComposerFileCapabilities.test.ts b/src/renderer/components/composer/variants/shared/__tests__/useComposerFileCapabilities.test.ts index da11f3c72d..c2d136b15b 100644 --- a/src/renderer/components/composer/variants/shared/__tests__/useComposerFileCapabilities.test.ts +++ b/src/renderer/components/composer/variants/shared/__tests__/useComposerFileCapabilities.test.ts @@ -11,7 +11,7 @@ const mocks = vi.hoisted(() => ({ isGenerateImageModels: vi.fn() })) -vi.mock('@renderer/config/models', () => mocks) +vi.mock('@renderer/utils/model', () => mocks) const model = (id: string) => ({ id }) as unknown as Model diff --git a/src/renderer/components/composer/variants/shared/useComposerFileCapabilities.ts b/src/renderer/components/composer/variants/shared/useComposerFileCapabilities.ts index b24271f94c..c245703e6c 100644 --- a/src/renderer/components/composer/variants/shared/useComposerFileCapabilities.ts +++ b/src/renderer/components/composer/variants/shared/useComposerFileCapabilities.ts @@ -1,4 +1,4 @@ -import { isGenerateImageModel, isGenerateImageModels, isVisionModel, isVisionModels } from '@renderer/config/models' +import { isGenerateImageModel, isGenerateImageModels, isVisionModel, isVisionModels } from '@renderer/utils/model' import type { Model } from '@shared/data/types/model' import { documentExts, imageExts, textExts } from '@shared/utils/file/fileExtensions' import { useMemo } from 'react' diff --git a/src/renderer/components/layout/AppShell.tsx b/src/renderer/components/layout/AppShell.tsx index 4c34bcb051..bb61df27a4 100644 --- a/src/renderer/components/layout/AppShell.tsx +++ b/src/renderer/components/layout/AppShell.tsx @@ -1,10 +1,10 @@ -import { clearTabInstanceMetadata } from '@renderer/config/tabInstanceMetadata' import { usePersistCache } from '@renderer/data/hooks/useCache' import { useCommandHandler } from '@renderer/hooks/command' import { useTabs } from '@renderer/hooks/tab' import useMacTransparentWindow from '@renderer/hooks/useMacTransparentWindow' import { getDefaultRouteTitle, isPageTitledRoute } from '@renderer/utils/routeTitle' import { cn } from '@renderer/utils/style' +import { clearTabInstanceMetadata } from '@renderer/utils/tabInstanceMetadata' import { useCallback, useEffect, useMemo } from 'react' import Sidebar from '../app/Sidebar' diff --git a/src/renderer/components/layout/TabIcon.tsx b/src/renderer/components/layout/TabIcon.tsx index 0ebe7d1d59..e27da60b7c 100644 --- a/src/renderer/components/layout/TabIcon.tsx +++ b/src/renderer/components/layout/TabIcon.tsx @@ -1,5 +1,5 @@ import EmojiIcon from '@renderer/components/EmojiIcon' -import { getMiniAppsLogo } from '@renderer/config/miniApps' +import { getMiniAppsLogo } from '@renderer/components/Icons/miniAppsLogo' import { cn } from '@renderer/utils/style' import { TAB_ICON_EMOJI_PREFIX } from '@renderer/utils/tabIcons' import type { FC } from 'react' diff --git a/src/renderer/components/layout/TabsProvider.tsx b/src/renderer/components/layout/TabsProvider.tsx index 1b6ebfb082..8ccc4793b2 100644 --- a/src/renderer/components/layout/TabsProvider.tsx +++ b/src/renderer/components/layout/TabsProvider.tsx @@ -1,9 +1,9 @@ import { loggerService } from '@logger' -import { resolveSidebarAppTabEntryUrl } from '@renderer/config/sidebar' import { usePersistCache } from '@renderer/data/hooks/useCache' import { type OpenTabOptions, TabsContext, type TabsContextValue } from '@renderer/hooks/tab' import { TabLruManager } from '@renderer/services/TabLruManager' import { getDefaultRouteTitle, isPageTitledRoute, isTopLevelRoute } from '@renderer/utils/routeTitle' +import { resolveSidebarAppTabEntryUrl } from '@renderer/utils/sidebar' import type { Tab, TabSavedState } from '@shared/data/cache/cacheValueTypes' import { IpcChannel } from '@shared/IpcChannel' import type { ReactNode } from 'react' diff --git a/src/renderer/components/layout/__tests__/AppShellTabBar.test.tsx b/src/renderer/components/layout/__tests__/AppShellTabBar.test.tsx index d830a075f6..ec55a361e5 100644 --- a/src/renderer/components/layout/__tests__/AppShellTabBar.test.tsx +++ b/src/renderer/components/layout/__tests__/AppShellTabBar.test.tsx @@ -38,7 +38,7 @@ vi.mock('@renderer/config/constant', () => ({ platform: 'linux' })) -vi.mock('@renderer/config/miniApps', () => ({ +vi.mock('@renderer/components/Icons/miniAppsLogo', () => ({ getMiniAppsLogo: () => undefined })) diff --git a/src/renderer/components/layout/useTabDrag.ts b/src/renderer/components/layout/useTabDrag.ts index e3c3aa0ae7..bd430b90df 100644 --- a/src/renderer/components/layout/useTabDrag.ts +++ b/src/renderer/components/layout/useTabDrag.ts @@ -1,5 +1,5 @@ -import { resolveSidebarAppTabEntryUrl } from '@renderer/config/sidebar' import type { Tab } from '@renderer/hooks/tab' +import { resolveSidebarAppTabEntryUrl } from '@renderer/utils/sidebar' import { IpcChannel } from '@shared/IpcChannel' import { useCallback, useEffect, useRef, useState } from 'react' diff --git a/src/renderer/config/agent.ts b/src/renderer/config/agent.ts deleted file mode 100644 index a8b85fe34b..0000000000 --- a/src/renderer/config/agent.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { resolveProviderIcon } from '@cherrystudio/ui/icons' -import type { PermissionModeCard } from '@renderer/types/agent' -import type { AgentBase, AgentConfiguration, AgentType } from '@shared/data/types/agent' - -// Partial defaults — `name` and `model` are user-supplied at create time. -// Workspace defaults belong to session creation, not to the agent blueprint. -type PartialAgentBase = Partial> - -export const DEFAULT_CLAUDE_CODE_CONFIG: PartialAgentBase = {} as const - -export const DEFAULT_CHERRY_CLAW_CONFIG: PartialAgentBase & { configuration: AgentConfiguration } = { - configuration: { - permission_mode: 'bypassPermissions', - max_turns: 100, - env_vars: {}, - soul_enabled: true, - scheduler_enabled: false, - scheduler_type: 'interval', - heartbeat_enabled: true, - heartbeat_interval: 30 - } -} - -export const getAgentTypeAvatar = (type: AgentType) => { - switch (type) { - case 'claude-code': - return resolveProviderIcon('anthropic') - default: - return undefined - } -} - -export const permissionModeCards: PermissionModeCard[] = [ - { - mode: 'default', - // t('agent.settings.tooling.permissionMode.default.title') - titleKey: 'agent.settings.tooling.permissionMode.default.title', - titleFallback: 'Normal Mode', - descriptionKey: 'agent.settings.tooling.permissionMode.default.description', - descriptionFallback: 'Can read files freely. Asks before editing or running commands.' - }, - { - mode: 'plan', - // t('agent.settings.tooling.permissionMode.plan.title') - titleKey: 'agent.settings.tooling.permissionMode.plan.title', - titleFallback: 'Plan Mode', - descriptionKey: 'agent.settings.tooling.permissionMode.plan.description', - descriptionFallback: 'Can only read files and make plans. Cannot edit files or run commands.' - }, - { - mode: 'acceptEdits', - // t('agent.settings.tooling.permissionMode.acceptEdits.title') - titleKey: 'agent.settings.tooling.permissionMode.acceptEdits.title', - titleFallback: 'Auto-edit Mode', - descriptionKey: 'agent.settings.tooling.permissionMode.acceptEdits.description', - descriptionFallback: 'Can read and edit files freely. Asks before running commands.' - }, - { - mode: 'bypassPermissions', - // t('agent.settings.tooling.permissionMode.bypassPermissions.title') - titleKey: 'agent.settings.tooling.permissionMode.bypassPermissions.title', - titleFallback: 'Full Auto Mode', - descriptionKey: 'agent.settings.tooling.permissionMode.bypassPermissions.description', - descriptionFallback: 'Can do everything without asking. Use with caution.', - caution: true - } -] diff --git a/src/renderer/config/models/default.ts b/src/renderer/config/models/default.ts deleted file mode 100644 index 3c60bb8b72..0000000000 --- a/src/renderer/config/models/default.ts +++ /dev/null @@ -1,2074 +0,0 @@ -import type { Model } from '@renderer/types/model' -import type { SystemProviderId } from '@renderer/types/provider' -import { - CHERRYAI_DEFAULT_MODEL_GROUP, - CHERRYAI_DEFAULT_MODEL_ID, - CHERRYAI_DEFAULT_MODEL_NAME, - CHERRYAI_PROVIDER_ID -} from '@shared/data/presets/cherryai' - -export const qwenModel: Model = { - id: CHERRYAI_DEFAULT_MODEL_ID, - name: CHERRYAI_DEFAULT_MODEL_NAME, - provider: CHERRYAI_PROVIDER_ID, - group: CHERRYAI_DEFAULT_MODEL_GROUP -} - -export const SYSTEM_MODELS: Record = { - defaultModel: [ - // Default assistant model - qwenModel, - // Default topic naming model - qwenModel, - // Default translation model - qwenModel, - // Default quick assistant model - qwenModel - ], - cherryin: [], - vertexai: [], - sophnet: [], - '302ai': [ - { - id: 'deepseek-chat', - name: 'deepseek-chat', - provider: '302ai', - group: 'DeepSeek' - }, - { - id: 'deepseek-reasoner', - name: 'deepseek-reasoner', - provider: '302ai', - group: 'DeepSeek' - }, - { - id: 'chatgpt-4o-latest', - name: 'chatgpt-4o-latest', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'gpt-4.1', - name: 'gpt-4.1', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'o3', - name: 'o3', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'o4-mini', - name: 'o4-mini', - provider: '302ai', - group: 'OpenAI' - }, - { - id: 'qwen3-235b-a22b', - name: 'qwen3-235b-a22b', - provider: '302ai', - group: 'Qwen' - }, - { - id: 'gemini-2.5-flash-preview-05-20', - name: 'gemini-2.5-flash-preview-05-20', - provider: '302ai', - group: 'Gemini' - }, - { - id: 'gemini-2.5-pro-preview-06-05', - name: 'gemini-2.5-pro-preview-06-05', - provider: '302ai', - group: 'Gemini' - }, - { - id: 'claude-sonnet-4-20250514', - provider: '302ai', - name: 'claude-sonnet-4-20250514', - group: 'Anthropic' - }, - { - id: 'claude-opus-4-20250514', - provider: '302ai', - name: 'claude-opus-4-20250514', - group: 'Anthropic' - }, - { - id: 'jina-clip-v2', - name: 'jina-clip-v2', - provider: '302ai', - group: 'Jina AI' - }, - { - id: 'jina-reranker-m0', - name: 'jina-reranker-m0', - provider: '302ai', - group: 'Jina AI' - } - ], - ph8: [ - { - id: 'deepseek-v3-241226', - name: 'deepseek-v3-241226', - provider: 'ph8', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-250120', - name: 'deepseek-r1-250120', - provider: 'ph8', - group: 'DeepSeek' - } - ], - aihubmix: [ - { - id: 'gpt-5', - provider: 'aihubmix', - name: 'gpt-5', - group: 'OpenAI' - }, - { - id: 'gpt-5-mini', - provider: 'aihubmix', - name: 'gpt-5-mini', - group: 'OpenAI' - }, - { - id: 'gpt-5-nano', - provider: 'aihubmix', - name: 'gpt-5-nano', - group: 'OpenAI' - }, - { - id: 'gpt-5-chat-latest', - provider: 'aihubmix', - name: 'gpt-5-chat-latest', - group: 'OpenAI' - }, - { - id: 'o3', - provider: 'aihubmix', - name: 'o3', - group: 'OpenAI' - }, - { - id: 'o4-mini', - provider: 'aihubmix', - name: 'o4-mini', - group: 'OpenAI' - }, - { - id: 'gpt-4.1', - provider: 'aihubmix', - name: 'gpt-4.1', - group: 'OpenAI' - }, - { - id: 'gpt-4o', - provider: 'aihubmix', - name: 'gpt-4o', - group: 'OpenAI' - }, - { - id: 'gpt-image-1', - provider: 'aihubmix', - name: 'gpt-image-1', - group: 'OpenAI' - }, - { - id: 'gpt-image-2', - provider: 'aihubmix', - name: 'gpt-image-2', - group: 'OpenAI' - }, - { - id: 'DeepSeek-V3', - provider: 'aihubmix', - name: 'DeepSeek-V3', - group: 'DeepSeek' - }, - { - id: 'DeepSeek-R1', - provider: 'aihubmix', - name: 'DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'claude-sonnet-4-20250514', - provider: 'aihubmix', - name: 'claude-sonnet-4-20250514', - group: 'Claude' - }, - { - id: 'gemini-2.5-pro', - provider: 'aihubmix', - name: 'gemini-2.5-pro', - group: 'Gemini' - }, - { - id: 'gemini-2.5-flash-nothink', - provider: 'aihubmix', - name: 'gemini-2.5-flash-nothink', - group: 'Gemini' - }, - { - id: 'gemini-2.5-flash', - provider: 'aihubmix', - name: 'gemini-2.5-flash', - group: 'Gemini' - }, - { - id: 'Qwen3-235B-A22B-Instruct-2507', - provider: 'aihubmix', - name: 'Qwen3-235B-A22B-Instruct-2507', - group: 'qwen' - }, - { - id: 'kimi-k2-0711-preview', - provider: 'aihubmix', - name: 'kimi-k2-0711-preview', - group: 'moonshot' - }, - { - id: 'Llama-4-Scout-17B-16E-Instruct', - provider: 'aihubmix', - name: 'Llama-4-Scout-17B-16E-Instruct', - group: 'llama' - }, - { - id: 'Llama-4-Maverick-17B-128E-Instruct-FP8', - provider: 'aihubmix', - name: 'Llama-4-Maverick-17B-128E-Instruct-FP8', - group: 'llama' - } - ], - burncloud: [ - { id: 'claude-opus-4-5-20251101', provider: 'burncloud', name: 'Claude 4.5 Opus', group: 'Claude 4.5' }, - { id: 'claude-sonnet-4-5-20250929', provider: 'burncloud', name: 'Claude 4.5 Sonnet', group: 'Claude 4.5' }, - { id: 'claude-haiku-4-5-20251001', provider: 'burncloud', name: 'Claude 4.5 Haiku', group: 'Claude 4.5' }, - - { id: 'gpt-5', provider: 'burncloud', name: 'GPT 5', group: 'GPT 5' }, - { id: 'gpt-5.1', provider: 'burncloud', name: 'GPT 5.1', group: 'GPT 5.1' }, - - { id: 'gemini-2.5-flash', provider: 'burncloud', name: 'Gemini 2.5 Flash', group: 'Gemini 2.5' }, - { id: 'gemini-2.5-flash-image', provider: 'burncloud', name: 'Gemini 2.5 Flash Image', group: 'Gemini 2.5' }, - { id: 'gemini-2.5-pro', provider: 'burncloud', name: 'Gemini 2.5 Pro', group: 'Gemini 2.5' }, - { id: 'gemini-3-pro-preview', provider: 'burncloud', name: 'Gemini 3 Pro Preview', group: 'Gemini 3' }, - - { id: 'deepseek-reasoner', name: 'DeepSeek Reasoner', provider: 'burncloud', group: 'deepseek-ai' }, - { id: 'deepseek-chat', name: 'DeepSeek Chat', provider: 'burncloud', group: 'deepseek-ai' } - ], - ovms: [], - ollama: [], - lmstudio: [], - silicon: [ - { - id: 'deepseek-ai/DeepSeek-V3.2', - name: 'deepseek-ai/DeepSeek-V3.2', - provider: 'silicon', - group: 'deepseek-ai' - }, - { - id: 'Qwen/Qwen3-8B', - name: 'Qwen/Qwen3-8B', - provider: 'silicon', - group: 'Qwen' - }, - { - id: 'BAAI/bge-m3', - name: 'BAAI/bge-m3', - provider: 'silicon', - group: 'BAAI' - } - ], - ppio: [ - { - id: 'deepseek/deepseek-v3.2', - provider: 'ppio', - name: 'DeepSeek V3.2', - group: 'deepseek' - }, - { - id: 'minimax/minimax-m2', - provider: 'ppio', - name: 'MiniMax M2', - group: 'minimaxai' - }, - { - id: 'qwen/qwen3-235b-a22b-instruct-2507', - provider: 'ppio', - name: 'Qwen3-235b-a22b-instruct-2507', - group: 'qwen' - }, - { - id: 'qwen/qwen3-vl-235b-a22b-instruct', - provider: 'ppio', - name: 'Qwen3-vl-235b-a22b-instruct', - group: 'qwen' - }, - { - id: 'qwen/qwen3-embedding-8b', - provider: 'ppio', - name: 'Qwen3 Embedding 8B', - group: 'qwen' - }, - { - id: 'qwen/qwen3-reranker-8b', - provider: 'ppio', - name: 'Qwen3 Reranker 8B', - group: 'qwen' - } - ], - alayanew: [], - openai: [ - { id: 'gpt-5.4', provider: 'openai', name: ' GPT 5.4', group: 'gpt-5.4' }, - { id: 'gpt-5.4-pro', provider: 'openai', name: ' GPT 5.4 Pro', group: 'gpt-5.4' }, - { id: 'gpt-5.2', provider: 'openai', name: ' GPT 5.2', group: 'gpt-5.2' }, - { id: 'gpt-5.2-pro', provider: 'openai', name: ' GPT 5.2 Pro', group: 'gpt-5.2' }, - { id: 'gpt-5.1', provider: 'openai', name: ' GPT 5.1', group: 'gpt-5.1' }, - { id: 'gpt-5', provider: 'openai', name: ' GPT 5', group: 'gpt-5' }, - { id: 'gpt-5-pro', provider: 'openai', name: ' GPT 5 Pro', group: 'gpt-5' }, - { id: 'gpt-5-chat', provider: 'openai', name: ' GPT 5 Chat', group: 'gpt-5' }, - { id: 'gpt-image-1', provider: 'openai', name: ' GPT Image 1', group: 'gpt-image' } - ], - 'azure-openai': [ - { - id: 'gpt-4o', - provider: 'azure-openai', - name: ' GPT-4o', - group: 'GPT 4o' - }, - { - id: 'gpt-4o-mini', - provider: 'azure-openai', - name: ' GPT-4o-mini', - group: 'GPT 4o' - } - ], - gemini: [ - { - id: 'gemini-2.5-flash', - provider: 'gemini', - name: 'Gemini 2.5 Flash', - group: 'Gemini 2.5' - }, - { - id: 'gemini-2.5-pro', - provider: 'gemini', - name: 'Gemini 2.5 Pro', - group: 'Gemini 2.5' - }, - { - id: 'gemini-2.5-flash-image-preview', - provider: 'gemini', - name: 'Gemini 2.5 Flash Image', - group: 'Gemini 2.5' - }, - { - id: 'gemini-3-pro-image-preview', - provider: 'gemini', - name: 'Gemini 3 Pro Image Preview', - group: 'Gemini 3' - }, - { - id: 'gemini-3-pro-preview', - provider: 'gemini', - name: 'Gemini 3 Pro Preview', - group: 'Gemini 3' - }, - { - id: 'gemini-3.1-pro-preview', - provider: 'gemini', - name: 'Gemini 3.1 Pro Preview', - group: 'Gemini 3' - } - ], - anthropic: [ - { - id: 'claude-opus-4-7', - provider: 'anthropic', - name: 'Claude Opus 4.7', - group: 'Claude 4.7' - }, - { - id: 'claude-opus-4-6', - provider: 'anthropic', - name: 'Claude Opus 4.6', - group: 'Claude 4.6' - }, - { - id: 'claude-sonnet-4-6', - provider: 'anthropic', - name: 'Claude Sonnet 4.6', - group: 'Claude 4.6' - }, - { - id: 'claude-sonnet-4-5', - provider: 'anthropic', - name: 'Claude Sonnet 4.5', - group: 'Claude 4.5' - }, - { - id: 'claude-haiku-4-5', - provider: 'anthropic', - name: 'Claude Haiku 4.5', - group: 'Claude 4.5' - }, - { - id: 'claude-opus-4-5', - provider: 'anthropic', - name: 'Claude Opus 4.5', - group: 'Claude 4.5' - } - ], - deepseek: [ - { - id: 'deepseek-v4-flash', - provider: 'deepseek', - name: 'deepseek-v4-flash', - group: 'DeepSeek' - }, - { - id: 'deepseek-v4-pro', - provider: 'deepseek', - name: 'deepseek-v4-pro', - group: 'DeepSeek' - } - ], - together: [ - { - id: 'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo', - provider: 'together', - name: 'Llama-3.2-11B-Vision', - group: 'Llama-3.2' - }, - { - id: 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', - provider: 'together', - name: 'Llama-3.2-90B-Vision', - group: 'Llama-3.2' - }, - { - id: 'google/gemma-2-27b-it', - provider: 'together', - name: 'gemma-2-27b-it', - group: 'Gemma' - }, - { - id: 'google/gemma-2-9b-it', - provider: 'together', - name: 'gemma-2-9b-it', - group: 'Gemma' - } - ], - ocoolai: [ - { - id: 'deepseek-chat', - provider: 'ocoolai', - name: 'deepseek-chat', - group: 'DeepSeek' - }, - { - id: 'deepseek-reasoner', - provider: 'ocoolai', - name: 'deepseek-reasoner', - group: 'DeepSeek' - }, - { - id: 'deepseek-ai/DeepSeek-R1', - provider: 'ocoolai', - name: 'deepseek-ai/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'HiSpeed/DeepSeek-R1', - provider: 'ocoolai', - name: 'HiSpeed/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'ocoolAI/DeepSeek-R1', - provider: 'ocoolai', - name: 'ocoolAI/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'Azure/DeepSeek-R1', - provider: 'ocoolai', - name: 'Azure/DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'gpt-4o', - provider: 'ocoolai', - name: 'gpt-4o', - group: 'OpenAI' - }, - { - id: 'gpt-4o-all', - provider: 'ocoolai', - name: 'gpt-4o-all', - group: 'OpenAI' - }, - { - id: 'gpt-4o-mini', - provider: 'ocoolai', - name: 'gpt-4o-mini', - group: 'OpenAI' - }, - { - id: 'gpt-4', - provider: 'ocoolai', - name: 'gpt-4', - group: 'OpenAI' - }, - { - id: 'o1-preview', - provider: 'ocoolai', - name: 'o1-preview', - group: 'OpenAI' - }, - { - id: 'o1-mini', - provider: 'ocoolai', - name: 'o1-mini', - group: 'OpenAI' - }, - { - id: 'claude-3-5-sonnet-20240620', - provider: 'ocoolai', - name: 'claude-3-5-sonnet-20240620', - group: 'Anthropic' - }, - { - id: 'claude-3-5-haiku-20241022', - provider: 'ocoolai', - name: 'claude-3-5-haiku-20241022', - group: 'Anthropic' - }, - { - id: 'gemini-pro', - provider: 'ocoolai', - name: 'gemini-pro', - group: 'Gemini' - }, - { - id: 'gemini-1.5-pro', - provider: 'ocoolai', - name: 'gemini-1.5-pro', - group: 'Gemini' - }, - { - id: 'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo', - provider: 'ocoolai', - name: 'Llama-3.2-90B-Vision-Instruct-Turbo', - group: 'Llama-3.2' - }, - { - id: 'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo', - provider: 'ocoolai', - name: 'Llama-3.2-11B-Vision-Instruct-Turbo', - group: 'Llama-3.2' - }, - { - id: 'meta-llama/Llama-3.2-3B-Vision-Instruct-Turbo', - provider: 'ocoolai', - name: 'Llama-3.2-3B-Vision-Instruct-Turbo', - group: 'Llama-3.2' - }, - { - id: 'google/gemma-2-27b-it', - provider: 'ocoolai', - name: 'gemma-2-27b-it', - group: 'Gemma' - }, - { - id: 'google/gemma-2-9b-it', - provider: 'ocoolai', - name: 'gemma-2-9b-it', - group: 'Gemma' - }, - { - id: 'Doubao-embedding', - provider: 'ocoolai', - name: 'Doubao-embedding', - group: 'Doubao' - }, - { - id: 'text-embedding-3-large', - provider: 'ocoolai', - name: 'text-embedding-3-large', - group: 'Embedding' - }, - { - id: 'text-embedding-3-small', - provider: 'ocoolai', - name: 'text-embedding-3-small', - group: 'Embedding' - }, - { - id: 'text-embedding-v2', - provider: 'ocoolai', - name: 'text-embedding-v2', - group: 'Embedding' - } - ], - github: [ - { - id: 'gpt-4o', - provider: 'github', - name: 'OpenAI GPT-4o', - group: 'OpenAI' - } - ], - copilot: [ - { - id: 'gpt-5-mini', - provider: 'copilot', - name: 'gpt-5-mini', - group: 'copilot' - } - ], - yi: [ - { id: 'yi-lightning', name: 'Yi Lightning', provider: 'yi', group: 'yi-lightning', owned_by: '01.ai' }, - { id: 'yi-vision-v2', name: 'Yi Vision v2', provider: 'yi', group: 'yi-vision', owned_by: '01.ai' } - ], - zhipu: [ - { - id: 'glm-5', - provider: 'zhipu', - name: 'GLM-5', - group: 'GLM-5' - }, - { - id: 'glm-4.7', - provider: 'zhipu', - name: 'GLM-4.7', - group: 'GLM-4.7' - }, - { - id: 'glm-4.5-flash', - provider: 'zhipu', - name: 'GLM-4.5-Flash', - group: 'GLM-4.5' - }, - { - id: 'glm-4.6', - provider: 'zhipu', - name: 'GLM-4.6', - group: 'GLM-4.6' - }, - { - id: 'glm-4.6v', - provider: 'zhipu', - name: 'GLM-4.6V', - group: 'GLM-4.6V' - }, - { - id: 'glm-4.6v-flash', - provider: 'zhipu', - name: 'GLM-4.6V-Flash', - group: 'GLM-4.6V' - }, - { - id: 'glm-4.6v-flashx', - provider: 'zhipu', - name: 'GLM-4.6V-FlashX', - group: 'GLM-4.6V' - }, - { - id: 'glm-4.7', - provider: 'zhipu', - name: 'GLM-4.7', - group: 'GLM-4.7' - }, - { - id: 'glm-4.5', - provider: 'zhipu', - name: 'GLM-4.5', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5-air', - provider: 'zhipu', - name: 'GLM-4.5-Air', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5-airx', - provider: 'zhipu', - name: 'GLM-4.5-AirX', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5v', - provider: 'zhipu', - name: 'GLM-4.5V', - group: 'GLM-4.5V' - }, - { - id: 'embedding-3', - provider: 'zhipu', - name: 'Embedding-3', - group: 'Embedding' - }, - { - id: 'cogView-4-250304', - provider: 'zhipu', - name: 'cogView-4', - group: 'cogView' - } - ], - moonshot: [ - { - id: 'moonshot-v1-auto', - name: 'moonshot-v1-auto', - provider: 'moonshot', - group: 'moonshot-v1', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }] - }, - { - id: 'kimi-k2-0711-preview', - name: 'kimi-k2-0711-preview', - provider: 'moonshot', - group: 'kimi-k2', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }], - pricing: { - input_per_million_tokens: 0.6, - output_per_million_tokens: 2.5, - currencySymbol: 'USD' - } - }, - { - id: 'kimi-k2.5', - provider: 'moonshot', - name: 'Kimi K2.5', - group: 'Kimi K2.5', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'vision' }, { type: 'function_calling' }] - }, - { - id: 'kimi-k2.6', - provider: 'moonshot', - name: 'Kimi K2.6', - group: 'Kimi K2.6', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'vision' }, { type: 'function_calling' }] - }, - { - id: 'kimi-k2-0905-Preview', - provider: 'moonshot', - name: 'Kimi K2 0905 Preview', - group: 'Kimi K2', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }] - }, - { - id: 'kimi-k2-turbo-preview', - provider: 'moonshot', - name: 'Kimi K2 Turbo Preview', - group: 'Kimi K2', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }] - }, - { - id: 'kimi-k2-thinking', - provider: 'moonshot', - name: 'Kimi K2 Thinking', - group: 'Kimi K2 Thinking', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }] - }, - { - id: 'kimi-k2-thinking-turbo', - provider: 'moonshot', - name: 'Kimi K2 Thinking Turbo', - group: 'Kimi K2 Thinking', - owned_by: 'moonshot', - capabilities: [{ type: 'text' }, { type: 'function_calling' }] - } - ], - baichuan: [ - { - id: 'Baichuan4', - provider: 'baichuan', - name: 'Baichuan4', - group: 'Baichuan4' - }, - { - id: 'Baichuan3-Turbo', - provider: 'baichuan', - name: 'Baichuan3 Turbo', - group: 'Baichuan3' - }, - { - id: 'Baichuan3-Turbo-128k', - provider: 'baichuan', - name: 'Baichuan3 Turbo 128k', - group: 'Baichuan3' - }, - { - id: 'Baichuan4-Turbo', - provider: 'baichuan', - name: 'Baichuan4 Turbo', - group: 'Baichuan4' - }, - { - id: 'Baichuan4-Air', - provider: 'baichuan', - name: 'Baichuan4 Air', - group: 'Baichuan4' - }, - { - id: 'Baichuan-M2', - provider: 'baichuan', - name: 'Baichuan M2', - group: 'Baichuan-M2' - }, - { - id: 'Baichuan-M2-Plus', - provider: 'baichuan', - name: 'Baichuan M2 Plus', - group: 'Baichuan-M2' - }, - { - id: 'Baichuan-M3', - provider: 'baichuan', - name: 'Baichuan M3', - group: 'Baichuan-M3' - }, - { - id: 'Baichuan-M3-Plus', - provider: 'baichuan', - name: 'Baichuan M3 Plus', - group: 'Baichuan-M3' - } - ], - modelscope: [ - { - id: 'Qwen/Qwen2.5-72B-Instruct', - name: 'Qwen/Qwen2.5-72B-Instruct', - provider: 'modelscope', - group: 'Qwen' - }, - { - id: 'Qwen/Qwen2.5-VL-72B-Instruct', - name: 'Qwen/Qwen2.5-VL-72B-Instruct', - provider: 'modelscope', - group: 'Qwen' - }, - { - id: 'Qwen/Qwen2.5-Coder-32B-Instruct', - name: 'Qwen/Qwen2.5-Coder-32B-Instruct', - provider: 'modelscope', - group: 'Qwen' - }, - { - id: 'deepseek-ai/DeepSeek-R1', - name: 'deepseek-ai/DeepSeek-R1', - provider: 'modelscope', - group: 'deepseek-ai' - }, - { - id: 'deepseek-ai/DeepSeek-V3', - name: 'deepseek-ai/DeepSeek-V3', - provider: 'modelscope', - group: 'deepseek-ai' - } - ], - dashscope: [ - { id: 'qwen3.5-plus', name: 'Qwen3.5-Plus', provider: 'dashscope', group: 'Qwen' }, - { id: 'qwen3.5-flash', name: 'Qwen3.5-Flash', provider: 'dashscope', group: 'Qwen' }, - { id: 'qwen3-max', name: 'Qwen3-Max', provider: 'dashscope', group: 'Qwen' }, - { id: 'kimi-k2.5', name: 'Kimi K2.5', provider: 'dashscope', group: 'Kimi' }, - { id: 'glm-5', name: 'GLM-5', provider: 'dashscope', group: 'GLM' }, - { id: 'MiniMax/MiniMax-M2.5', name: 'MiniMax M2.5', provider: 'dashscope', group: 'MiniMax' }, - { id: 'deepseek-v3.2', name: 'DeepSeek V3.2', provider: 'dashscope', group: 'DeepSeek' } - ], - stepfun: [ - { - id: 'step-1-8k', - provider: 'stepfun', - name: 'Step 1 8K', - group: 'Step 1' - }, - { - id: 'step-1-flash', - provider: 'stepfun', - name: 'Step 1 Flash', - group: 'Step 1' - } - ], - doubao: [ - { - id: 'doubao-seed-1-8-251228', - provider: 'doubao', - name: 'Doubao-Seed-1.8', - group: 'Doubao-Seed-1.8' - }, - { - id: 'doubao-1-5-vision-pro-32k-250115', - provider: 'doubao', - name: 'doubao-1.5-vision-pro', - group: 'Doubao-1.5-vision-pro' - }, - { - id: 'doubao-1-5-pro-32k-250115', - provider: 'doubao', - name: 'doubao-1.5-pro-32k', - group: 'Doubao-1.5-pro' - }, - { - id: 'doubao-1-5-pro-32k-character-250228', - provider: 'doubao', - name: 'doubao-1.5-pro-32k-character', - group: 'Doubao-1.5-pro' - }, - { - id: 'doubao-1-5-pro-256k-250115', - provider: 'doubao', - name: 'Doubao-1.5-pro-256k', - group: 'Doubao-1.5-pro' - }, - { - id: 'deepseek-r1-250120', - provider: 'doubao', - name: 'DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-distill-qwen-32b-250120', - provider: 'doubao', - name: 'DeepSeek-R1-Distill-Qwen-32B', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-distill-qwen-7b-250120', - provider: 'doubao', - name: 'DeepSeek-R1-Distill-Qwen-7B', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3-250324', - provider: 'doubao', - name: 'DeepSeek-V3', - group: 'DeepSeek' - }, - { - id: 'doubao-pro-32k-241215', - provider: 'doubao', - name: 'Doubao-pro-32k', - group: 'Doubao-pro' - }, - { - id: 'doubao-pro-32k-functioncall-241028', - provider: 'doubao', - name: 'Doubao-pro-32k-functioncall-241028', - group: 'Doubao-pro' - }, - { - id: 'doubao-pro-32k-character-241215', - provider: 'doubao', - name: 'Doubao-pro-32k-character-241215', - group: 'Doubao-pro' - }, - { - id: 'doubao-pro-256k-241115', - provider: 'doubao', - name: 'Doubao-pro-256k', - group: 'Doubao-pro' - }, - { - id: 'doubao-lite-4k-character-240828', - provider: 'doubao', - name: 'Doubao-lite-4k-character-240828', - group: 'Doubao-lite' - }, - { - id: 'doubao-lite-32k-240828', - provider: 'doubao', - name: 'Doubao-lite-32k', - group: 'Doubao-lite' - }, - { - id: 'doubao-lite-32k-character-241015', - provider: 'doubao', - name: 'Doubao-lite-32k-character-241015', - group: 'Doubao-lite' - }, - { - id: 'doubao-lite-128k-240828', - provider: 'doubao', - name: 'Doubao-lite-128k', - group: 'Doubao-lite' - }, - { - id: 'doubao-1-5-lite-32k-250115', - provider: 'doubao', - name: 'Doubao-1.5-lite-32k', - group: 'Doubao-lite' - }, - { - id: 'doubao-embedding-large-text-240915', - provider: 'doubao', - name: 'Doubao-embedding-large', - group: 'Doubao-embedding' - }, - { - id: 'doubao-embedding-text-240715', - provider: 'doubao', - name: 'Doubao-embedding', - group: 'Doubao-embedding' - }, - { - id: 'doubao-embedding-vision-241215', - provider: 'doubao', - name: 'Doubao-embedding-vision', - group: 'Doubao-embedding' - }, - { - id: 'doubao-vision-lite-32k-241015', - provider: 'doubao', - name: 'Doubao-vision-lite-32k', - group: 'Doubao-vision-lite-32k' - }, - { - id: 'doubao-seed-2-0-pro-260215', - provider: 'doubao', - name: 'Doubao-Seed-2.0-Pro', - group: 'Doubao-Seed-2.0' - }, - { - id: 'doubao-seed-2-0-lite-260215', - provider: 'doubao', - name: 'Doubao-Seed-2.0-Lite', - group: 'Doubao-Seed-2.0' - }, - { - id: 'doubao-seed-2-0-code-preview-260215', - provider: 'doubao', - name: 'Doubao-Seed-2.0-Code-Preview', - group: 'Doubao-Seed-2.0' - }, - { - id: 'doubao-seed-2-0-mini-260215', - provider: 'doubao', - name: 'Doubao-Seed-2.0-Mini', - group: 'Doubao-Seed-2.0' - } - ], - minimax: [ - { - id: 'MiniMax-M3', - provider: 'minimax', - name: 'MiniMax-M3', - group: 'M3' - }, - { - id: 'MiniMax-M2.7', - provider: 'minimax', - name: 'MiniMax-M2.7', - group: 'M2.7' - }, - { - id: 'MiniMax-M2.7-highspeed', - provider: 'minimax', - name: 'MiniMax-M2.7-highspeed', - group: 'M2.7' - } - ], - 'minimax-global': [ - { - id: 'MiniMax-M3', - provider: 'minimax-global', - name: 'MiniMax-M3', - group: 'M3' - }, - { - id: 'MiniMax-M2.7', - provider: 'minimax-global', - name: 'MiniMax-M2.7', - group: 'M2.7' - }, - { - id: 'MiniMax-M2.7-highspeed', - provider: 'minimax-global', - name: 'MiniMax-M2.7-highspeed', - group: 'M2.7' - } - ], - hyperbolic: [ - { - id: 'Qwen/Qwen2-VL-72B-Instruct', - provider: 'hyperbolic', - name: 'Qwen2-VL-72B-Instruct', - group: 'Qwen2-VL' - }, - { - id: 'Qwen/Qwen2-VL-7B-Instruct', - provider: 'hyperbolic', - name: 'Qwen2-VL-7B-Instruct', - group: 'Qwen2-VL' - }, - { - id: 'mistralai/Pixtral-12B-2409', - provider: 'hyperbolic', - name: 'Pixtral-12B-2409', - group: 'Pixtral' - }, - { - id: 'meta-llama/Meta-Llama-3.1-405B', - provider: 'hyperbolic', - name: 'Meta-Llama-3.1-405B', - group: 'Meta-Llama-3.1' - } - ], - grok: [ - { - id: 'grok-4.3', - provider: 'grok', - name: 'Grok 4.3', - group: 'Grok' - }, - { - id: 'grok-4', - provider: 'grok', - name: 'Grok 4', - group: 'Grok' - }, - { - id: 'grok-3', - provider: 'grok', - name: 'Grok 3', - group: 'Grok' - }, - { - id: 'grok-3-fast', - provider: 'grok', - name: 'Grok 3 Fast', - group: 'Grok' - }, - { - id: 'grok-3-mini', - provider: 'grok', - name: 'Grok 3 Mini', - group: 'Grok' - }, - { - id: 'grok-3-mini-fast', - provider: 'grok', - name: 'Grok 3 Mini Fast', - group: 'Grok' - } - ], - mistral: [ - { - id: 'pixtral-12b-2409', - provider: 'mistral', - name: 'Pixtral 12B [Free]', - group: 'Pixtral' - }, - { - id: 'pixtral-large-latest', - provider: 'mistral', - name: 'Pixtral Large', - group: 'Pixtral' - }, - { - id: 'ministral-3b-latest', - provider: 'mistral', - name: 'Mistral 3B [Free]', - group: 'Mistral Mini' - }, - { - id: 'ministral-8b-latest', - provider: 'mistral', - name: 'Mistral 8B [Free]', - group: 'Mistral Mini' - }, - { - id: 'codestral-latest', - provider: 'mistral', - name: 'Mistral Codestral', - group: 'Mistral Code' - }, - { - id: 'mistral-large-latest', - provider: 'mistral', - name: 'Mistral Large', - group: 'Mistral Chat' - }, - { - id: 'mistral-small-latest', - provider: 'mistral', - name: 'Mistral Small', - group: 'Mistral Chat' - }, - { - id: 'open-mistral-nemo', - provider: 'mistral', - name: 'Mistral Nemo', - group: 'Mistral Chat' - }, - { - id: 'mistral-embed', - provider: 'mistral', - name: 'Mistral Embedding', - group: 'Mistral Embed' - } - ], - jina: [ - { - id: 'jina-clip-v1', - provider: 'jina', - name: 'jina-clip-v1', - group: 'Jina Clip' - }, - { - id: 'jina-clip-v2', - provider: 'jina', - name: 'jina-clip-v2', - group: 'Jina Clip' - }, - { - id: 'jina-embeddings-v2-base-en', - provider: 'jina', - name: 'jina-embeddings-v2-base-en', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-es', - provider: 'jina', - name: 'jina-embeddings-v2-base-es', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-de', - provider: 'jina', - name: 'jina-embeddings-v2-base-de', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-zh', - provider: 'jina', - name: 'jina-embeddings-v2-base-zh', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v2-base-code', - provider: 'jina', - name: 'jina-embeddings-v2-base-code', - group: 'Jina Embeddings V2' - }, - { - id: 'jina-embeddings-v3', - provider: 'jina', - name: 'jina-embeddings-v3', - group: 'Jina Embeddings V3' - } - ], - fireworks: [ - { - id: 'accounts/fireworks/models/mythomax-l2-13b', - provider: 'fireworks', - name: 'mythomax-l2-13b', - group: 'Gryphe' - }, - { - id: 'accounts/fireworks/models/llama-v3-70b-instruct', - provider: 'fireworks', - name: 'Llama-3-70B-Instruct', - group: 'Llama3' - } - ], - hunyuan: [ - { - id: 'hunyuan-pro', - provider: 'hunyuan', - name: 'hunyuan-pro', - group: 'Hunyuan' - }, - { - id: 'hunyuan-standard', - provider: 'hunyuan', - name: 'hunyuan-standard', - group: 'Hunyuan' - }, - { - id: 'hunyuan-lite', - provider: 'hunyuan', - name: 'hunyuan-lite', - group: 'Hunyuan' - }, - { - id: 'hunyuan-standard-256k', - provider: 'hunyuan', - name: 'hunyuan-standard-256k', - group: 'Hunyuan' - }, - { - id: 'hunyuan-vision', - provider: 'hunyuan', - name: 'hunyuan-vision', - group: 'Hunyuan' - }, - { - id: 'hunyuan-code', - provider: 'hunyuan', - name: 'hunyuan-code', - group: 'Hunyuan' - }, - { - id: 'hunyuan-role', - provider: 'hunyuan', - name: 'hunyuan-role', - group: 'Hunyuan' - }, - { - id: 'hunyuan-turbo', - provider: 'hunyuan', - name: 'hunyuan-turbo', - group: 'Hunyuan' - }, - { - id: 'hunyuan-turbos-latest', - provider: 'hunyuan', - name: 'hunyuan-turbos-latest', - group: 'Hunyuan' - }, - { - id: 'hunyuan-embedding', - provider: 'hunyuan', - name: 'hunyuan-embedding', - group: 'Embedding' - } - ], - nvidia: [ - { - id: '01-ai/yi-large', - provider: 'nvidia', - name: 'yi-large', - group: 'Yi' - }, - { - id: 'meta/llama-3.1-405b-instruct', - provider: 'nvidia', - name: 'llama-3.1-405b-instruct', - group: 'llama-3.1' - } - ], - openrouter: [ - { - id: 'google/gemini-2.5-flash-image-preview', - provider: 'openrouter', - name: 'Google: Gemini 2.5 Flash Image', - group: 'google' - }, - { - id: 'google/gemini-2.5-flash-preview', - provider: 'openrouter', - name: 'Google: Gemini 2.5 Flash Preview', - group: 'google' - }, - { - id: 'qwen/qwen-2.5-7b-instruct:free', - provider: 'openrouter', - name: 'Qwen: Qwen-2.5-7B Instruct', - group: 'qwen' - }, - { - id: 'deepseek/deepseek-chat', - provider: 'openrouter', - name: 'DeepSeek: V3', - group: 'deepseek' - }, - { - id: 'mistralai/mistral-7b-instruct:free', - provider: 'openrouter', - name: 'Mistral: Mistral 7B Instruct', - group: 'mistralai' - } - ], - groq: [ - { - id: 'llama3-8b-8192', - provider: 'groq', - name: 'LLaMA3 8B', - group: 'Llama3' - }, - { - id: 'llama3-70b-8192', - provider: 'groq', - name: 'LLaMA3 70B', - group: 'Llama3' - }, - { - id: 'mistral-saba-24b', - provider: 'groq', - name: 'Mistral Saba 24B', - group: 'Mistral' - }, - { - id: 'gemma-9b-it', - provider: 'groq', - name: 'Gemma 9B', - group: 'Gemma' - } - ], - 'baidu-cloud': [ - { - id: 'deepseek-r1', - provider: 'baidu-cloud', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'baidu-cloud', - name: 'DeepSeek V3', - group: 'DeepSeek' - }, - { - id: 'ernie-4.0-8k-latest', - provider: 'baidu-cloud', - name: 'ERNIE-4.0', - group: 'ERNIE' - }, - { - id: 'ernie-4.0-turbo-8k-latest', - provider: 'baidu-cloud', - name: 'ERNIE 4.0 Trubo', - group: 'ERNIE' - }, - { - id: 'ernie-speed-8k', - provider: 'baidu-cloud', - name: 'ERNIE Speed', - group: 'ERNIE' - }, - { - id: 'ernie-lite-8k', - provider: 'baidu-cloud', - name: 'ERNIE Lite', - group: 'ERNIE' - }, - { - id: 'bge-large-zh', - provider: 'baidu-cloud', - name: 'BGE Large ZH', - group: 'Embedding' - }, - { - id: 'bge-large-en', - provider: 'baidu-cloud', - name: 'BGE Large EN', - group: 'Embedding' - } - ], - dmxapi: [ - { - id: 'Qwen/Qwen2.5-7B-Instruct', - provider: 'dmxapi', - name: 'Qwen/Qwen2.5-7B-Instruct', - group: '免费模型' - }, - { - id: 'ERNIE-Speed-128K', - provider: 'dmxapi', - name: 'ERNIE-Speed-128K', - group: '免费模型' - }, - { - id: 'gpt-4o', - provider: 'dmxapi', - name: 'gpt-4o', - group: 'OpenAI' - }, - { - id: 'gpt-4o-mini', - provider: 'dmxapi', - name: 'gpt-4o-mini', - group: 'OpenAI' - }, - { - id: 'DMXAPI-DeepSeek-R1', - provider: 'dmxapi', - name: 'DMXAPI-DeepSeek-R1', - group: 'DeepSeek' - }, - { - id: 'DMXAPI-DeepSeek-V3', - provider: 'dmxapi', - name: 'DMXAPI-DeepSeek-V3', - group: 'DeepSeek' - }, - { - id: 'claude-3-5-sonnet-20241022', - provider: 'dmxapi', - name: 'claude-3-5-sonnet-20241022', - group: 'Claude' - }, - { - id: 'gemini-2.0-flash', - provider: 'dmxapi', - name: 'gemini-2.0-flash', - group: 'Gemini' - } - ], - perplexity: [ - { - id: 'sonar-reasoning-pro', - provider: 'perplexity', - name: 'sonar-reasoning-pro', - group: 'Sonar' - }, - { - id: 'sonar-reasoning', - provider: 'perplexity', - name: 'sonar-reasoning', - group: 'Sonar' - }, - { - id: 'sonar-pro', - provider: 'perplexity', - name: 'sonar-pro', - group: 'Sonar' - }, - { - id: 'sonar', - provider: 'perplexity', - name: 'sonar', - group: 'Sonar' - }, - { - id: 'sonar-deep-research', - provider: 'perplexity', - name: 'sonar-deep-research', - group: 'Sonar' - } - ], - infini: [ - { - id: 'deepseek-r1', - provider: 'infini', - name: 'deepseek-r1', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-distill-qwen-32b', - provider: 'infini', - name: 'deepseek-r1-distill-qwen-32b', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'infini', - name: 'deepseek-v3', - group: 'DeepSeek' - }, - { - id: 'qwen2.5-72b-instruct', - provider: 'infini', - name: 'qwen2.5-72b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2.5-32b-instruct', - provider: 'infini', - name: 'qwen2.5-32b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2.5-14b-instruct', - provider: 'infini', - name: 'qwen2.5-14b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2.5-7b-instruct', - provider: 'infini', - name: 'qwen2.5-7b-instruct', - group: 'Qwen' - }, - { - id: 'qwen2-72b-instruct', - provider: 'infini', - name: 'qwen2-72b-instruct', - group: 'Qwen' - }, - { - id: 'qwq-32b-preview', - provider: 'infini', - name: 'qwq-32b-preview', - group: 'Qwen' - }, - { - id: 'qwen2.5-coder-32b-instruct', - provider: 'infini', - name: 'qwen2.5-coder-32b-instruct', - group: 'Qwen' - }, - { - id: 'llama-3.3-70b-instruct', - provider: 'infini', - name: 'llama-3.3-70b-instruct', - group: 'Llama' - }, - { - id: 'bge-m3', - provider: 'infini', - name: 'bge-m3', - group: 'BAAI' - }, - { - id: 'gemma-2-27b-it', - provider: 'infini', - name: 'gemma-2-27b-it', - group: 'Gemma' - }, - { - id: 'jina-embeddings-v2-base-zh', - provider: 'infini', - name: 'jina-embeddings-v2-base-zh', - group: 'Jina' - }, - { - id: 'jina-embeddings-v2-base-code', - provider: 'infini', - name: 'jina-embeddings-v2-base-code', - group: 'Jina' - } - ], - xirang: [], - 'tencent-cloud-ti': [ - { - id: 'deepseek-r1', - provider: 'tencent-cloud-ti', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'tencent-cloud-ti', - name: 'DeepSeek V3', - group: 'DeepSeek' - } - ], - gpustack: [], - voyageai: [ - { - id: 'voyage-3-large', - provider: 'voyageai', - name: 'voyage-3-large', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-3', - provider: 'voyageai', - name: 'voyage-3', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-3-lite', - provider: 'voyageai', - name: 'voyage-3-lite', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-code-3', - provider: 'voyageai', - name: 'voyage-code-3', - group: 'Voyage Embeddings V3' - }, - { - id: 'voyage-finance-3', - provider: 'voyageai', - name: 'voyage-finance-3', - group: 'Voyage Embeddings V2' - }, - { - id: 'voyage-law-2', - provider: 'voyageai', - name: 'voyage-law-2', - group: 'Voyage Embeddings V2' - }, - { - id: 'voyage-code-2', - provider: 'voyageai', - name: 'voyage-code-2', - group: 'Voyage Embeddings V2' - }, - { - id: 'rerank-2', - provider: 'voyageai', - name: 'rerank-2', - group: 'Voyage Rerank V2' - }, - { - id: 'rerank-2-lite', - provider: 'voyageai', - name: 'rerank-2-lite', - group: 'Voyage Rerank V2' - } - ], - qiniu: [ - { - id: 'deepseek-r1', - provider: 'qiniu', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-search', - provider: 'qiniu', - name: 'DeepSeek R1 Search', - group: 'DeepSeek' - }, - { - id: 'deepseek-r1-32b', - provider: 'qiniu', - name: 'DeepSeek R1 32B', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'qiniu', - name: 'DeepSeek V3', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3-search', - provider: 'qiniu', - name: 'DeepSeek V3 Search', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3-tool', - provider: 'qiniu', - name: 'DeepSeek V3 Tool', - group: 'DeepSeek' - }, - { - id: 'qwq-32b', - provider: 'qiniu', - name: 'QWQ 32B', - group: 'Qwen' - }, - { - id: 'qwen2.5-72b-instruct', - provider: 'qiniu', - name: 'Qwen2.5 72B Instruct', - group: 'Qwen' - } - ], - tokenflux: [ - { - id: 'gpt-4.1', - provider: 'tokenflux', - name: 'GPT-4.1', - group: 'GPT-4.1' - }, - { - id: 'gpt-4.1-mini', - provider: 'tokenflux', - name: 'GPT-4.1 Mini', - group: 'GPT-4.1' - }, - { - id: 'claude-sonnet-4', - provider: 'tokenflux', - name: 'Claude Sonnet 4', - group: 'Claude' - }, - { - id: 'claude-3-7-sonnet', - provider: 'tokenflux', - name: 'Claude 3.7 Sonnet', - group: 'Claude' - }, - { - id: 'gemini-2.5-pro', - provider: 'tokenflux', - name: 'Gemini 2.5 Pro', - group: 'Gemini' - }, - { - id: 'gemini-2.5-flash', - provider: 'tokenflux', - name: 'Gemini 2.5 Flash', - group: 'Gemini' - }, - { - id: 'deepseek-r1', - provider: 'tokenflux', - name: 'DeepSeek R1', - group: 'DeepSeek' - }, - { - id: 'deepseek-v3', - provider: 'tokenflux', - name: 'DeepSeek V3', - group: 'DeepSeek' - }, - { - id: 'qwen-max', - provider: 'tokenflux', - name: 'Qwen Max', - group: 'Qwen' - }, - { - id: 'qwen-plus', - provider: 'tokenflux', - name: 'Qwen Plus', - group: 'Qwen' - } - ], - cephalon: [ - { - id: 'DeepSeek-R1', - provider: 'cephalon', - name: 'DeepSeek-R1满血版', - capabilities: [{ type: 'reasoning' }], - group: 'DeepSeek' - } - ], - lanyun: [ - { - id: '/maas/deepseek-ai/DeepSeek-R1-0528', - name: 'deepseek-ai/DeepSeek-R1', - provider: 'lanyun', - group: 'deepseek-ai' - }, - { - id: '/maas/deepseek-ai/DeepSeek-V3-0324', - name: 'deepseek-ai/DeepSeek-V3', - provider: 'lanyun', - group: 'deepseek-ai' - }, - { - id: '/maas/qwen/Qwen2.5-72B-Instruct', - provider: 'lanyun', - name: 'Qwen2.5-72B-Instruct', - group: 'Qwen' - }, - { - id: '/maas/qwen/Qwen3-235B-A22B', - name: 'Qwen/Qwen3-235B', - provider: 'lanyun', - group: 'Qwen' - }, - { - id: '/maas/minimax/MiniMax-M1-80k', - name: 'MiniMax-M1-80k', - provider: 'lanyun', - group: 'MiniMax' - }, - { - id: '/maas/google/Gemma3-27B', - name: 'Gemma3-27B', - provider: 'lanyun', - group: 'google' - } - ], - 'new-api': [], - 'aws-bedrock': [], - poe: [ - { id: 'Claude-Opus-4.6', name: 'Claude Opus 4.6', provider: 'poe', group: 'Anthropic' }, - { id: 'Claude-Sonnet-4.6', name: 'Claude Sonnet 4.6', provider: 'poe', group: 'Anthropic' }, - { id: 'Claude-Haiku-4.5', name: 'Claude Haiku 4.5', provider: 'poe', group: 'Anthropic' }, - { id: 'GPT-5.4', name: 'GPT 5.4', provider: 'poe', group: 'OpenAI' }, - { id: 'GPT-5.3-Codex', name: 'GPT 5.3 Codex', provider: 'poe', group: 'OpenAI' }, - { id: 'GPT-5.2', name: 'GPT 5.2', provider: 'poe', group: 'OpenAI' }, - { id: 'GPT-5.2-Codex', name: 'GPT 5.2 Codex', provider: 'poe', group: 'OpenAI' }, - { id: 'GPT-5.1', name: 'GPT 5.1', provider: 'poe', group: 'OpenAI' }, - { id: 'Gemini-3.1-Pro', name: 'Gemini 3.1 Pro', provider: 'poe', group: 'Google' }, - { id: 'Grok-4', name: 'Grok 4', provider: 'poe', group: 'xAI' }, - { id: 'DeepSeek-R1', name: 'DeepSeek R1', provider: 'poe', group: 'DeepSeek' }, - { id: 'Kimi-K2.5', name: 'Kimi K2.5', provider: 'poe', group: 'Kimi' }, - { id: 'Kimi-K2-Thinking', name: 'Kimi K2 Thinking', provider: 'poe', group: 'Kimi' } - ], - aionly: [ - { - id: 'claude-opus-4-6', - name: 'Claude Opus 4.6', - provider: 'aionly', - group: 'Anthropic' - }, - { - id: 'claude-haiku-4-5-20251001', - name: 'Claude Haiku 4.5', - provider: 'aionly', - group: 'Anthropic' - }, - { - id: 'claude-sonnet-4-6', - name: 'Claude Sonnet 4.6', - provider: 'aionly', - group: 'Anthropic' - }, - { - id: 'gpt-5.4', - name: 'GPT-5.4', - provider: 'aionly', - group: 'OpenAI' - }, - { - id: 'gpt-5.4-mini', - name: 'GPT-5.4-MiNi', - provider: 'aionly', - group: 'OpenAI' - }, - { - id: 'gpt-5.4-nano', - name: 'GPT-5.4-NaNo', - provider: 'aionly', - group: 'OpenAI' - }, - { - id: 'gemini-3.1-flash-lite-preview', - name: 'Gemini 3.1 Flash-Lite', - provider: 'aionly', - group: 'Google' - }, - { - id: 'gemini-3.1-pro-preview', - name: 'Gemini 3.1 Pro Preview', - provider: 'aionly', - group: 'Google' - }, - { - id: 'gemini-2.5-pro', - name: 'Gemini 2.5 Pro', - provider: 'aionly', - group: 'Google' - }, - { - id: 'gemini-2.5-flash', - name: 'Gemini 2.5 Flash', - provider: 'aionly', - group: 'Google' - } - ], - longcat: [ - { - id: 'LongCat-Flash-Chat', - name: 'LongCat Flash Chat', - provider: 'longcat', - group: 'LongCat' - }, - { - id: 'LongCat-Flash-Thinking', - name: 'LongCat Flash Thinking', - provider: 'longcat', - group: 'LongCat' - } - ], - huggingface: [], - gateway: [], - cerebras: [ - { - id: 'gpt-oss-120b', - name: 'GPT oss 120B', - provider: 'cerebras', - group: 'openai' - }, - { - id: 'zai-glm-4.6', - name: 'GLM 4.6', - provider: 'cerebras', - group: 'zai' - }, - { - id: 'qwen-3-235b-a22b-instruct-2507', - name: 'Qwen 3 235B A22B Instruct', - provider: 'cerebras', - group: 'qwen' - } - ], - mimo: [ - { - id: 'mimo-v2.5', - name: 'Mimo V2.5', - provider: 'mimo', - group: 'mimo' - }, - { - id: 'mimo-v2.5-pro', - name: 'Mimo V2.5 Pro', - provider: 'mimo', - group: 'mimo' - }, - { - id: 'mimo-v2-flash', - name: 'Mimo V2 Flash', - provider: 'mimo', - group: 'mimo' - }, - { - id: 'mimo-v2-omni', - name: 'Mimo V2 Omni', - provider: 'mimo', - group: 'mimo' - } - ], - zai: [ - { - id: 'glm-5', - provider: 'zai', - name: 'GLM-5', - group: 'GLM-5' - }, - { - id: 'glm-4.7', - provider: 'zai', - name: 'GLM-4.7', - group: 'GLM-4.7' - }, - { - id: 'glm-4.5-flash', - provider: 'zai', - name: 'GLM-4.5-Flash', - group: 'GLM-4.5' - }, - { - id: 'glm-4.6', - provider: 'zai', - name: 'GLM-4.6', - group: 'GLM-4.6' - }, - { - id: 'glm-4.6v', - provider: 'zai', - name: 'GLM-4.6V', - group: 'GLM-4.6V' - }, - { - id: 'glm-4.6v-flash', - provider: 'zai', - name: 'GLM-4.6V-Flash', - group: 'GLM-4.6V' - }, - { - id: 'glm-4.5', - provider: 'zai', - name: 'GLM-4.5', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5-air', - provider: 'zai', - name: 'GLM-4.5-Air', - group: 'GLM-4.5' - }, - { - id: 'glm-4.5v', - provider: 'zai', - name: 'GLM-4.5V', - group: 'GLM-4.5V' - } - ] -} diff --git a/src/renderer/config/models/index.ts b/src/renderer/config/models/index.ts deleted file mode 100644 index 23d887849a..0000000000 --- a/src/renderer/config/models/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from './default' -export * from './embedding' -export * from './logo' -export * from './openai' -export * from './qwen' -export * from './reasoning' -export * from './tooluse' -export * from './utils' -export * from './vision' -export * from './websearch' diff --git a/src/renderer/config/models/qwen.ts b/src/renderer/config/models/qwen.ts deleted file mode 100644 index 1df1a8b81e..0000000000 --- a/src/renderer/config/models/qwen.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { Model } from '@shared/data/types/model' -import { isQwen35to39Model as sharedIsQwen35to39Model, isQwenMTModel as sharedIsQwenMTModel } from '@shared/utils/model' - -export const isQwenMTModel = (model: Model): boolean => sharedIsQwenMTModel(model) - -export const isQwen35to39Model = (model?: Model): boolean => (model ? sharedIsQwen35to39Model(model) : false) diff --git a/src/renderer/hooks/__tests__/useConversationNavigation.test.tsx b/src/renderer/hooks/__tests__/useConversationNavigation.test.tsx index 79dcdeaf34..3f03a2d4af 100644 --- a/src/renderer/hooks/__tests__/useConversationNavigation.test.tsx +++ b/src/renderer/hooks/__tests__/useConversationNavigation.test.tsx @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { useConversationNavigation } from '../useConversationNavigation' -// Drive the boundary over a fake tabs context; config/sidebar (the identity↔url registry) +// Drive the boundary over a fake tabs context; utils/sidebar (the identity↔url registry) // runs for real, so these tests also lock the assistants/agents instanceKey wiring. const tabsMock = vi.hoisted(() => ({ ctx: null as ReturnType | null, diff --git a/src/renderer/hooks/tab/useTabSelfMetadata.ts b/src/renderer/hooks/tab/useTabSelfMetadata.ts index f1277daf7f..f6fc2fa732 100644 --- a/src/renderer/hooks/tab/useTabSelfMetadata.ts +++ b/src/renderer/hooks/tab/useTabSelfMetadata.ts @@ -1,6 +1,6 @@ -import { buildTabInstanceMetadata } from '@renderer/config/tabInstanceMetadata' import { isPageTitledRoute } from '@renderer/utils/routeTitle' import { emojiTabIcon } from '@renderer/utils/tabIcons' +import { buildTabInstanceMetadata } from '@renderer/utils/tabInstanceMetadata' import type { Tab } from '@shared/data/cache/cacheValueTypes' import type { TabInstanceAppId } from '@shared/types/tabInstanceMetadata' import { useEffect } from 'react' diff --git a/src/renderer/hooks/useAssistant.ts b/src/renderer/hooks/useAssistant.ts index ebbfdc6d50..25623447ae 100644 --- a/src/renderer/hooks/useAssistant.ts +++ b/src/renderer/hooks/useAssistant.ts @@ -21,7 +21,7 @@ import { usePreference } from '@data/hooks/usePreference' import { loggerService } from '@logger' import { useModelById } from '@renderer/hooks/useModel' import type { Assistant, AssistantSettings } from '@renderer/types/assistant' -import { reconcileReasoningEffortForModel, reconcileWebSearchForModel } from '@renderer/utils/modelReconcile' +import { reconcileReasoningEffortForModel, reconcileWebSearchForModel } from '@renderer/utils/model' import type { ConcreteApiPaths } from '@shared/data/api/apiTypes' import type { CreateAssistantDto, UpdateAssistantDto } from '@shared/data/api/schemas/assistants' import type { Model } from '@shared/data/types/model' diff --git a/src/renderer/hooks/useConversationNavigation.ts b/src/renderer/hooks/useConversationNavigation.ts index 3dd6ce6e40..021c193276 100644 --- a/src/renderer/hooks/useConversationNavigation.ts +++ b/src/renderer/hooks/useConversationNavigation.ts @@ -3,13 +3,13 @@ import { type ResourceListRevealSource } from '@renderer/components/chat/resources/resourceListRevealEvents' import { useWindowFrame } from '@renderer/components/chat/shell/WindowFrameContext' +import { type TabsContextValue, useOptionalTabsContext } from '@renderer/hooks/tab' import { buildSidebarAppOpenMetadata, getSidebarApp, getSidebarAppTabInstanceKey, tabBelongsToApp -} from '@renderer/config/sidebar' -import { type TabsContextValue, useOptionalTabsContext } from '@renderer/hooks/tab' +} from '@renderer/utils/sidebar' import type { SidebarIcon } from '@shared/data/preference/preferenceTypes' import { IpcChannel } from '@shared/IpcChannel' import { useMemo } from 'react' diff --git a/src/renderer/pages/agents/AgentPage.tsx b/src/renderer/pages/agents/AgentPage.tsx index f04726b7e9..784af89d68 100644 --- a/src/renderer/pages/agents/AgentPage.tsx +++ b/src/renderer/pages/agents/AgentPage.tsx @@ -8,7 +8,6 @@ import { createRecentSessionEntryFromSession, upsertGlobalSearchRecentEntry } from '@renderer/components/GlobalSearch/globalSearchGroups' -import { getTabInstanceKey } from '@renderer/config/tabInstanceMetadata' import { usePersistCache } from '@renderer/data/hooks/useCache' import { useInvalidateCache } from '@renderer/data/hooks/useDataApi' import { useAgent, useAgents } from '@renderer/hooks/agents/useAgent' @@ -21,6 +20,7 @@ import { buildAgentSessionTopicId } from '@renderer/utils/agentSession' import { formatErrorMessageWithPrefix } from '@renderer/utils/error' import { getDefaultRouteTitle } from '@renderer/utils/routeTitle' import { cn } from '@renderer/utils/style' +import { getTabInstanceKey } from '@renderer/utils/tabInstanceMetadata' import type { AgentSessionEntity } from '@shared/data/api/schemas/agentSessions' import { AGENT_WORKSPACE_TYPE, type AgentSessionWorkspaceSource } from '@shared/data/api/schemas/agentWorkspaces' import { MIN_WINDOW_HEIGHT, SECOND_MIN_WINDOW_WIDTH } from '@shared/utils/window' diff --git a/src/renderer/pages/code/CodeCliPage.tsx b/src/renderer/pages/code/CodeCliPage.tsx index cc2cb03598..4d9e172a8b 100644 --- a/src/renderer/pages/code/CodeCliPage.tsx +++ b/src/renderer/pages/code/CodeCliPage.tsx @@ -18,7 +18,6 @@ import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' // resolved by tsgo on main's program (resolves on feat's); transitional, reverts // to the barrel once main converges with feat. import { ModelSelector } from '@renderer/components/Selector/model' -import { CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS, isSiliconAnthropicCompatibleModel } from '@renderer/config/codeProviders' import { isMac, isWin } from '@renderer/config/constant' import { usePersistCache } from '@renderer/data/hooks/useCache' import { useCodeCli } from '@renderer/hooks/useCodeCli' @@ -26,6 +25,10 @@ import { useModels } from '@renderer/hooks/useModel' import { getProviderDisplayName, useProviders } from '@renderer/hooks/useProvider' import { useTimer } from '@renderer/hooks/useTimer' import { ipcApi } from '@renderer/ipc' +import { + CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS, + isSiliconAnthropicCompatibleModel +} from '@renderer/pages/code/codeProviders' import { loggerService } from '@renderer/services/LoggerService' import { EFFORT_RATIO } from '@renderer/types/reasoning' import { getThinkingBudget } from '@shared/ai/reasoningBudget' diff --git a/src/renderer/config/codeProviders.ts b/src/renderer/pages/code/codeProviders.ts similarity index 100% rename from src/renderer/config/codeProviders.ts rename to src/renderer/pages/code/codeProviders.ts diff --git a/src/renderer/pages/code/index.ts b/src/renderer/pages/code/index.ts index df72937fec..892f92030d 100644 --- a/src/renderer/pages/code/index.ts +++ b/src/renderer/pages/code/index.ts @@ -9,7 +9,7 @@ import { QoderCli, QwenCode } from '@cherrystudio/ui/icons' -import { CLAUDE_SUPPORTED_PROVIDERS } from '@renderer/config/codeProviders' +import { CLAUDE_SUPPORTED_PROVIDERS } from '@renderer/pages/code/codeProviders' import { formatApiHost } from '@renderer/utils/api' import { sanitizeProviderName } from '@renderer/utils/naming' import type { EndpointType } from '@shared/data/types/model' diff --git a/src/renderer/pages/home/HomePage.tsx b/src/renderer/pages/home/HomePage.tsx index 071468ef63..6737b1677e 100644 --- a/src/renderer/pages/home/HomePage.tsx +++ b/src/renderer/pages/home/HomePage.tsx @@ -16,7 +16,6 @@ import { createRecentTopicEntryFromTopic, upsertGlobalSearchRecentEntry } from '@renderer/components/GlobalSearch/globalSearchGroups' -import { getTabInstanceKey } from '@renderer/config/tabInstanceMetadata' import { usePersistCache } from '@renderer/data/hooks/useCache' import { useCommandHandler } from '@renderer/hooks/command' import { useCurrentTab, useCurrentTabId, useIsActiveTab, useTabSelfMetadata } from '@renderer/hooks/tab' @@ -29,6 +28,7 @@ import type { FileMetadata } from '@renderer/types/file' import type { Topic } from '@renderer/types/topic' import { getDefaultRouteTitle } from '@renderer/utils/routeTitle' import { cn } from '@renderer/utils/style' +import { getTabInstanceKey } from '@renderer/utils/tabInstanceMetadata' import type { CherryMessagePart } from '@shared/data/types/message' import type { UniqueModelId } from '@shared/data/types/model' import { MIN_WINDOW_HEIGHT, SECOND_MIN_WINDOW_WIDTH } from '@shared/utils/window' diff --git a/src/renderer/pages/home/messages/__tests__/homeMessageListAdapter.test.tsx b/src/renderer/pages/home/messages/__tests__/homeMessageListAdapter.test.tsx index fc773bd8ed..78fd571015 100644 --- a/src/renderer/pages/home/messages/__tests__/homeMessageListAdapter.test.tsx +++ b/src/renderer/pages/home/messages/__tests__/homeMessageListAdapter.test.tsx @@ -62,14 +62,10 @@ vi.mock('@renderer/components/Selector', () => ({ ModelSelector: ({ trigger }: { trigger: ReactNode }) => <>{trigger} })) -vi.mock('@renderer/config/models', () => ({ +vi.mock('@renderer/utils/model', () => ({ isVisionModel: vi.fn(() => false) })) -vi.mock('@renderer/config/models/_bridge', () => ({ - toSharedCompatModel: vi.fn(() => undefined) -})) - vi.mock('@renderer/components/chat/messages/editing/MessageEditingContext', () => ({ useMessageEditing: () => ({ editingMessageId: null, editingMessage: null, startEditing: vi.fn() }) })) diff --git a/src/renderer/pages/home/messages/homeMessageListAdapter.tsx b/src/renderer/pages/home/messages/homeMessageListAdapter.tsx index 03fe262012..dd2b7c8bea 100644 --- a/src/renderer/pages/home/messages/homeMessageListAdapter.tsx +++ b/src/renderer/pages/home/messages/homeMessageListAdapter.tsx @@ -33,7 +33,6 @@ import { toMessageListItem } from '@renderer/components/chat/messages/utils/messageListItem' import { ModelSelector } from '@renderer/components/Selector' -import { isVisionModel } from '@renderer/config/models' import { useChatWrite } from '@renderer/hooks/chat/ChatWriteContext' import { useCommandHandler } from '@renderer/hooks/command' import { SiblingsContext } from '@renderer/hooks/SiblingsContext' @@ -45,6 +44,7 @@ import { formatErrorMessageWithPrefix, isAbortError } from '@renderer/utils/erro import { updateCodeBlock } from '@renderer/utils/markdown' import { createComposerRichClipboardContentFromParts } from '@renderer/utils/message/composerClipboard' import { getComposerTextFromParts } from '@renderer/utils/message/composerTokens' +import { isVisionModel } from '@renderer/utils/model' import { translateText } from '@renderer/utils/translate' import type { TranslateLangCode } from '@shared/data/preference/preferenceTypes' import type { CherryMessagePart, CherryUIMessage, ModelSnapshot } from '@shared/data/types/message' diff --git a/src/renderer/pages/launchpad/LaunchpadPage.tsx b/src/renderer/pages/launchpad/LaunchpadPage.tsx index 53eb508811..1674710000 100644 --- a/src/renderer/pages/launchpad/LaunchpadPage.tsx +++ b/src/renderer/pages/launchpad/LaunchpadPage.tsx @@ -1,17 +1,17 @@ import { usePreference } from '@data/hooks/usePreference' +import { SIDEBAR_ICON_COMPONENTS } from '@renderer/components/app/sidebarIcons' import { CommandContextMenu, type CommandContextMenuExtraItem } from '@renderer/components/command' import App from '@renderer/components/MiniApp/MiniApp' import Scrollbar from '@renderer/components/Scrollbar' +import { useMiniApps } from '@renderer/hooks/useMiniApps' +import { getSidebarIconLabelKey } from '@renderer/i18n/label' import { getRequiredSidebarIconsVisible, getSidebarMenuPath, REQUIRED_SIDEBAR_ICONS, sanitizeSidebarIcons, - SIDEBAR_ICON_COMPONENTS, SIDEBAR_ICON_ORDER -} from '@renderer/config/sidebar' -import { useMiniApps } from '@renderer/hooks/useMiniApps' -import { getSidebarIconLabelKey } from '@renderer/i18n/label' +} from '@renderer/utils/sidebar' import type { SidebarFavorite, SidebarIcon } from '@shared/data/preference/preferenceTypes' import type { MiniApp as MiniAppType } from '@shared/data/types/miniApp' import { useNavigate } from '@tanstack/react-router' diff --git a/src/renderer/pages/mini-apps/MiniAppPage.tsx b/src/renderer/pages/mini-apps/MiniAppPage.tsx index 0483844bf6..8eea5a494c 100644 --- a/src/renderer/pages/mini-apps/MiniAppPage.tsx +++ b/src/renderer/pages/mini-apps/MiniAppPage.tsx @@ -1,6 +1,6 @@ import { loggerService } from '@logger' import { LogoAvatar } from '@renderer/components/Icons' -import { getMiniAppsLogo } from '@renderer/config/miniApps' +import { getMiniAppsLogo } from '@renderer/components/Icons/miniAppsLogo' import { useCurrentTab, useCurrentTabId } from '@renderer/hooks/tab' import { useOptionalTabsContext } from '@renderer/hooks/tab' import { useMiniAppPopup } from '@renderer/hooks/useMiniAppPopup' diff --git a/src/renderer/pages/mini-apps/MiniAppSettings/MiniAppListColumn.tsx b/src/renderer/pages/mini-apps/MiniAppSettings/MiniAppListColumn.tsx index aa28071063..644c84f5df 100644 --- a/src/renderer/pages/mini-apps/MiniAppSettings/MiniAppListColumn.tsx +++ b/src/renderer/pages/mini-apps/MiniAppSettings/MiniAppListColumn.tsx @@ -1,6 +1,6 @@ import { Scrollbar, Sortable, Tooltip } from '@cherrystudio/ui' import { LogoAvatar } from '@renderer/components/Icons' -import { getMiniAppsLogo } from '@renderer/config/miniApps' +import { getMiniAppsLogo } from '@renderer/components/Icons/miniAppsLogo' import type { MiniApp } from '@shared/data/types/miniApp' import { ArrowLeftToLine, ArrowRightToLine } from 'lucide-react' import type { FC } from 'react' diff --git a/src/renderer/pages/mini-apps/MiniAppSettings/__tests__/MiniAppListColumn.test.tsx b/src/renderer/pages/mini-apps/MiniAppSettings/__tests__/MiniAppListColumn.test.tsx index b85383cb80..a249bf47ab 100644 --- a/src/renderer/pages/mini-apps/MiniAppSettings/__tests__/MiniAppListColumn.test.tsx +++ b/src/renderer/pages/mini-apps/MiniAppSettings/__tests__/MiniAppListColumn.test.tsx @@ -30,7 +30,7 @@ vi.mock('@renderer/components/Icons', () => ({ LogoAvatar: ({ logo }: { logo: string }) => })) -vi.mock('@renderer/config/miniApps', () => ({ +vi.mock('@renderer/components/Icons/miniAppsLogo', () => ({ getMiniAppsLogo: () => undefined })) diff --git a/src/renderer/pages/mini-apps/NewMiniAppPanel.tsx b/src/renderer/pages/mini-apps/NewMiniAppPanel.tsx index 6c27c6ce4e..85f7092d7c 100644 --- a/src/renderer/pages/mini-apps/NewMiniAppPanel.tsx +++ b/src/renderer/pages/mini-apps/NewMiniAppPanel.tsx @@ -12,7 +12,7 @@ import { } from '@cherrystudio/ui' import { loggerService } from '@logger' import { LogoAvatar } from '@renderer/components/Icons' -import { getMiniAppsLogo } from '@renderer/config/miniApps' +import { getMiniAppsLogo } from '@renderer/components/Icons/miniAppsLogo' import { useMiniApps } from '@renderer/hooks/useMiniApps' import { compressImage, convertToBase64 } from '@renderer/utils/image' import { uuid } from '@renderer/utils/uuid' diff --git a/src/renderer/pages/mini-apps/__tests__/NewMiniAppPanel.test.tsx b/src/renderer/pages/mini-apps/__tests__/NewMiniAppPanel.test.tsx index 1109213ef0..11cc4789ce 100644 --- a/src/renderer/pages/mini-apps/__tests__/NewMiniAppPanel.test.tsx +++ b/src/renderer/pages/mini-apps/__tests__/NewMiniAppPanel.test.tsx @@ -29,7 +29,7 @@ vi.mock('@renderer/components/Icons', () => ({ LogoAvatar: ({ logo }: { logo: unknown }) => miniapp-logo-preview })) -vi.mock('@renderer/config/miniApps', () => ({ +vi.mock('@renderer/components/Icons/miniAppsLogo', () => ({ getMiniAppsLogo: (logo?: string) => (logo === 'application' ? 'application-icon' : undefined) })) diff --git a/src/renderer/config/ocr.ts b/src/renderer/pages/settings/FileProcessingSettings/ocr.ts similarity index 98% rename from src/renderer/config/ocr.ts rename to src/renderer/pages/settings/FileProcessingSettings/ocr.ts index b27a8b717e..6cf6d3c015 100644 --- a/src/renderer/config/ocr.ts +++ b/src/renderer/pages/settings/FileProcessingSettings/ocr.ts @@ -1,3 +1,4 @@ +import { isMac, isWin } from '@renderer/config/constant' import type { BuiltinOcrProvider, BuiltinOcrProviderId, @@ -10,8 +11,6 @@ import type { } from '@renderer/types/ocr' import { parseTranslateLangCode } from '@shared/data/preference/preferenceTypes' -import { isMac, isWin } from './constant' - const tesseract: OcrTesseractProvider = { id: 'tesseract', name: 'Tesseract', diff --git a/src/renderer/pages/settings/FileProcessingSettings/utils/fileProcessingMeta.ts b/src/renderer/pages/settings/FileProcessingSettings/utils/fileProcessingMeta.ts index a3ca3cba85..bd8ca0e37d 100644 --- a/src/renderer/pages/settings/FileProcessingSettings/utils/fileProcessingMeta.ts +++ b/src/renderer/pages/settings/FileProcessingSettings/utils/fileProcessingMeta.ts @@ -1,7 +1,7 @@ import type { CompoundIcon } from '@cherrystudio/ui' import { Application, Doc2x, Intel, Mineru, Mistral, Paddleocr, TesseractJs } from '@cherrystudio/ui/icons' import { isWin } from '@renderer/config/constant' -import { TESSERACT_LANG_MAP } from '@renderer/config/ocr' +import { TESSERACT_LANG_MAP } from '@renderer/pages/settings/FileProcessingSettings/ocr' import type { FileProcessorFeature, FileProcessorId } from '@shared/data/preference/preferenceTypes' import type { FileProcessorFeatureCapability, FileProcessorMerged } from '@shared/data/presets/fileProcessing' diff --git a/src/renderer/pages/settings/McpSettings/BuiltinMcpServerList.tsx b/src/renderer/pages/settings/McpSettings/BuiltinMcpServerList.tsx index f08dce71a6..347e46e572 100644 --- a/src/renderer/pages/settings/McpSettings/BuiltinMcpServerList.tsx +++ b/src/renderer/pages/settings/McpSettings/BuiltinMcpServerList.tsx @@ -1,8 +1,8 @@ import { Badge, Button, Popover, PopoverContent, PopoverTrigger, Tabs, TabsList, TabsTrigger } from '@cherrystudio/ui' import CollapsibleSearchBar from '@renderer/components/CollapsibleSearchBar' -import { builtinMcpServers } from '@renderer/config/builtinMcpServers' import { useMcpServers } from '@renderer/hooks/useMcpServer' import { getBuiltInMcpServerDescriptionLabelKey } from '@renderer/i18n/label' +import { builtinMcpServers } from '@renderer/pages/settings/McpSettings/builtinMcpServers' import { cn } from '@renderer/utils/style' import { Check, Plus } from 'lucide-react' import type { FC } from 'react' diff --git a/src/renderer/config/builtinMcpServers.ts b/src/renderer/pages/settings/McpSettings/builtinMcpServers.ts similarity index 100% rename from src/renderer/config/builtinMcpServers.ts rename to src/renderer/pages/settings/McpSettings/builtinMcpServers.ts diff --git a/src/renderer/pages/settings/ProviderSettings/ModelList/HealthCheckDrawer.tsx b/src/renderer/pages/settings/ProviderSettings/ModelList/HealthCheckDrawer.tsx index dcb617949d..1bdc03f32c 100644 --- a/src/renderer/pages/settings/ProviderSettings/ModelList/HealthCheckDrawer.tsx +++ b/src/renderer/pages/settings/ProviderSettings/ModelList/HealthCheckDrawer.tsx @@ -10,7 +10,6 @@ import { Tooltip } from '@cherrystudio/ui' import Scrollbar from '@renderer/components/Scrollbar' -import { getModelLogo } from '@renderer/config/models' import type { ModelHealthCheckGenerationOutput, ModelHealthCheckSkipReason, @@ -18,6 +17,7 @@ import type { } from '@renderer/pages/settings/ProviderSettings/types/healthCheck' import { HealthStatus } from '@renderer/pages/settings/ProviderSettings/types/healthCheck' import { maskApiKey } from '@renderer/utils/api' +import { getModelLogo } from '@renderer/utils/model' import { cn } from '@renderer/utils/style' import { CheckCircle2, Info, Loader2, XCircle } from 'lucide-react' import { type ReactNode, useEffect, useMemo, useState } from 'react' diff --git a/src/renderer/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx b/src/renderer/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx index 32cb6987ab..f51ede8db7 100644 --- a/src/renderer/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx +++ b/src/renderer/pages/settings/ProviderSettings/ModelList/ModelListItem.tsx @@ -1,5 +1,5 @@ import { Avatar, AvatarFallback, Button, RowFlex, Switch, Tooltip } from '@cherrystudio/ui' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogo } from '@renderer/utils/model' import { cn } from '@renderer/utils/style' import type { Model } from '@shared/data/types/model' import { Settings, Trash2 } from 'lucide-react' diff --git a/src/renderer/pages/settings/ProviderSettings/ModelList/ModelSyncPreviewPanel.tsx b/src/renderer/pages/settings/ProviderSettings/ModelList/ModelSyncPreviewPanel.tsx index 09316dc52a..336a9e147d 100644 --- a/src/renderer/pages/settings/ProviderSettings/ModelList/ModelSyncPreviewPanel.tsx +++ b/src/renderer/pages/settings/ProviderSettings/ModelList/ModelSyncPreviewPanel.tsx @@ -1,5 +1,5 @@ import { Alert, Button, Checkbox } from '@cherrystudio/ui' -import { getModelLogo } from '@renderer/config/models' +import { getModelLogo } from '@renderer/utils/model' import type { Model } from '@shared/data/types/model' import { parseUniqueModelId, type UniqueModelId } from '@shared/data/types/model' import { CheckCircle2, Plus, Trash2 } from 'lucide-react' diff --git a/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListItem.test.tsx b/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListItem.test.tsx index c7db48b843..c2b005b3b2 100644 --- a/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListItem.test.tsx +++ b/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListItem.test.tsx @@ -46,7 +46,7 @@ vi.mock('@cherrystudio/ui', async (importOriginal) => { } }) -vi.mock('@renderer/config/models', async (importOriginal) => ({ +vi.mock('@renderer/utils/model', async (importOriginal) => ({ ...(await importOriginal()), getModelLogo: () => null })) diff --git a/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListSyncDrawer.test.tsx b/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListSyncDrawer.test.tsx index f7ee1bd7c7..9f9648be89 100644 --- a/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListSyncDrawer.test.tsx +++ b/src/renderer/pages/settings/ProviderSettings/ModelList/__tests__/ModelListSyncDrawer.test.tsx @@ -1,3 +1,4 @@ +import type * as ModelModule from '@renderer/utils/model' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' @@ -46,7 +47,8 @@ vi.mock('@cherrystudio/ui', async (importOriginal) => { } }) -vi.mock('@renderer/config/models', () => ({ +vi.mock('@renderer/utils/model', async (importOriginal) => ({ + ...(await importOriginal()), getModelLogo: () => null })) diff --git a/src/renderer/pages/settings/ProviderSettings/ModelList/utils.ts b/src/renderer/pages/settings/ProviderSettings/ModelList/utils.ts index b1a32bf8b5..6c7d6be421 100644 --- a/src/renderer/pages/settings/ProviderSettings/ModelList/utils.ts +++ b/src/renderer/pages/settings/ProviderSettings/ModelList/utils.ts @@ -1,4 +1,4 @@ -import { getSearchMatchScore } from '@renderer/utils/modelSearch' +import { getSearchMatchScore } from '@renderer/utils/model' import type { Model } from '@shared/data/types/model' export const isValidNewApiModel = (model: Model): boolean => !!(model.endpointTypes && model.endpointTypes.length > 0) diff --git a/src/renderer/pages/settings/ProviderSettings/ProviderSpecific/ProviderOauth.tsx b/src/renderer/pages/settings/ProviderSettings/ProviderSpecific/ProviderOauth.tsx index 15c101f150..a7021aff2c 100644 --- a/src/renderer/pages/settings/ProviderSettings/ProviderSpecific/ProviderOauth.tsx +++ b/src/renderer/pages/settings/ProviderSettings/ProviderSpecific/ProviderOauth.tsx @@ -1,10 +1,10 @@ import { Button, RowFlex } from '@cherrystudio/ui' import { resolveProviderIcon } from '@cherrystudio/ui/icons' import OauthButton from '@renderer/components/Oauth/OauthButton' -import { PROVIDER_URLS } from '@renderer/config/providers' import { useProvider } from '@renderer/hooks/useProvider' import { getProviderLabelKey } from '@renderer/i18n/label' import { oauthCardClasses } from '@renderer/pages/settings/ProviderSettings/primitives/ProviderSettingsPrimitives' +import { PROVIDER_URLS } from '@renderer/pages/settings/ProviderSettings/providerUrls' import { providerBills, providerCharge } from '@renderer/utils/oauth' import { hasApiKeys } from '@shared/utils/provider' import { CircleDollarSign, ReceiptText } from 'lucide-react' diff --git a/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderEndpointActions.ts b/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderEndpointActions.ts index 08efd5ee82..756bd77f9b 100644 --- a/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderEndpointActions.ts +++ b/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderEndpointActions.ts @@ -1,5 +1,5 @@ import { loggerService } from '@logger' -import { PROVIDER_URLS } from '@renderer/config/providers' +import { PROVIDER_URLS } from '@renderer/pages/settings/ProviderSettings/providerUrls' import { validateApiHost } from '@renderer/utils/api' import { ErrorCode, isDataApiError, isSerializedDataApiError, toDataApiError } from '@shared/data/api' import { ENDPOINT_TYPE } from '@shared/data/types/model' diff --git a/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderHostPreview.ts b/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderHostPreview.ts index 5bc5072306..f39654b6cd 100644 --- a/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderHostPreview.ts +++ b/src/renderer/pages/settings/ProviderSettings/hooks/providerSetting/useProviderHostPreview.ts @@ -1,5 +1,5 @@ -import { PROVIDER_URLS } from '@renderer/config/providers' import { useProviderAuthConfig } from '@renderer/hooks/useProvider' +import { PROVIDER_URLS } from '@renderer/pages/settings/ProviderSettings/providerUrls' import type { Provider } from '@shared/data/types/provider' import { getProviderHostTopology } from '@shared/utils/providerTopology' import { useMemo } from 'react' diff --git a/src/renderer/config/providers.ts b/src/renderer/pages/settings/ProviderSettings/providerUrls.ts similarity index 55% rename from src/renderer/config/providers.ts rename to src/renderer/pages/settings/ProviderSettings/providerUrls.ts index 4c60063936..18a60b522d 100644 --- a/src/renderer/config/providers.ts +++ b/src/renderer/pages/settings/ProviderSettings/providerUrls.ts @@ -1,686 +1,5 @@ -import { OpenAIServiceTiers, type SystemProvider, type SystemProviderId } from '@renderer/types/provider' -import { CHERRYAI_API_BASE_URL, CHERRYAI_PROVIDER_ID, CHERRYAI_PROVIDER_NAME } from '@shared/data/presets/cherryai' - -import { TOKENFLUX_HOST } from './constant' -import { qwenModel, SYSTEM_MODELS } from './models' - -export const CHERRYAI_PROVIDER: SystemProvider = { - id: CHERRYAI_PROVIDER_ID as SystemProviderId, - name: CHERRYAI_PROVIDER_NAME, - type: 'openai', - apiKey: '', - apiHost: CHERRYAI_API_BASE_URL, - models: [qwenModel], - isSystem: true, - enabled: true -} - -export const SYSTEM_PROVIDERS_CONFIG: Record = { - cherryin: { - id: 'cherryin', - name: 'CherryIN', - type: 'openai', - apiKey: '', - apiHost: 'https://open.cherryin.cc', - anthropicApiHost: 'https://open.cherryin.cc', - models: [], - isSystem: true, - enabled: true - }, - silicon: { - id: 'silicon', - name: 'Silicon', - type: 'openai', - apiKey: '', - apiHost: 'https://api.siliconflow.cn', - anthropicApiHost: 'https://api.siliconflow.cn', - models: SYSTEM_MODELS.silicon, - isSystem: true, - enabled: false - }, - aihubmix: { - id: 'aihubmix', - name: 'AiHubMix', - type: 'openai', - apiKey: '', - apiHost: 'https://aihubmix.com', - anthropicApiHost: 'https://aihubmix.com', - models: SYSTEM_MODELS.aihubmix, - isSystem: true, - enabled: false - }, - ovms: { - id: 'ovms', - name: 'OpenVINO Model Server', - type: 'openai', - apiKey: '', - apiHost: 'http://localhost:8000/v3/', - models: SYSTEM_MODELS.ovms, - isSystem: true, - enabled: false - }, - ocoolai: { - id: 'ocoolai', - name: 'ocoolAI', - type: 'openai', - apiKey: '', - apiHost: 'https://api.ocoolai.com', - models: SYSTEM_MODELS.ocoolai, - isSystem: true, - enabled: false - }, - zhipu: { - id: 'zhipu', - name: 'ZhiPu', - type: 'openai', - apiKey: '', - apiHost: 'https://open.bigmodel.cn/api/paas/v4/', - anthropicApiHost: 'https://open.bigmodel.cn/api/anthropic', - models: SYSTEM_MODELS.zhipu, - isSystem: true, - enabled: false - }, - zai: { - id: 'zai', - name: 'Z.ai', - type: 'openai', - apiKey: '', - apiHost: 'https://api.z.ai/api/paas/v4/', - anthropicApiHost: 'https://api.z.ai/api/anthropic', - models: SYSTEM_MODELS.zai, - isSystem: true, - enabled: false - }, - deepseek: { - id: 'deepseek', - name: 'deepseek', - type: 'openai', - apiKey: '', - apiHost: 'https://api.deepseek.com', - anthropicApiHost: 'https://api.deepseek.com/anthropic', - models: SYSTEM_MODELS.deepseek, - isSystem: true, - enabled: false - }, - alayanew: { - id: 'alayanew', - name: 'AlayaNew', - type: 'openai', - apiKey: '', - apiHost: 'https://deepseek.alayanew.com', - models: SYSTEM_MODELS.alayanew, - isSystem: true, - enabled: false - }, - dmxapi: { - id: 'dmxapi', - name: 'DMXAPI', - type: 'openai', - apiKey: '', - apiHost: 'https://www.dmxapi.cn', - anthropicApiHost: 'https://www.dmxapi.cn', - models: SYSTEM_MODELS.dmxapi, - isSystem: true, - enabled: false - }, - aionly: { - id: 'aionly', - name: 'AIOnly', - type: 'openai', - apiKey: '', - apiHost: 'https://api.aiionly.com', - models: SYSTEM_MODELS.aionly, - isSystem: true, - enabled: false - }, - burncloud: { - id: 'burncloud', - name: 'BurnCloud', - type: 'openai', - apiKey: '', - apiHost: 'https://ai.burncloud.com', - models: SYSTEM_MODELS.burncloud, - isSystem: true, - enabled: false - }, - tokenflux: { - id: 'tokenflux', - name: 'TokenFlux', - type: 'openai', - apiKey: '', - apiHost: 'https://api.tokenflux.ai/openai/v1', - anthropicApiHost: 'https://api.tokenflux.ai/anthropic', - models: SYSTEM_MODELS.tokenflux, - isSystem: true, - enabled: false - }, - '302ai': { - id: '302ai', - name: '302.AI', - type: 'openai', - apiKey: '', - apiHost: 'https://api.302.ai', - anthropicApiHost: 'https://api.302.ai', - models: SYSTEM_MODELS['302ai'], - isSystem: true, - enabled: false - }, - cephalon: { - id: 'cephalon', - name: 'Cephalon', - type: 'openai', - apiKey: '', - apiHost: 'https://cephalon.cloud/user-center/v1/model', - models: SYSTEM_MODELS.cephalon, - isSystem: true, - enabled: false - }, - lanyun: { - id: 'lanyun', - name: 'LANYUN', - type: 'openai', - apiKey: '', - apiHost: 'https://maas-api.lanyun.net', - models: SYSTEM_MODELS.lanyun, - isSystem: true, - enabled: false - }, - ph8: { - id: 'ph8', - name: 'PH8', - type: 'openai', - apiKey: '', - apiHost: 'https://ph8.co', - models: SYSTEM_MODELS.ph8, - isSystem: true, - enabled: false - }, - sophnet: { - id: 'sophnet', - name: 'SophNet', - type: 'openai', - apiKey: '', - apiHost: 'https://www.sophnet.com/api/open-apis/v1', - models: [], - isSystem: true, - enabled: false - }, - ppio: { - id: 'ppio', - name: 'PPIO', - type: 'openai', - apiKey: '', - apiHost: 'https://api.ppinfra.com/v3/openai/', - models: SYSTEM_MODELS.ppio, - isSystem: true, - enabled: false - }, - dashscope: { - id: 'dashscope', - name: 'Bailian', - type: 'openai', - apiKey: '', - apiHost: 'https://dashscope.aliyuncs.com/compatible-mode/v1/', - anthropicApiHost: 'https://dashscope.aliyuncs.com/apps/anthropic', - models: SYSTEM_MODELS.dashscope, - isSystem: true, - enabled: false - }, - minimax: { - id: 'minimax', - name: 'MiniMax', - type: 'openai', - apiKey: '', - apiHost: 'https://api.minimaxi.com/v1/', - anthropicApiHost: 'https://api.minimaxi.com/anthropic', - models: SYSTEM_MODELS.minimax, - isSystem: true, - enabled: false - }, - 'minimax-global': { - id: 'minimax-global', - name: 'MiniMax Global', - type: 'openai', - apiKey: '', - apiHost: 'https://api.minimax.io/v1/', - anthropicApiHost: 'https://api.minimax.io/anthropic', - models: SYSTEM_MODELS['minimax-global'], - isSystem: true, - enabled: false - }, - moonshot: { - id: 'moonshot', - name: 'Moonshot AI', - type: 'openai', - apiKey: '', - apiHost: 'https://api.moonshot.cn', - anthropicApiHost: 'https://api.moonshot.cn/anthropic', - models: SYSTEM_MODELS.moonshot, - isSystem: true, - enabled: false - }, - qiniu: { - id: 'qiniu', - name: 'Qiniu', - type: 'openai', - apiKey: '', - apiHost: 'https://api.qnaigc.com', - anthropicApiHost: 'https://api.qnaigc.com', - models: SYSTEM_MODELS.qiniu, - isSystem: true, - enabled: false - }, - openrouter: { - id: 'openrouter', - name: 'OpenRouter', - type: 'openai', - apiKey: '', - apiHost: 'https://openrouter.ai/api/v1/', - // Anthropic-compatible endpoint for Agent mode (Claude Code SDK) - // https://openrouter.ai/docs/guides/guides/coding-agents/claude-code-integration - anthropicApiHost: 'https://openrouter.ai/api', - models: SYSTEM_MODELS.openrouter, - isSystem: true, - enabled: false - }, - 'new-api': { - id: 'new-api', - name: 'New API', - type: 'new-api', - apiKey: '', - apiHost: 'http://localhost:3000', - anthropicApiHost: 'http://localhost:3000', - models: SYSTEM_MODELS['new-api'], - isSystem: true, - enabled: false - }, - ollama: { - id: 'ollama', - name: 'Ollama', - type: 'ollama', - apiKey: '', - apiHost: 'http://localhost:11434', - anthropicApiHost: 'http://localhost:11434', - models: SYSTEM_MODELS.ollama, - isSystem: true, - enabled: false - }, - lmstudio: { - id: 'lmstudio', - name: 'LM Studio', - type: 'openai', - apiKey: '', - apiHost: 'http://localhost:1234', - anthropicApiHost: 'http://localhost:1234', - models: SYSTEM_MODELS.lmstudio, - isSystem: true, - enabled: false - }, - anthropic: { - id: 'anthropic', - name: 'Anthropic', - type: 'anthropic', - apiKey: '', - apiHost: 'https://api.anthropic.com', - models: SYSTEM_MODELS.anthropic, - isSystem: true, - enabled: false - }, - openai: { - id: 'openai', - name: 'OpenAI', - type: 'openai-response', - apiKey: '', - apiHost: 'https://api.openai.com', - models: SYSTEM_MODELS.openai, - isSystem: true, - enabled: false, - serviceTier: OpenAIServiceTiers.auto - }, - 'azure-openai': { - id: 'azure-openai', - name: 'Azure OpenAI', - type: 'azure-openai', - apiKey: '', - apiHost: '', - apiVersion: '', - models: SYSTEM_MODELS['azure-openai'], - isSystem: true, - enabled: false - }, - gemini: { - id: 'gemini', - name: 'Gemini', - type: 'gemini', - apiKey: '', - apiHost: 'https://generativelanguage.googleapis.com', - models: SYSTEM_MODELS.gemini, - isSystem: true, - enabled: false, - isVertex: false - }, - vertexai: { - id: 'vertexai', - name: 'VertexAI', - type: 'vertexai', - apiKey: '', - apiHost: '', - models: SYSTEM_MODELS.vertexai, - isSystem: true, - enabled: false, - isVertex: true - }, - github: { - id: 'github', - name: 'Github Models', - type: 'openai', - apiKey: '', - apiHost: 'https://models.github.ai/inference', - models: SYSTEM_MODELS.github, - isSystem: true, - enabled: false - }, - copilot: { - id: 'copilot', - name: 'Github Copilot', - type: 'openai', - apiKey: '', - apiHost: 'https://api.githubcopilot.com/', - models: SYSTEM_MODELS.copilot, - isSystem: true, - enabled: false, - isAuthed: false - }, - doubao: { - id: 'doubao', - name: 'doubao', - type: 'openai', - apiKey: '', - apiHost: 'https://ark.cn-beijing.volces.com/api/v3/', - models: SYSTEM_MODELS.doubao, - isSystem: true, - enabled: false - }, - baichuan: { - id: 'baichuan', - name: 'BAICHUAN AI', - type: 'openai', - apiKey: '', - apiHost: 'https://api.baichuan-ai.com', - models: SYSTEM_MODELS.baichuan, - isSystem: true, - enabled: false - }, - stepfun: { - id: 'stepfun', - name: 'StepFun', - type: 'openai', - apiKey: '', - apiHost: 'https://api.stepfun.com', - anthropicApiHost: 'https://api.stepfun.com', - models: SYSTEM_MODELS.stepfun, - isSystem: true, - enabled: false - }, - yi: { - id: 'yi', - name: 'Yi', - type: 'openai', - apiKey: '', - apiHost: 'https://api.lingyiwanwu.com', - models: SYSTEM_MODELS.yi, - isSystem: true, - enabled: false - }, - infini: { - id: 'infini', - name: 'Infini', - type: 'openai', - apiKey: '', - apiHost: 'https://cloud.infini-ai.com/maas', - models: SYSTEM_MODELS.infini, - isSystem: true, - enabled: false - }, - groq: { - id: 'groq', - name: 'Groq', - type: 'openai', - apiKey: '', - apiHost: 'https://api.groq.com/openai', - models: SYSTEM_MODELS.groq, - isSystem: true, - enabled: false - }, - together: { - id: 'together', - name: 'Together', - type: 'openai', - apiKey: '', - apiHost: 'https://api.together.xyz', - models: SYSTEM_MODELS.together, - isSystem: true, - enabled: false - }, - fireworks: { - id: 'fireworks', - name: 'Fireworks', - type: 'openai', - apiKey: '', - apiHost: 'https://api.fireworks.ai/inference', - models: SYSTEM_MODELS.fireworks, - isSystem: true, - enabled: false - }, - nvidia: { - id: 'nvidia', - name: 'nvidia', - type: 'openai', - apiKey: '', - apiHost: 'https://integrate.api.nvidia.com', - models: SYSTEM_MODELS.nvidia, - isSystem: true, - enabled: false - }, - grok: { - id: 'grok', - name: 'Grok', - type: 'openai', - apiKey: '', - apiHost: 'https://api.x.ai', - models: SYSTEM_MODELS.grok, - isSystem: true, - enabled: false - }, - hyperbolic: { - id: 'hyperbolic', - name: 'Hyperbolic', - type: 'openai', - apiKey: '', - apiHost: 'https://api.hyperbolic.xyz', - models: SYSTEM_MODELS.hyperbolic, - isSystem: true, - enabled: false - }, - mistral: { - id: 'mistral', - name: 'Mistral', - type: 'openai', - apiKey: '', - apiHost: 'https://api.mistral.ai', - models: SYSTEM_MODELS.mistral, - isSystem: true, - enabled: false - }, - jina: { - id: 'jina', - name: 'Jina', - type: 'openai', - apiKey: '', - apiHost: 'https://api.jina.ai', - models: SYSTEM_MODELS.jina, - isSystem: true, - enabled: false - }, - perplexity: { - id: 'perplexity', - name: 'Perplexity', - type: 'openai', - apiKey: '', - apiHost: 'https://api.perplexity.ai/', - models: SYSTEM_MODELS.perplexity, - isSystem: true, - enabled: false - }, - modelscope: { - id: 'modelscope', - name: 'ModelScope', - type: 'openai', - apiKey: '', - apiHost: 'https://api-inference.modelscope.cn/v1/', - anthropicApiHost: 'https://api-inference.modelscope.cn', - models: SYSTEM_MODELS.modelscope, - isSystem: true, - enabled: false - }, - xirang: { - id: 'xirang', - name: 'Xirang', - type: 'openai', - apiKey: '', - apiHost: 'https://wishub-x1.ctyun.cn', - models: SYSTEM_MODELS.xirang, - isSystem: true, - enabled: false - }, - hunyuan: { - id: 'hunyuan', - name: 'hunyuan', - type: 'openai', - apiKey: '', - apiHost: 'https://api.hunyuan.cloud.tencent.com', - models: SYSTEM_MODELS.hunyuan, - isSystem: true, - enabled: false - }, - 'tencent-cloud-ti': { - id: 'tencent-cloud-ti', - name: 'Tencent Cloud TI', - type: 'openai', - apiKey: '', - apiHost: 'https://api.lkeap.cloud.tencent.com', - models: SYSTEM_MODELS['tencent-cloud-ti'], - isSystem: true, - enabled: false - }, - 'baidu-cloud': { - id: 'baidu-cloud', - name: 'Baidu Cloud', - type: 'openai', - apiKey: '', - apiHost: 'https://qianfan.baidubce.com/v2/', - models: SYSTEM_MODELS['baidu-cloud'], - isSystem: true, - enabled: false - }, - gpustack: { - id: 'gpustack', - name: 'GPUStack', - type: 'openai', - apiKey: '', - apiHost: '', - models: SYSTEM_MODELS.gpustack, - isSystem: true, - enabled: false - }, - voyageai: { - id: 'voyageai', - name: 'VoyageAI', - type: 'openai', - apiKey: '', - apiHost: 'https://api.voyageai.com', - models: SYSTEM_MODELS.voyageai, - isSystem: true, - enabled: false - }, - 'aws-bedrock': { - id: 'aws-bedrock', - name: 'AWS Bedrock', - type: 'aws-bedrock', - apiKey: '', - apiHost: '', - models: SYSTEM_MODELS['aws-bedrock'], - isSystem: true, - enabled: false - }, - poe: { - id: 'poe', - name: 'Poe', - type: 'openai', - apiKey: '', - apiHost: 'https://api.poe.com/v1/', - models: SYSTEM_MODELS['poe'], - isSystem: true, - enabled: false - }, - longcat: { - id: 'longcat', - name: 'LongCat', - type: 'openai', - apiKey: '', - apiHost: 'https://api.longcat.chat/openai', - anthropicApiHost: 'https://api.longcat.chat/anthropic', - models: SYSTEM_MODELS.longcat, - isSystem: true, - enabled: false - }, - huggingface: { - id: 'huggingface', - name: 'Hugging Face', - type: 'openai-response', - apiKey: '', - apiHost: 'https://router.huggingface.co/v1/', - models: [], - isSystem: true, - enabled: false - }, - gateway: { - id: 'gateway', - name: 'Vercel AI Gateway', - type: 'gateway', - apiKey: '', - apiHost: 'https://ai-gateway.vercel.sh/v1/ai', - models: [], - isSystem: true, - enabled: false - }, - cerebras: { - id: 'cerebras', - name: 'Cerebras AI', - type: 'openai', - apiKey: '', - apiHost: 'https://api.cerebras.ai/v1', - models: SYSTEM_MODELS.cerebras, - isSystem: true, - enabled: false - }, - mimo: { - id: 'mimo', - name: 'Xiaomi MiMo', - type: 'openai', - apiKey: '', - apiHost: 'https://api.xiaomimimo.com', - anthropicApiHost: 'https://api.xiaomimimo.com/anthropic', - models: SYSTEM_MODELS.mimo, - isSystem: true, - enabled: false - } -} as const - -export const SYSTEM_PROVIDERS: SystemProvider[] = Object.values(SYSTEM_PROVIDERS_CONFIG) - -export const NOT_SUPPORTED_RERANK_PROVIDERS = ['ollama', 'lmstudio'] as const satisfies SystemProviderId[] -export const ONLY_SUPPORTED_DIMENSION_PROVIDERS = ['ollama', 'infini'] as const satisfies SystemProviderId[] +import { TOKENFLUX_HOST } from '@renderer/config/constant' +import type { SystemProviderId } from '@renderer/types/provider' type ProviderUrls = { api: { diff --git a/src/renderer/services/__tests__/ErrorDiagnosisService.test.ts b/src/renderer/services/__tests__/ErrorDiagnosisService.test.ts index 544ad23407..a7c68e1426 100644 --- a/src/renderer/services/__tests__/ErrorDiagnosisService.test.ts +++ b/src/renderer/services/__tests__/ErrorDiagnosisService.test.ts @@ -11,11 +11,6 @@ vi.mock('../ModelService', () => ({ readDefaultModel: vi.fn().mockResolvedValue(undefined) })) -// Mock CHERRYAI_PROVIDER -vi.mock('@renderer/config/providers', () => ({ - CHERRYAI_PROVIDER: { id: 'cherryai', type: 'openai', apiHost: 'https://api.cherry-ai.com', models: [] } -})) - // Mock LoggerService vi.mock('@renderer/services/LoggerService', () => ({ loggerService: { diff --git a/src/renderer/utils/__tests__/export.test.ts b/src/renderer/utils/__tests__/export.test.ts index 30797e0a52..6e22f695a7 100644 --- a/src/renderer/utils/__tests__/export.test.ts +++ b/src/renderer/utils/__tests__/export.test.ts @@ -7,14 +7,6 @@ import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest' // --- Mocks Setup --- -// Add this before the test suites -vi.mock('@renderer/config/miniApps', () => { - return { - PRESETS_MINI_APPS: [], - allMiniApps: [] - } -}) - // Mock window.api beforeEach(() => { Object.defineProperty(window, 'api', { diff --git a/src/renderer/config/__tests__/sidebar.test.ts b/src/renderer/utils/__tests__/sidebar.test.ts similarity index 86% rename from src/renderer/config/__tests__/sidebar.test.ts rename to src/renderer/utils/__tests__/sidebar.test.ts index df83838ba6..501ca232c5 100644 --- a/src/renderer/config/__tests__/sidebar.test.ts +++ b/src/renderer/utils/__tests__/sidebar.test.ts @@ -1,13 +1,12 @@ +import { SIDEBAR_ICON_COMPONENTS } from '@renderer/components/app/sidebarIcons' import { Library } from 'lucide-react' import { describe, expect, it } from 'vitest' import { - getDefaultSidebarFavorites, getOrderedVisibleSidebarIcons, getRequiredSidebarIconsVisible, getSidebarMenuPath, resolveSidebarActiveItem, - SIDEBAR_ICON_COMPONENTS, SIDEBAR_ICON_ORDER } from '../sidebar' @@ -43,12 +42,6 @@ describe('sidebar config helpers', () => { ]) }) - it('resets to default sidebar favorites without forcing non-default icons visible', () => { - const reset = getDefaultSidebarFavorites() - - expect(reset).toEqual(['assistants', 'agents', 'store', 'translate', 'mini_app']) - }) - it('resolves menu paths and active items with the paintings provider route', () => { expect(getSidebarMenuPath('paintings', 'zhipu')).toBe('/app/paintings/zhipu') expect(resolveSidebarActiveItem('/app/paintings/zhipu')).toBe('paintings') diff --git a/src/renderer/utils/agent.ts b/src/renderer/utils/agent.ts index 67dd370b95..edfafb4ecc 100644 --- a/src/renderer/utils/agent.ts +++ b/src/renderer/utils/agent.ts @@ -1,3 +1,4 @@ +import type { PermissionModeCard } from '@renderer/types/agent' import type { AgentConfiguration } from '@shared/data/types/agent' export const DEFAULT_AGENT_AVATAR = '🤖' @@ -9,3 +10,39 @@ export function getAgentAvatar(avatar?: unknown) { export function getAgentAvatarFromConfiguration(configuration?: Pick | null) { return getAgentAvatar(configuration?.avatar) } + +export const permissionModeCards: PermissionModeCard[] = [ + { + mode: 'default', + // t('agent.settings.tooling.permissionMode.default.title') + titleKey: 'agent.settings.tooling.permissionMode.default.title', + titleFallback: 'Normal Mode', + descriptionKey: 'agent.settings.tooling.permissionMode.default.description', + descriptionFallback: 'Can read files freely. Asks before editing or running commands.' + }, + { + mode: 'plan', + // t('agent.settings.tooling.permissionMode.plan.title') + titleKey: 'agent.settings.tooling.permissionMode.plan.title', + titleFallback: 'Plan Mode', + descriptionKey: 'agent.settings.tooling.permissionMode.plan.description', + descriptionFallback: 'Can only read files and make plans. Cannot edit files or run commands.' + }, + { + mode: 'acceptEdits', + // t('agent.settings.tooling.permissionMode.acceptEdits.title') + titleKey: 'agent.settings.tooling.permissionMode.acceptEdits.title', + titleFallback: 'Auto-edit Mode', + descriptionKey: 'agent.settings.tooling.permissionMode.acceptEdits.description', + descriptionFallback: 'Can read and edit files freely. Asks before running commands.' + }, + { + mode: 'bypassPermissions', + // t('agent.settings.tooling.permissionMode.bypassPermissions.title') + titleKey: 'agent.settings.tooling.permissionMode.bypassPermissions.title', + titleFallback: 'Full Auto Mode', + descriptionKey: 'agent.settings.tooling.permissionMode.bypassPermissions.description', + descriptionFallback: 'Can do everything without asking. Use with caution.', + caution: true + } +] diff --git a/src/renderer/utils/assistant.ts b/src/renderer/utils/assistant.ts index 025f713be6..0bf6c100e4 100644 --- a/src/renderer/utils/assistant.ts +++ b/src/renderer/utils/assistant.ts @@ -1,4 +1,4 @@ -import { isFunctionCallingModel } from '@renderer/config/models' +import { isFunctionCallingModel } from '@renderer/utils/model' import type { Model } from '@shared/data/types/model' /** diff --git a/src/renderer/utils/__tests__/modelReconcile.test.ts b/src/renderer/utils/model/__tests__/reconcile.test.ts similarity index 93% rename from src/renderer/utils/__tests__/modelReconcile.test.ts rename to src/renderer/utils/model/__tests__/reconcile.test.ts index 1c636b3b11..515dd1b79c 100644 --- a/src/renderer/utils/__tests__/modelReconcile.test.ts +++ b/src/renderer/utils/model/__tests__/reconcile.test.ts @@ -1,7 +1,7 @@ import { type Model, MODEL_CAPABILITY, type ModelCapability } from '@shared/data/types/model' import { describe, expect, it } from 'vitest' -import { canModelUseAssistantWebSearch, reconcileWebSearchForModel } from '../modelReconcile' +import { canModelUseAssistantWebSearch, reconcileWebSearchForModel } from '../reconcile' const createModel = (capabilities: ModelCapability[] = []): Model => ({ id: 'provider::model', @@ -14,7 +14,7 @@ const createModel = (capabilities: ModelCapability[] = []): Model => ({ isHidden: false }) -describe('modelReconcile web search', () => { +describe('reconcile web search', () => { it('rejects enabled web search when the next model cannot consume it', () => { const nextModel = createModel() diff --git a/src/renderer/utils/__tests__/modelSearch.test.ts b/src/renderer/utils/model/__tests__/search.test.ts similarity index 97% rename from src/renderer/utils/__tests__/modelSearch.test.ts rename to src/renderer/utils/model/__tests__/search.test.ts index d5e1f055ef..6e2178cfb7 100644 --- a/src/renderer/utils/__tests__/modelSearch.test.ts +++ b/src/renderer/utils/model/__tests__/search.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it } from 'vitest' -import { getSearchMatchScore, type ModelSearchField } from '../modelSearch' +import { getSearchMatchScore, type ModelSearchField } from '../search' -describe('modelSearch', () => { +describe('search', () => { const fields = [ { value: 'GPT-4o', weight: 0, allowAbbreviation: true }, { value: 'gpt-4o-mini', weight: 1, allowAbbreviation: true } diff --git a/src/renderer/config/models/embedding.ts b/src/renderer/utils/model/embedding.ts similarity index 100% rename from src/renderer/config/models/embedding.ts rename to src/renderer/utils/model/embedding.ts diff --git a/src/renderer/utils/model/index.ts b/src/renderer/utils/model/index.ts new file mode 100644 index 0000000000..e7c8dd75b5 --- /dev/null +++ b/src/renderer/utils/model/index.ts @@ -0,0 +1,30 @@ +// Curated public surface for the renderer model helpers. +// Named re-exports only (no `export *`) per naming-conventions §5. + +export { isEmbeddingModel, isRerankModel } from './embedding' +export { getModelLogo } from './logo' +export { isGPT5SeriesReasoningModel } from './openai' +export { + getModelSupportedReasoningEffortOptions, + getThinkModelType, + isDoubaoThinkingAutoModel, + isFixedReasoningModel, + isQwenReasoningModel, + isReasoningModel, + isSupportedReasoningEffortModel, + isSupportedThinkingTokenModel, + isSupportedThinkingTokenQwenModel, + MODEL_SUPPORTED_OPTIONS, + MODEL_SUPPORTED_REASONING_EFFORT +} from './reasoning' +export { + canModelUseAssistantWebSearch, + hasModelBuiltinWebSearch, + reconcileReasoningEffortForModel, + reconcileWebSearchForModel +} from './reconcile' +export { getSearchMatchScore } from './search' +export { isFunctionCallingModel } from './tooluse' +export { isGenerateImageModels, isVisionModels } from './utils' +export { isGenerateImageModel, isVisionModel } from './vision' +export { isOpenAIWebSearchModel, isOpenRouterBuiltInWebSearchModel, isWebSearchModel } from './websearch' diff --git a/src/renderer/config/models/logo.ts b/src/renderer/utils/model/logo.ts similarity index 100% rename from src/renderer/config/models/logo.ts rename to src/renderer/utils/model/logo.ts diff --git a/src/renderer/config/models/openai.ts b/src/renderer/utils/model/openai.ts similarity index 100% rename from src/renderer/config/models/openai.ts rename to src/renderer/utils/model/openai.ts diff --git a/src/renderer/config/models/reasoning.ts b/src/renderer/utils/model/reasoning.ts similarity index 100% rename from src/renderer/config/models/reasoning.ts rename to src/renderer/utils/model/reasoning.ts diff --git a/src/renderer/utils/modelReconcile.ts b/src/renderer/utils/model/reconcile.ts similarity index 95% rename from src/renderer/utils/modelReconcile.ts rename to src/renderer/utils/model/reconcile.ts index 1e3a32fca3..22f5e3db0c 100644 --- a/src/renderer/utils/modelReconcile.ts +++ b/src/renderer/utils/model/reconcile.ts @@ -13,21 +13,21 @@ * patch needed". Callers compose multiple reconcile fns and only emit a * settings patch when at least one returned non-null. */ -import { - getThinkModelType, - isFunctionCallingModel, - isOpenRouterBuiltInWebSearchModel, - isSupportedReasoningEffortModel, - isSupportedThinkingTokenModel, - isWebSearchModel, - MODEL_SUPPORTED_OPTIONS, - MODEL_SUPPORTED_REASONING_EFFORT -} from '@renderer/config/models' import { cacheService } from '@renderer/data/CacheService' import type { AssistantSettings } from '@renderer/types/assistant' import type { ThinkingOption } from '@renderer/types/reasoning' import type { Model } from '@shared/data/types/model' +import { + getThinkModelType, + isSupportedReasoningEffortModel, + isSupportedThinkingTokenModel, + MODEL_SUPPORTED_OPTIONS, + MODEL_SUPPORTED_REASONING_EFFORT +} from './reasoning' +import { isFunctionCallingModel } from './tooluse' +import { isOpenRouterBuiltInWebSearchModel, isWebSearchModel } from './websearch' + export type ReasoningEffortPatch = { reasoning_effort?: string } diff --git a/src/renderer/utils/modelSearch.ts b/src/renderer/utils/model/search.ts similarity index 100% rename from src/renderer/utils/modelSearch.ts rename to src/renderer/utils/model/search.ts diff --git a/src/renderer/config/models/tooluse.ts b/src/renderer/utils/model/tooluse.ts similarity index 100% rename from src/renderer/config/models/tooluse.ts rename to src/renderer/utils/model/tooluse.ts diff --git a/src/renderer/config/models/utils.ts b/src/renderer/utils/model/utils.ts similarity index 100% rename from src/renderer/config/models/utils.ts rename to src/renderer/utils/model/utils.ts diff --git a/src/renderer/config/models/vision.ts b/src/renderer/utils/model/vision.ts similarity index 100% rename from src/renderer/config/models/vision.ts rename to src/renderer/utils/model/vision.ts diff --git a/src/renderer/config/models/websearch.ts b/src/renderer/utils/model/websearch.ts similarity index 100% rename from src/renderer/config/models/websearch.ts rename to src/renderer/utils/model/websearch.ts diff --git a/src/renderer/config/sidebar.ts b/src/renderer/utils/sidebar.ts similarity index 77% rename from src/renderer/config/sidebar.ts rename to src/renderer/utils/sidebar.ts index ea4fe33169..3b8733e6f2 100644 --- a/src/renderer/config/sidebar.ts +++ b/src/renderer/utils/sidebar.ts @@ -1,26 +1,11 @@ -import { OpenClawSidebarIcon } from '@renderer/components/Icons/SvgIcon' -import type { SidebarMenuItem } from '@renderer/components/Sidebar/types' import { buildTabInstanceMetadata, getTabInstanceAppId, getTabInstanceKey, hasTabInstanceMetadataForApp -} from '@renderer/config/tabInstanceMetadata' +} from '@renderer/utils/tabInstanceMetadata' import type { Tab } from '@shared/data/cache/cacheValueTypes' import type { SidebarIcon } from '@shared/data/preference/preferenceTypes' -import { getDefaultValue } from '@shared/data/preference/preferenceUtils' -import { - Code, - FileSearch, - Folder, - Languages, - LayoutGrid, - Library, - MessageSquare, - MousePointerClick, - NotepadText, - Palette -} from 'lucide-react' /** * Context passed to sidebar navigation handlers. Carries per-call state the @@ -51,7 +36,6 @@ export interface SidebarInstanceKey { export interface SidebarApp { id: SidebarIcon - icon: SidebarMenuItem['icon'] routePrefix: string /** Url to open when no tab exists yet (defaults to `routePrefix`). */ resolveUrl?: (ctx: SidebarNavContext) => string @@ -85,7 +69,6 @@ function isMessageOnlyConversationUrl(url: string): boolean { export const SIDEBAR_APPS: readonly SidebarApp[] = [ { id: 'assistants', - icon: MessageSquare, routePrefix: '/app/chat', instanceKey: { keyFromUrl: (url) => getNormalConversationSearchParamFromUrl(url, 'topicId'), @@ -95,7 +78,6 @@ export const SIDEBAR_APPS: readonly SidebarApp[] = [ }, { id: 'agents', - icon: MousePointerClick, routePrefix: '/app/agents', instanceKey: { keyFromUrl: (url) => getNormalConversationSearchParamFromUrl(url, 'sessionId'), @@ -105,49 +87,40 @@ export const SIDEBAR_APPS: readonly SidebarApp[] = [ }, { id: 'paintings', - icon: Palette, routePrefix: '/app/paintings', resolveUrl: ({ defaultPaintingProvider }) => `/app/paintings/${defaultPaintingProvider}` }, { id: 'translate', - icon: Languages, routePrefix: '/app/translate' }, { id: 'store', - icon: Library, routePrefix: '/app/library' }, { id: 'mini_app', - icon: LayoutGrid, routePrefix: '/app/mini-app', exactRouteFocus: true }, { id: 'knowledge', - icon: FileSearch, routePrefix: '/app/knowledge' }, { id: 'files', - icon: Folder, routePrefix: '/app/files' }, { id: 'code_tools', - icon: Code, routePrefix: '/app/code' }, { id: 'notes', - icon: NotepadText, routePrefix: '/app/notes' }, { id: 'openclaw', - icon: OpenClawSidebarIcon, routePrefix: '/app/openclaw' } ] @@ -196,28 +169,6 @@ export function resolveSidebarAppTabEntryUrl(tab: Pick) return tab.url } -/** - * The tab id to focus on a sidebar click, or undefined if none exists. Apps with - * sub-instances narrow the match to the last-focused key (so clicking returns to - * that one); keyless apps focus any tab they own. - */ -export function findAppTabToFocus(app: SidebarApp, tabs: Tab[], ctx: SidebarNavContext): string | undefined { - const key = app.instanceKey?.defaultKey(ctx) - const existing = tabs.find( - (t) => - t.type === 'route' && - (app.exactRouteFocus ? t.url === app.routePrefix : tabBelongsToApp(app, t.url)) && - (app.instanceKey && key ? getSidebarAppTabInstanceKey(app, t) === key : true) - ) - return existing?.id -} - -/** The url to open when no owned tab exists yet (base route, resolveUrl, or routePrefix). */ -export function resolveAppOpenUrl(app: SidebarApp, ctx: SidebarNavContext): string { - const key = app.instanceKey?.defaultKey(ctx) - return app.instanceKey && key ? app.routePrefix : (app.resolveUrl?.(ctx) ?? app.routePrefix) -} - export function buildSidebarAppOpenMetadata(app: SidebarApp, key?: string): Tab['metadata'] { if (!app.instanceKey || !key) return undefined if (app.id !== 'assistants' && app.id !== 'agents') return undefined @@ -239,22 +190,6 @@ export const REQUIRED_SIDEBAR_ICONS: SidebarIcon[] = ['assistants'] const sidebarIconSet = new Set(SIDEBAR_ICON_ORDER) -export const SIDEBAR_ROUTE_PREFIX_MAP: Record = SIDEBAR_APPS.reduce( - (acc, app) => { - acc[app.id] = app.routePrefix - return acc - }, - {} as Record -) - -export const SIDEBAR_ICON_COMPONENTS: Record = SIDEBAR_APPS.reduce( - (acc, app) => { - acc[app.id] = app.icon - return acc - }, - {} as Record -) - export function getSidebarMenuPath(icon: SidebarIcon, defaultPaintingProvider: string): string { const app = getSidebarApp(icon) if (!app) return '' @@ -302,7 +237,3 @@ export function getOrderedVisibleSidebarIcons(icons: readonly SidebarIcon[] | un return visible } - -export function getDefaultSidebarFavorites(): SidebarIcon[] { - return getOrderedVisibleSidebarIcons(getDefaultValue('ui.sidebar.favorites')) -} diff --git a/src/renderer/config/tabInstanceMetadata.ts b/src/renderer/utils/tabInstanceMetadata.ts similarity index 100% rename from src/renderer/config/tabInstanceMetadata.ts rename to src/renderer/utils/tabInstanceMetadata.ts diff --git a/src/renderer/windows/subWindow/SubWindowAppShell.tsx b/src/renderer/windows/subWindow/SubWindowAppShell.tsx index fb725be5fc..899b72641b 100644 --- a/src/renderer/windows/subWindow/SubWindowAppShell.tsx +++ b/src/renderer/windows/subWindow/SubWindowAppShell.tsx @@ -5,11 +5,11 @@ import { TabRouter } from '@renderer/components/layout/TabRouter' import { TITLE_BAR_HEIGHT_CLASS } from '@renderer/components/layout/titleBar' import MiniAppTabsPool from '@renderer/components/MiniApp/MiniAppTabsPool' import WindowControls, { useHasWindowControls } from '@renderer/components/WindowControls' -import { clearTabInstanceMetadata } from '@renderer/config/tabInstanceMetadata' import { useTabs } from '@renderer/hooks/tab' import { useWindowInitData } from '@renderer/hooks/useWindowInitData' import { getDefaultRouteTitle, isPageTitledRoute } from '@renderer/utils/routeTitle' import { cn } from '@renderer/utils/style' +import { clearTabInstanceMetadata } from '@renderer/utils/tabInstanceMetadata' import type { SubWindowInitData } from '@shared/types/subWindow' import { Activity, type CSSProperties, useEffect, useRef } from 'react'