feat(ProviderSettings): add API options settings and popup for providers

- Introduced ApiOptionsSettings component to manage API options for providers.
- Added ApiOptionsSettingsPopup for displaying API options in a modal.
- Updated ProviderSetting to include a button for opening the API options popup for non-system providers.
- Refactored imports and adjusted layout in ProviderSetting for better UI consistency.
This commit is contained in:
kangfenmao 2025-08-12 11:53:26 +08:00
parent ef57e543c6
commit 793ccf978e
4 changed files with 116 additions and 77 deletions

View File

@ -1,8 +1,8 @@
import InfoTooltip from '@renderer/components/InfoTooltip'
import { HStack } from '@renderer/components/Layout'
import { useProvider } from '@renderer/hooks/useProvider'
import { isSystemProvider, Provider } from '@renderer/types'
import { Collapse, Flex, Switch } from 'antd'
import { Provider } from '@renderer/types'
import { Flex, Switch } from 'antd'
import { startTransition, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@ -55,17 +55,6 @@ const ApiOptionsSettings = ({ providerId }: Props) => {
},
checked: !provider.apiOptions?.isNotSupportStreamOptions
},
{
key: 'openai_array_content',
label: t('settings.provider.api.options.array_content.label'),
tip: t('settings.provider.api.options.array_content.help'),
onChange: (checked: boolean) => {
updateProviderTransition({
apiOptions: { ...provider.apiOptions, isNotSupportArrayContent: !checked }
})
},
checked: !provider.apiOptions?.isNotSupportArrayContent
},
{
key: 'openai_service_tier',
label: t('settings.provider.api.options.service_tier.label'),
@ -93,64 +82,41 @@ const ApiOptionsSettings = ({ providerId }: Props) => {
)
const options = useMemo(() => {
const items: OptionType[] = []
const items: OptionType[] = [
{
key: 'openai_array_content',
label: t('settings.provider.api.options.array_content.label'),
tip: t('settings.provider.api.options.array_content.help'),
onChange: (checked: boolean) => {
updateProviderTransition({
apiOptions: { ...provider.apiOptions, isNotSupportArrayContent: !checked }
})
},
checked: !provider.apiOptions?.isNotSupportArrayContent
}
]
if (provider.type === 'openai' || provider.type === 'openai-response' || provider.type === 'azure-openai') {
items.push(...openAIOptions)
}
return items
}, [openAIOptions, provider.type])
if (options.length === 0 || isSystemProvider(provider)) {
return null
}
return items
}, [openAIOptions, provider.apiOptions, provider.type, t, updateProviderTransition])
return (
<>
<Collapse
items={[
{
key: 'settings',
styles: {
header: {
paddingLeft: 0,
paddingRight: 6
},
body: {
padding: 0
}
},
label: (
<div
style={{
fontSize: 14,
color: 'var(--color-text-1)',
userSelect: 'none',
fontWeight: 'bold'
}}>
{t('settings.provider.api.options.label')}
</div>
),
children: (
<Flex vertical gap="middle">
{options.map((item) => (
<HStack key={item.key} justifyContent="space-between">
<HStack alignItems="center" gap={6}>
<label style={{ cursor: 'pointer' }} htmlFor={item.key}>
{item.label}
</label>
<InfoTooltip title={item.tip}></InfoTooltip>
</HStack>
<Switch id={item.key} checked={item.checked} onChange={item.onChange} />
</HStack>
))}
</Flex>
)
}
]}
ghost
expandIconPosition="end"
/>
</>
<Flex vertical gap="middle">
{options.map((item) => (
<HStack key={item.key} justifyContent="space-between">
<HStack alignItems="center" gap={6}>
<label style={{ cursor: 'pointer' }} htmlFor={item.key}>
{item.label}
</label>
<InfoTooltip title={item.tip}></InfoTooltip>
</HStack>
<Switch id={item.key} checked={item.checked} onChange={item.onChange} />
</HStack>
))}
</Flex>
)
}

View File

@ -0,0 +1,71 @@
import { TopView } from '@renderer/components/TopView'
import { Modal } from 'antd'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import ApiOptionsSettings from './ApiOptionsSettings'
interface ShowParams {
providerId: string
}
interface Props extends ShowParams {
resolve: (data: any) => void
}
const PopupContainer: React.FC<Props> = ({ providerId, resolve }) => {
const { t } = useTranslation()
const [open, setOpen] = useState(true)
const onOk = () => {
setOpen(false)
}
const onCancel = () => {
setOpen(false)
}
const onClose = () => {
resolve({})
}
ApiOptionsSettingsPopup.hide = onCancel
return (
<Modal
title={t('settings.provider.api.options.label')}
open={open}
onOk={onOk}
onCancel={onCancel}
afterClose={onClose}
transitionName="animation-move-down"
styles={{ body: { padding: '20px 16px' } }}
footer={null}
centered>
<ApiOptionsSettings providerId={providerId} />
</Modal>
)
}
const TopViewKey = 'ApiOptionsSettingsPopup'
export default class ApiOptionsSettingsPopup {
static topviewId = 0
static hide() {
TopView.hide(TopViewKey)
}
static show(props: ShowParams) {
return new Promise<any>((resolve) => {
TopView.show(
<PopupContainer
{...props}
resolve={(v) => {
resolve(v)
TopView.hide(TopViewKey)
}}
/>,
TopViewKey
)
})
}
}

