diff --git a/src/renderer/src/aiCore/provider/providerConfig.ts b/src/renderer/src/aiCore/provider/providerConfig.ts index 1fa34e5a1a..0a3bbc7b58 100644 --- a/src/renderer/src/aiCore/provider/providerConfig.ts +++ b/src/renderer/src/aiCore/provider/providerConfig.ts @@ -63,13 +63,14 @@ function handleSpecialProviders(model: Model, provider: Provider): Provider { // return createVertexProvider(provider) // } + if (isNewApiProvider(provider)) { + return newApiResolverCreator(model, provider) + } + if (isSystemProvider(provider)) { if (provider.id === 'aihubmix') { return aihubmixProviderCreator(model, provider) } - if (isNewApiProvider(provider)) { - return newApiResolverCreator(model, provider) - } if (provider.id === 'vertexai') { return vertexAnthropicProviderCreator(model, provider) } diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index aa734260f7..22faf0fb0e 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -289,7 +289,7 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = 'new-api': { id: 'new-api', name: 'New API', - type: 'openai', + type: 'new-api', apiKey: '', apiHost: 'http://localhost:3000', anthropicApiHost: 'http://localhost:3000', @@ -1432,5 +1432,5 @@ export const isGeminiWebSearchProvider = (provider: Provider) => { } export const isNewApiProvider = (provider: Provider) => { - return ['new-api', 'cherryin'].includes(provider.id) + return ['new-api', 'cherryin'].includes(provider.id) || provider.type === 'new-api' } diff --git a/src/renderer/src/pages/paintings/AihubmixPage.tsx b/src/renderer/src/pages/paintings/AihubmixPage.tsx index cf6a264a54..507554f1ce 100644 --- a/src/renderer/src/pages/paintings/AihubmixPage.tsx +++ b/src/renderer/src/pages/paintings/AihubmixPage.tsx @@ -14,7 +14,6 @@ import { usePaintings } from '@renderer/hooks/usePaintings' import { useAllProviders } from '@renderer/hooks/useProvider' import { useRuntime } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' -import { getProviderLabel } from '@renderer/i18n/label' import FileManager from '@renderer/services/FileManager' import { translateText } from '@renderer/services/TranslateService' import { useAppDispatch } from '@renderer/store' @@ -35,6 +34,7 @@ import SendMessageButton from '../home/Inputbar/SendMessageButton' import { SettingHelpLink, SettingTitle } from '../settings' import Artboard from './components/Artboard' import PaintingsList from './components/PaintingsList' +import ProviderSelect from './components/ProviderSelect' import { type ConfigItem, createModeConfigs, DEFAULT_PAINTING } from './config/aihubmixConfig' import { checkProviderEnabled } from './utils' @@ -76,20 +76,6 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => { const { t } = useTranslation() const { theme } = useTheme() const providers = useAllProviders() - const providerOptions = Options.map((option) => { - const provider = providers.find((p) => p.id === option) - if (provider) { - return { - label: getProviderLabel(provider.id), - value: provider.id - } - } else { - return { - label: 'Unknown Provider', - value: undefined - } - } - }) const dispatch = useAppDispatch() const { generating } = useRuntime() const navigate = useNavigate() @@ -849,17 +835,12 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => { /> - - + {/* 使用JSON配置渲染设置项 */} {modeConfigs[mode].filter((item) => (item.condition ? item.condition(painting) : true)).map(renderConfigItem)} @@ -1034,12 +1015,6 @@ const ModeSegmentedContainer = styled.div` padding-top: 24px; ` -const SelectOptionContainer = styled.div` - display: flex; - align-items: center; - gap: 8px; -` - // 添加新的样式组件 const ProviderTitleContainer = styled.div` display: flex; diff --git a/src/renderer/src/pages/paintings/DmxapiPage.tsx b/src/renderer/src/pages/paintings/DmxapiPage.tsx index 2ffc9fd958..976dc4f24d 100644 --- a/src/renderer/src/pages/paintings/DmxapiPage.tsx +++ b/src/renderer/src/pages/paintings/DmxapiPage.tsx @@ -8,7 +8,6 @@ import { getProviderLogo } from '@renderer/config/providers' import { usePaintings } from '@renderer/hooks/usePaintings' import { useAllProviders } from '@renderer/hooks/useProvider' import { useRuntime } from '@renderer/hooks/useRuntime' -import { getProviderLabel } from '@renderer/i18n/label' import FileManager from '@renderer/services/FileManager' import { useAppDispatch } from '@renderer/store' import { setGenerating } from '@renderer/store/runtime' @@ -29,6 +28,7 @@ import { SettingHelpLink, SettingTitle } from '../settings' import Artboard from './components/Artboard' import ImageUploader from './components/ImageUploader' import PaintingsList from './components/PaintingsList' +import ProviderSelect from './components/ProviderSelect' import { COURSE_URL, DEFAULT_PAINTING, @@ -46,20 +46,6 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { const [painting, setPainting] = useState(dmxapi_paintings?.[0] || DEFAULT_PAINTING) const { t } = useTranslation() const providers = useAllProviders() - const providerOptions = Options.map((option) => { - const provider = providers.find((p) => p.id === option) - if (provider) { - return { - label: getProviderLabel(provider.id), - value: provider.id - } - } else { - return { - label: 'Unknown Provider', - value: undefined - } - } - }) const dmxapiProvider = providers.find((p) => p.id === 'dmxapi')! @@ -785,9 +771,9 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { return ( - {t('paintings.title')} + {t('paintings.title')} {isMac && ( - + @@ -797,7 +783,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { - {t('common.provider')} + {t('common.provider')}
{t('paintings.paint_course')} @@ -805,28 +791,19 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => { {t('paintings.top_up')} - +
- + {painting.generationMode && [generationModeType.EDIT, generationModeType.MERGE].includes(painting.generationMode) && ( <> - 参考图 + 参考图 = ({ Options }) => { )} - + {t('common.model')} {painting.priceModel !== '0' ? painting.priceModel : ''} - {t('paintings.image.size')} + {t('paintings.image.size')} p.value === 'new-api')?.value} - onChange={handleProviderChange} - style={{ width: '100%' }}> - {providerOptions.map((provider) => ( - - - - {provider.label} - - - ))} - + {/* 当没有可用的 Image Generation 模型时,提示用户先去新增 */} {modelOptions.length === 0 && ( @@ -792,20 +778,12 @@ const ProviderLogo = styled(Avatar)` border: 0.5px solid var(--color-border); ` -// 添加新的样式组件 const ModeSegmentedContainer = styled.div` display: flex; justify-content: center; padding-top: 24px; ` -const SelectOptionContainer = styled.div` - display: flex; - align-items: center; - gap: 8px; -` - -// 添加新的样式组件 const ProviderTitleContainer = styled.div` display: flex; justify-content: space-between; diff --git a/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx b/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx index 9c0ec8a2af..a817334f6a 100644 --- a/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx +++ b/src/renderer/src/pages/paintings/PaintingsRoutePage.tsx @@ -1,8 +1,10 @@ import { loggerService } from '@logger' +import { isNewApiProvider } from '@renderer/config/providers' +import { useAllProviders } from '@renderer/hooks/useProvider' import { useAppDispatch } from '@renderer/store' import { setDefaultPaintingProvider } from '@renderer/store/settings' -import { PaintingProvider } from '@renderer/types' -import { FC, useEffect } from 'react' +import { PaintingProvider, SystemProviderId } from '@renderer/types' +import { FC, useEffect, useMemo } from 'react' import { Route, Routes, useParams } from 'react-router-dom' import AihubmixPage from './AihubmixPage' @@ -14,19 +16,23 @@ import ZhipuPage from './ZhipuPage' const logger = loggerService.withContext('PaintingsRoutePage') -const Options = ['zhipu', 'aihubmix', 'silicon', 'dmxapi', 'tokenflux', 'new-api'] +const BASE_OPTIONS: SystemProviderId[] = ['zhipu', 'aihubmix', 'silicon', 'dmxapi', 'tokenflux'] const PaintingsRoutePage: FC = () => { const params = useParams() const provider = params['*'] const dispatch = useAppDispatch() + const providers = useAllProviders() + const Options = useMemo(() => { + return [...BASE_OPTIONS, ...providers.filter((p) => isNewApiProvider(p)).map((p) => p.id)] + }, [providers]) useEffect(() => { logger.debug(`defaultPaintingProvider: ${provider}`) if (provider && Options.includes(provider)) { dispatch(setDefaultPaintingProvider(provider as PaintingProvider)) } - }, [provider, dispatch]) + }, [provider, dispatch, Options]) return ( @@ -36,7 +42,12 @@ const PaintingsRoutePage: FC = () => { } /> } /> } /> - } /> + {/* new-api family providers are mounted dynamically below */} + {providers + .filter((p) => isNewApiProvider(p)) + .map((p) => ( + } /> + ))} ) } diff --git a/src/renderer/src/pages/paintings/SiliconPage.tsx b/src/renderer/src/pages/paintings/SiliconPage.tsx index 54f413f420..dac2577a44 100644 --- a/src/renderer/src/pages/paintings/SiliconPage.tsx +++ b/src/renderer/src/pages/paintings/SiliconPage.tsx @@ -12,14 +12,12 @@ import { HStack, VStack } from '@renderer/components/Layout' import Scrollbar from '@renderer/components/Scrollbar' import TranslateButton from '@renderer/components/TranslateButton' import { isMac } from '@renderer/config/constant' -import { getProviderLogo } from '@renderer/config/providers' import { LanguagesEnum } from '@renderer/config/translate' import { useTheme } from '@renderer/context/ThemeProvider' import { usePaintings } from '@renderer/hooks/usePaintings' import { useAllProviders } from '@renderer/hooks/useProvider' import { useRuntime } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' -import { getProviderLabel } from '@renderer/i18n/label' import { getProviderByModel } from '@renderer/services/AssistantService' import FileManager from '@renderer/services/FileManager' import { translateText } from '@renderer/services/TranslateService' @@ -27,7 +25,7 @@ import { useAppDispatch } from '@renderer/store' import { setGenerating } from '@renderer/store/runtime' import type { FileMetadata, Painting } from '@renderer/types' import { getErrorMessage, uuid } from '@renderer/utils' -import { Avatar, Button, Input, InputNumber, Radio, Select, Slider, Switch, Tooltip } from 'antd' +import { Button, Input, InputNumber, Radio, Select, Slider, Switch, Tooltip } from 'antd' import TextArea from 'antd/es/input/TextArea' import { Info } from 'lucide-react' import type { FC } from 'react' @@ -40,6 +38,7 @@ import SendMessageButton from '../home/Inputbar/SendMessageButton' import { SettingTitle } from '../settings' import Artboard from './components/Artboard' import PaintingsList from './components/PaintingsList' +import ProviderSelect from './components/ProviderSelect' import { checkProviderEnabled } from './utils' export const TEXT_TO_IMAGES_MODELS = [ @@ -115,22 +114,8 @@ const SiliconPage: FC<{ Options: string[] }> = ({ Options }) => { const [painting, setPainting] = useState(siliconflow_paintings[0] || DEFAULT_PAINTING) const { theme } = useTheme() const providers = useAllProviders() - const providerOptions = Options.map((option) => { - const provider = providers.find((p) => p.id === option) - if (provider) { - return { - label: getProviderLabel(provider.id), - value: provider.id - } - } else { - return { - label: 'Unknown Provider', - value: undefined - } - } - }) - const siliconflowProvider = providers.find((p) => p.id === 'silicon') + const siliconFlowProvider = providers.find((p) => p.id === 'silicon')! const [currentImageIndex, setCurrentImageIndex] = useState(0) const [isLoading, setIsLoading] = useState(false) @@ -170,7 +155,7 @@ const SiliconPage: FC<{ Options: string[] }> = ({ Options }) => { } const onGenerate = async () => { - await checkProviderEnabled(siliconflowProvider!, t) + await checkProviderEnabled(siliconFlowProvider!, t) if (painting.files.length > 0) { const confirmed = await window.modal.confirm({ @@ -389,17 +374,8 @@ const SiliconPage: FC<{ Options: string[] }> = ({ Options }) => { {t('common.provider')} - - {t('common.model')} + + {t('common.model')} p.value === 'tokenflux')?.value} - onChange={handleProviderChange} - style={{ width: '100%' }}> - {providerOptions.map((provider) => ( - - - - {provider.label} - - - ))} - + {/* Model & Pricing Section */} = ({ Options }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [painting?.id]) // 只在painting的id改变时执行,避免无限循环 - const providerOptions = Options.map((option) => { - const provider = providers.find((p) => p.id === option) - if (provider) { - return { - label: getProviderLabel(provider.id), - value: provider.id - } - } else { - return { - label: 'Unknown Provider', - value: undefined - } - } - }) - const zhipuProvider = providers.find((p) => p.id === 'zhipu')! const [currentImageIndex, setCurrentImageIndex] = useState(0) @@ -370,16 +355,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => { /> - + {t('common.model')} { + const selectedKey = Array.from(keys)[0] as string + onChange(selectedKey) + }} + style={style} + className={`w-full ${className || ''}`} + renderValue={(items) => { + return items.map((item) => ( +
+
+ +
+ {item.textValue} +
+ )) + }}> + {providerOptions.map((providerOption) => ( + + + + }> + {providerOption.label} + + ))} + + ) +} + +export default ProviderSelect diff --git a/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx index f02485c454..5a20279594 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx @@ -252,7 +252,8 @@ const PopupContainer: React.FC = ({ provider, resolve }) => { { label: 'OpenAI-Response', value: 'openai-response' }, { label: 'Gemini', value: 'gemini' }, { label: 'Anthropic', value: 'anthropic' }, - { label: 'Azure OpenAI', value: 'azure-openai' } + { label: 'Azure OpenAI', value: 'azure-openai' }, + { label: 'New API', value: 'new-api' } ]} /> diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 96bbdbd4e5..7bdf186e62 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -273,6 +273,8 @@ export type PaintingParams = { id: string urls: string[] files: FileMetadata[] + // provider that this painting belongs to (for new-api family separation) + providerId?: string } export type PaintingProvider = 'zhipu' | 'aihubmix' | 'silicon' | 'dmxapi' | 'new-api' diff --git a/src/renderer/src/types/provider.ts b/src/renderer/src/types/provider.ts index e5233c196e..782d8e98fd 100644 --- a/src/renderer/src/types/provider.ts +++ b/src/renderer/src/types/provider.ts @@ -11,7 +11,8 @@ export const ProviderTypeSchema = z.enum([ 'vertexai', 'mistral', 'aws-bedrock', - 'vertex-anthropic' + 'vertex-anthropic', + 'new-api' ]) export type ProviderType = z.infer