feat(setting): add api check

This commit is contained in:
kangfenmao 2024-07-12 14:27:27 +08:00
parent 1729c1e875
commit d3a163e50a
3 changed files with 80 additions and 11 deletions

View File

@ -1,15 +1,16 @@
import { Provider } from '@renderer/types'
import { FC, useEffect, useState } from 'react'
import styled from 'styled-components'
import { Avatar, Button, Card, Divider, Flex, Input, Switch } from 'antd'
import { Avatar, Button, Card, Divider, Flex, Input, Space, Switch } from 'antd'
import { useProvider } from '@renderer/hooks/useProvider'
import { groupBy } from 'lodash'
import { SettingContainer, SettingSubtitle, SettingTitle } from '.'
import { getModelLogo } from '@renderer/services/provider'
import { EditOutlined, ExportOutlined, PlusOutlined } from '@ant-design/icons'
import { CheckOutlined, EditOutlined, ExportOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons'
import AddModelPopup from './AddModelPopup'
import EditModelsPopup from './EditModelsPopup'
import Link from 'antd/es/typography/Link'
import { checkApi } from '@renderer/services/api'
interface Props {
provider: Provider
@ -92,6 +93,8 @@ const PROVIDER_CONFIG = {
const ProviderSetting: FC<Props> = ({ provider }) => {
const [apiKey, setApiKey] = useState(provider.apiKey)
const [apiHost, setApiHost] = useState(provider.apiHost)
const [apiValid, setApiValid] = useState(false)
const [apiChecking, setApiChecking] = useState(false)
const { updateProvider, models } = useProvider(provider.id)
const modelGroups = groupBy(models, 'group')
@ -106,12 +109,22 @@ const ProviderSetting: FC<Props> = ({ provider }) => {
const onManageModel = () => EditModelsPopup.show({ provider })
const onAddModel = () => AddModelPopup.show({ title: 'Add Model', provider })
const onCheckApi = async () => {
setApiChecking(true)
const valid = await checkApi({ ...provider, apiKey, apiHost })
setApiValid(valid)
setApiChecking(false)
setTimeout(() => setApiValid(false), 3000)
}
const providerConfig = PROVIDER_CONFIG[provider.id]
const officialWebsite = providerConfig?.websites?.official
const apiKeyWebsite = providerConfig?.websites?.apiKey
const docsWebsite = providerConfig?.websites?.docs
const modelsWebsite = providerConfig?.websites?.models
const apiKeyDisabled = provider.id === 'ollama'
return (
<SettingContainer>
<SettingTitle>
@ -131,15 +144,22 @@ const ProviderSetting: FC<Props> = ({ provider }) => {
</SettingTitle>
<Divider style={{ width: '100%', margin: '10px 0' }} />
<SettingSubtitle style={{ marginTop: 5 }}>API Key</SettingSubtitle>
<Input
value={apiKey}
placeholder="API Key"
onChange={(e) => setApiKey(e.target.value)}
onBlur={onUpdateApiKey}
spellCheck={false}
disabled={provider.id === 'ollama'}
autoFocus={provider.enabled && apiKey === ''}
/>
<Space.Compact style={{ width: '100%' }}>
<Input
value={apiKey}
placeholder="API Key"
onChange={(e) => setApiKey(e.target.value)}
onBlur={onUpdateApiKey}
spellCheck={false}
disabled={apiKeyDisabled}
autoFocus={provider.enabled && apiKey === ''}
/>
{!apiKeyDisabled && (
<Button type={apiValid ? 'primary' : 'default'} ghost={apiValid} onClick={onCheckApi}>
{apiChecking ? <LoadingOutlined spin /> : apiValid ? <CheckOutlined /> : 'Check'}
</Button>
)}
</Space.Compact>
{apiKeyWebsite && (
<HelpTextRow>
<HelpText>Get API key from: </HelpText>

View File

@ -20,12 +20,14 @@ export const SettingTitle = styled.div`
justify-content: space-between;
align-items: center;
font-weight: 900;
user-select: none;
`
export const SettingSubtitle = styled.div`
font-size: 12px;
color: var(--color-text-2);
margin: 15px 0 10px 0;
user-select: none;
`
export const SettingDivider = styled(Divider)`

View File

@ -104,6 +104,53 @@ export async function fetchMessagesSummary({ messages, assistant }: FetchMessage
return response.choices[0].message?.content
}
export async function checkApi(provider: Provider) {
const openaiProvider = getOpenAiProvider(provider)
const model = provider.models[0]
const key = 'api-check'
const style = { marginTop: '3vh' }
if (!provider.apiKey) {
window.message.error({ content: 'Please enter your API key first', key, style })
return false
}
if (!provider.apiHost) {
window.message.error({ content: 'Please enter your API host first', key, style })
return false
}
if (!model) {
window.message.error({ content: 'Please select a model first', key, style })
return false
}
let valid = false
let errorMessage = ''
try {
const response = await openaiProvider.chat.completions.create({
model: model.id,
messages: [{ role: 'user', content: 'hello' }],
stream: false
})
valid = Boolean(response?.choices[0].message)
} catch (error) {
errorMessage = (error as Error).message
valid = false
}
window.message[valid ? 'success' : 'error']({
key: 'api-check',
style: { marginTop: '3vh' },
duration: valid ? 2 : 8,
content: valid ? 'API connection successful' : 'API connection failed ' + errorMessage
})
return valid
}
export async function fetchModels(provider: Provider) {
try {
const openaiProvider = getOpenAiProvider(provider)