View File

@ -12,7 +12,7 @@ import ManageModelsPopup from '@renderer/pages/settings/ProviderSettings/ModelLi
import NewApiAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiAddModelPopup'
import { Model } from '@renderer/types'
import { filterModelsByKeywords } from '@renderer/utils'
import { Button, Empty, Flex, Spin, Tooltip } from 'antd'
import { Button, Flex, Spin, Tooltip } from 'antd'
import { groupBy, isEmpty, sortBy, toPairs } from 'lodash'
import { ListCheck, Plus } from 'lucide-react'
import React, { memo, startTransition, useCallback, useEffect, useMemo, useState } from 'react'
@ -120,7 +120,7 @@ const ModelList: React.FC<ModelListProps> = ({ providerId }) => {
</HStack>
</SettingSubtitle>
<Spin spinning={isLoading} indicator={<LoadingIcon color="var(--color-text-2)" />}>
{displayedModelGroups && !isEmpty(displayedModelGroups) ? (
{displayedModelGroups && !isEmpty(displayedModelGroups) && (
<Flex gap={12} vertical>
{Object.keys(displayedModelGroups).map((group, i) => (
<ModelListGroup
@ -135,12 +135,6 @@ const ModelList: React.FC<ModelListProps> = ({ providerId }) => {
/>
))}
</Flex>
) : (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={t('settings.models.empty')}
style={{ visibility: isLoading ? 'hidden' : 'visible' }}
/>
)}
</Spin>
<Flex justify="space-between" align="center">

View File

@ -10,13 +10,14 @@ import i18n from '@renderer/i18n'
import { ModelList } from '@renderer/pages/settings/ProviderSettings/ModelList'
import { checkApi } from '@renderer/services/ApiService'
import { isProviderSupportAuth } from '@renderer/services/ProviderService'
import { isSystemProvider } from '@renderer/types'
import { ApiKeyConnectivity, HealthStatus } from '@renderer/types/healthCheck'
import { formatApiHost, formatApiKeys, getFancyProviderName, isOpenAIProvider } from '@renderer/utils'
import { formatErrorMessage } from '@renderer/utils/error'
import { Button, Divider, Flex, Input, Space, Switch, Tooltip } from 'antd'
import Link from 'antd/es/typography/Link'
import { debounce, isEmpty } from 'lodash'
import { Check, Settings2, SquareArrowOutUpRight, TriangleAlert } from 'lucide-react'
import { Bolt, Check, Settings2, SquareArrowOutUpRight, TriangleAlert } from 'lucide-react'
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -29,7 +30,7 @@ import {
SettingSubtitle,
SettingTitle
} from '..'
import ApiOptionsSettings from './ApiOptionsSettings'
import ApiOptionsSettingsPopup from './ApiOptionsSettings/ApiOptionsSettingsPopup'
import AwsBedrockSettings from './AwsBedrockSettings'
import CustomHeaderPopup from './CustomHeaderPopup'
import DMXAPISettings from './DMXAPISettings'
@ -229,13 +230,21 @@ const ProviderSetting: FC<Props> = ({ providerId }) => {
return (
<SettingContainer theme={theme} style={{ background: 'var(--color-background)' }}>
<SettingTitle>
<Flex align="center" gap={5}>
<Flex align="center" gap={8}>
<ProviderName>{fancyProviderName}</ProviderName>
{officialWebsite && (
<Link target="_blank" href={providerConfig.websites.official} style={{ display: 'flex' }}>
<Button type="text" size="small" icon={<SquareArrowOutUpRight size={14} />} />
</Link>
)}
{!isSystemProvider(provider) && (
<Button
type="text"
icon={<Bolt size={14} />}
size="small"
onClick={() => ApiOptionsSettingsPopup.show({ providerId: provider.id })}
/>
)}
</Flex>
<Switch
value={provider.enabled}
@ -366,7 +375,6 @@ const ProviderSetting: FC<Props> = ({ providerId }) => {
{provider.id === 'copilot' && <GithubCopilotSettings providerId={provider.id} />}
{provider.id === 'aws-bedrock' && <AwsBedrockSettings />}
{provider.id === 'vertexai' && <VertexAISettings providerId={provider.id} />}
<ApiOptionsSettings providerId={provider.id} />
<ModelList providerId={provider.id} />
</SettingContainer>
)