feat:优化webui多个页面的人机交互,修复插件地址问题,放宽插件id限制,增加高级页面缩进,统计页面快捷按钮,优化新手引导
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
// 设置向导各步骤表单组件
|
||||
|
||||
import { ExternalLink, Eye, EyeOff, X } from 'lucide-react'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
@@ -15,16 +14,14 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
|
||||
import type {
|
||||
ApiProviderSetupConfig,
|
||||
BotBasicConfig,
|
||||
EmojiConfig,
|
||||
OtherBasicConfig,
|
||||
ModelSetupConfig,
|
||||
PersonalityConfig,
|
||||
SiliconFlowConfig,
|
||||
} from './types'
|
||||
|
||||
// ====== 步骤1:Bot基础配置 ======
|
||||
@@ -156,22 +153,6 @@ export function BotBasicForm({ config, onChange }: BotBasicFormProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddAlias = (alias: string) => {
|
||||
if (alias.trim() && !config.alias_names.includes(alias.trim())) {
|
||||
onChange({
|
||||
...config,
|
||||
alias_names: [...config.alias_names, alias.trim()],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveAlias = (index: number) => {
|
||||
onChange({
|
||||
...config,
|
||||
alias_names: config.alias_names.filter((_, aliasIndex) => aliasIndex !== index),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-3">
|
||||
@@ -254,53 +235,6 @@ export function BotBasicForm({ config, onChange }: BotBasicFormProps) {
|
||||
{t('setupPage.forms.botBasic.nickname.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label>{t('setupPage.forms.botBasic.alias.label')}</Label>
|
||||
<div className="mb-2 flex flex-wrap gap-2">
|
||||
{config.alias_names.map((alias, index) => (
|
||||
<Badge key={index} variant="secondary" className="gap-1">
|
||||
{alias}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveAlias(index)}
|
||||
className="hover:text-destructive ml-1"
|
||||
aria-label={t('setupPage.forms.botBasic.alias.remove', { alias })}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="alias_input"
|
||||
placeholder={t('setupPage.forms.botBasic.alias.placeholder')}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleAddAlias((e.target as HTMLInputElement).value)
|
||||
;(e.target as HTMLInputElement).value = ''
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
const input = document.getElementById('alias_input') as HTMLInputElement
|
||||
if (input) {
|
||||
handleAddAlias(input.value)
|
||||
input.value = ''
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('setupPage.forms.botBasic.alias.add')}
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.botBasic.alias.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -313,7 +247,6 @@ interface PersonalityFormProps {
|
||||
|
||||
export function PersonalityForm({ config, onChange }: PersonalityFormProps) {
|
||||
const { t } = useTranslation()
|
||||
const multipleReplyStyleText = config.multiple_reply_style.join('\n')
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -344,276 +277,61 @@ export function PersonalityForm({ config, onChange }: PersonalityFormProps) {
|
||||
{t('setupPage.forms.personality.replyStyle.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="multiple_reply_style">
|
||||
{t('setupPage.forms.personality.multipleReplyStyle.label')}
|
||||
</Label>
|
||||
<Textarea
|
||||
id="multiple_reply_style"
|
||||
placeholder={t('setupPage.forms.personality.multipleReplyStyle.placeholder')}
|
||||
value={multipleReplyStyleText}
|
||||
onChange={(e) =>
|
||||
onChange({
|
||||
...config,
|
||||
multiple_reply_style: e.target.value
|
||||
.split('\n')
|
||||
.map((style) => style.trim())
|
||||
.filter(Boolean),
|
||||
})
|
||||
}
|
||||
rows={5}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.personality.multipleReplyStyle.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="multiple_probability">
|
||||
{t('setupPage.forms.personality.multipleProbability.label')}
|
||||
</Label>
|
||||
<span className="text-muted-foreground text-sm">
|
||||
{(config.multiple_probability * 100).toFixed(0)}%
|
||||
</span>
|
||||
</div>
|
||||
<Input
|
||||
id="multiple_probability"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.1"
|
||||
value={config.multiple_probability}
|
||||
onChange={(e) => onChange({ ...config, multiple_probability: Number(e.target.value) })}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.personality.multipleProbability.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ====== 步骤3:表情包配置 ======
|
||||
interface EmojiFormProps {
|
||||
config: EmojiConfig
|
||||
onChange: (config: EmojiConfig) => void
|
||||
// ====== 步骤3:API 提供商配置 ======
|
||||
interface ApiProviderSetupFormProps {
|
||||
config: ApiProviderSetupConfig
|
||||
onChange: (config: ApiProviderSetupConfig) => void
|
||||
}
|
||||
|
||||
export function EmojiForm({ config, onChange }: EmojiFormProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="emoji_send_num">{t('setupPage.forms.emoji.emojiSendNum.label')}</Label>
|
||||
<Input
|
||||
id="emoji_send_num"
|
||||
type="number"
|
||||
min="1"
|
||||
max="64"
|
||||
value={config.emoji_send_num}
|
||||
onChange={(e) => onChange({ ...config, emoji_send_num: Number(e.target.value) })}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.emoji.emojiSendNum.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="max_reg_num">{t('setupPage.forms.emoji.maxRegNum.label')}</Label>
|
||||
<Input
|
||||
id="max_reg_num"
|
||||
type="number"
|
||||
min="1"
|
||||
max="200"
|
||||
value={config.max_reg_num}
|
||||
onChange={(e) => onChange({ ...config, max_reg_num: Number(e.target.value) })}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.emoji.maxRegNum.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="do_replace">{t('setupPage.forms.emoji.doReplace.label')}</Label>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.emoji.doReplace.description')}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="do_replace"
|
||||
checked={config.do_replace}
|
||||
onCheckedChange={(checked) => onChange({ ...config, do_replace: checked })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="check_interval">{t('setupPage.forms.emoji.checkInterval.label')}</Label>
|
||||
<Input
|
||||
id="check_interval"
|
||||
type="number"
|
||||
min="1"
|
||||
max="120"
|
||||
value={config.check_interval}
|
||||
onChange={(e) => onChange({ ...config, check_interval: Number(e.target.value) })}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.emoji.checkInterval.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="steal_emoji">{t('setupPage.forms.emoji.stealEmoji.label')}</Label>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.emoji.stealEmoji.description')}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="steal_emoji"
|
||||
checked={config.steal_emoji}
|
||||
onCheckedChange={(checked) => onChange({ ...config, steal_emoji: checked })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="content_filtration">
|
||||
{t('setupPage.forms.emoji.contentFiltration.label')}
|
||||
</Label>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.emoji.contentFiltration.description')}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="content_filtration"
|
||||
checked={config.content_filtration}
|
||||
onCheckedChange={(checked) => onChange({ ...config, content_filtration: checked })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{config.content_filtration && (
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="filtration_prompt">
|
||||
{t('setupPage.forms.emoji.filtrationPrompt.label')}
|
||||
</Label>
|
||||
<Input
|
||||
id="filtration_prompt"
|
||||
placeholder={t('setupPage.forms.emoji.filtrationPrompt.placeholder')}
|
||||
value={config.filtration_prompt}
|
||||
onChange={(e) => onChange({ ...config, filtration_prompt: e.target.value })}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.emoji.filtrationPrompt.description')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ====== 步骤4:其他基础配置 ======
|
||||
interface OtherBasicFormProps {
|
||||
config: OtherBasicConfig
|
||||
onChange: (config: OtherBasicConfig) => void
|
||||
}
|
||||
|
||||
export function OtherBasicForm({ config, onChange }: OtherBasicFormProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="all_global">{t('setupPage.forms.other.allGlobal.label')}</Label>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.other.allGlobal.description')}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="all_global"
|
||||
checked={config.all_global}
|
||||
onCheckedChange={(checked) => onChange({ ...config, all_global: checked })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ====== 步骤5:硅基流动API配置 ======
|
||||
interface SiliconFlowFormProps {
|
||||
config: SiliconFlowConfig
|
||||
onChange: (config: SiliconFlowConfig) => void
|
||||
}
|
||||
|
||||
export function SiliconFlowForm({ config, onChange }: SiliconFlowFormProps) {
|
||||
export function ApiProviderSetupForm({ config, onChange }: ApiProviderSetupFormProps) {
|
||||
const { t } = useTranslation()
|
||||
const [showApiKey, setShowApiKey] = useState(false)
|
||||
const apiKeyToggleLabel = showApiKey
|
||||
? t('setupPage.forms.siliconFlow.apiKey.hide')
|
||||
: t('setupPage.forms.siliconFlow.apiKey.show')
|
||||
const autoConfigItems = [
|
||||
t('setupPage.forms.siliconFlow.autoConfig.items.deepseek'),
|
||||
t('setupPage.forms.siliconFlow.autoConfig.items.qwen3'),
|
||||
t('setupPage.forms.siliconFlow.autoConfig.items.qwen3Vl'),
|
||||
t('setupPage.forms.siliconFlow.autoConfig.items.senseVoice'),
|
||||
t('setupPage.forms.siliconFlow.autoConfig.items.bgeM3'),
|
||||
t('setupPage.forms.siliconFlow.autoConfig.items.lpmm'),
|
||||
]
|
||||
? t('setupPage.forms.apiProvider.apiKey.hide')
|
||||
: t('setupPage.forms.apiProvider.apiKey.show')
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4 dark:border-blue-800 dark:bg-blue-950/30">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="mt-0.5">
|
||||
<svg
|
||||
className="h-5 w-5 text-blue-600 dark:text-blue-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1 text-sm">
|
||||
<p className="mb-1 font-medium text-blue-900 dark:text-blue-100">
|
||||
{t('setupPage.forms.siliconFlow.about.title')}
|
||||
</p>
|
||||
<p className="mb-2 text-blue-700 dark:text-blue-300">
|
||||
{t('setupPage.forms.siliconFlow.about.description')}
|
||||
</p>
|
||||
<a
|
||||
href="https://cloud.siliconflow.cn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1 font-medium text-blue-600 hover:underline dark:text-blue-400"
|
||||
>
|
||||
{t('setupPage.forms.siliconFlow.about.link')}
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="provider_name">{t('setupPage.forms.apiProvider.providerName.label')}</Label>
|
||||
<Input
|
||||
id="provider_name"
|
||||
placeholder={t('setupPage.forms.apiProvider.providerName.placeholder')}
|
||||
value={config.provider_name}
|
||||
onChange={(e) => onChange({ ...config, provider_name: e.target.value })}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.apiProvider.providerName.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="siliconflow_api_key">{t('setupPage.forms.siliconFlow.apiKey.label')}</Label>
|
||||
<Label htmlFor="base_url">{t('setupPage.forms.apiProvider.baseUrl.label')}</Label>
|
||||
<Input
|
||||
id="base_url"
|
||||
placeholder="https://api.example.com/v1"
|
||||
value={config.base_url}
|
||||
onChange={(e) => onChange({ ...config, base_url: e.target.value })}
|
||||
className="font-mono"
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.apiProvider.baseUrl.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="api_key">{t('setupPage.forms.apiProvider.apiKey.label')}</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="siliconflow_api_key"
|
||||
id="api_key"
|
||||
type={showApiKey ? 'text' : 'password'}
|
||||
placeholder="sk-..."
|
||||
value={config.api_key}
|
||||
onChange={(e) => onChange({ api_key: e.target.value })}
|
||||
onChange={(e) => onChange({ ...config, api_key: e.target.value })}
|
||||
className="pr-10 font-mono"
|
||||
/>
|
||||
<Button
|
||||
@@ -633,25 +351,103 @@ export function SiliconFlowForm({ config, onChange }: SiliconFlowFormProps) {
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.siliconFlow.apiKey.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-muted/50 space-y-2 rounded-lg p-4 text-sm">
|
||||
<p className="font-medium">{t('setupPage.forms.siliconFlow.autoConfig.title')}</p>
|
||||
<ul className="text-muted-foreground ml-2 list-inside list-disc space-y-1">
|
||||
{autoConfigItems.map((item) => (
|
||||
<li key={item}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-4 dark:border-amber-800 dark:bg-amber-950/30">
|
||||
<p className="text-sm text-amber-900 dark:text-amber-100">
|
||||
<span className="font-medium">{t('setupPage.forms.siliconFlow.hint.title')}</span>
|
||||
{t('setupPage.forms.siliconFlow.hint.description')}
|
||||
{t('setupPage.forms.apiProvider.apiKey.description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ====== 步骤4:基础模型配置 ======
|
||||
interface ModelSetupFormProps {
|
||||
config: ModelSetupConfig
|
||||
onChange: (config: ModelSetupConfig) => void
|
||||
}
|
||||
|
||||
export function ModelSetupForm({ config, onChange }: ModelSetupFormProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="planner_model_identifier">
|
||||
{t('setupPage.forms.modelSetup.planner.identifier.label')}
|
||||
</Label>
|
||||
<Input
|
||||
id="planner_model_identifier"
|
||||
placeholder="gpt-4.1-mini"
|
||||
value={config.planner_model_identifier}
|
||||
onChange={(e) =>
|
||||
onChange({
|
||||
...config,
|
||||
planner_model_identifier: e.target.value,
|
||||
planner_model_name: e.target.value,
|
||||
})
|
||||
}
|
||||
className="font-mono"
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.modelSetup.planner.identifier.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-4 rounded-md bg-muted/40 p-3">
|
||||
<Label htmlFor="planner_visual" className="text-sm font-medium">
|
||||
{t('setupPage.forms.modelSetup.planner.visual.label')}
|
||||
</Label>
|
||||
<Switch
|
||||
id="planner_visual"
|
||||
checked={config.planner_visual}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...config, planner_visual: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="replyer_model_identifier">
|
||||
{t('setupPage.forms.modelSetup.replyer.identifier.label')}
|
||||
</Label>
|
||||
<Input
|
||||
id="replyer_model_identifier"
|
||||
placeholder="gpt-4.1"
|
||||
value={config.replyer_model_identifier}
|
||||
onChange={(e) =>
|
||||
onChange({
|
||||
...config,
|
||||
replyer_model_identifier: e.target.value,
|
||||
replyer_model_name: e.target.value,
|
||||
})
|
||||
}
|
||||
className="font-mono"
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('setupPage.forms.modelSetup.replyer.identifier.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-4 rounded-md bg-muted/40 p-3">
|
||||
<Label htmlFor="replyer_visual" className="text-sm font-medium">
|
||||
{t('setupPage.forms.modelSetup.replyer.visual.label')}
|
||||
</Label>
|
||||
<Switch
|
||||
id="replyer_visual"
|
||||
checked={config.replyer_visual}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...config, replyer_visual: checked })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-muted/50 rounded-lg p-4 text-sm text-muted-foreground">
|
||||
{t('setupPage.forms.modelSetup.saveHint')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,13 +4,49 @@ import { parseResponse, throwIfError } from '@/lib/api-helpers'
|
||||
import { fetchWithAuth, getAuthHeaders } from '@/lib/fetch-with-auth'
|
||||
|
||||
import type {
|
||||
ApiProviderSetupConfig,
|
||||
BotBasicConfig,
|
||||
EmojiConfig,
|
||||
OtherBasicConfig,
|
||||
ModelSetupConfig,
|
||||
PersonalityConfig,
|
||||
SiliconFlowConfig,
|
||||
} from './types'
|
||||
|
||||
interface ModelInfo {
|
||||
model_identifier: string
|
||||
name: string
|
||||
api_provider: string
|
||||
price_in?: number
|
||||
cache?: boolean
|
||||
cache_price_in?: number
|
||||
price_out?: number
|
||||
force_stream_mode?: boolean
|
||||
visual?: boolean
|
||||
extra_params?: Record<string, unknown>
|
||||
}
|
||||
|
||||
interface ApiProviderConfig {
|
||||
name: string
|
||||
base_url: string
|
||||
api_key: string
|
||||
client_type?: string
|
||||
max_retry?: number
|
||||
timeout?: number
|
||||
retry_interval?: number
|
||||
}
|
||||
|
||||
interface TaskConfig {
|
||||
model_list?: string[]
|
||||
max_tokens?: number
|
||||
temperature?: number
|
||||
slow_threshold?: number
|
||||
selection_strategy?: string
|
||||
}
|
||||
|
||||
interface ModelConfig {
|
||||
models?: ModelInfo[]
|
||||
api_providers?: ApiProviderConfig[]
|
||||
model_task_config?: Record<string, TaskConfig>
|
||||
}
|
||||
|
||||
// ===== 读取配置 =====
|
||||
|
||||
// 读取Bot基础配置
|
||||
@@ -56,73 +92,57 @@ export async function loadPersonalityConfig(): Promise<PersonalityConfig> {
|
||||
}
|
||||
}
|
||||
|
||||
// 读取表情包配置
|
||||
export async function loadEmojiConfig(): Promise<EmojiConfig> {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
|
||||
const result = await parseResponse<{ config: { emoji?: EmojiConfig } }>(
|
||||
response
|
||||
)
|
||||
const data = throwIfError(result)
|
||||
const emojiConfig = (data.config.emoji || {}) as Partial<EmojiConfig>
|
||||
|
||||
return {
|
||||
emoji_send_num: emojiConfig.emoji_send_num ?? 25,
|
||||
max_reg_num: emojiConfig.max_reg_num ?? 64,
|
||||
do_replace: emojiConfig.do_replace ?? true,
|
||||
check_interval: emojiConfig.check_interval ?? 10,
|
||||
steal_emoji: emojiConfig.steal_emoji ?? true,
|
||||
content_filtration: emojiConfig.content_filtration ?? false,
|
||||
filtration_prompt: emojiConfig.filtration_prompt || '',
|
||||
}
|
||||
}
|
||||
|
||||
// 读取其他基础配置
|
||||
export async function loadOtherBasicConfig(): Promise<OtherBasicConfig> {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
|
||||
const result = await parseResponse<{
|
||||
config: {
|
||||
expression?: { all_global_jargon?: boolean }
|
||||
}
|
||||
}>(response)
|
||||
const data = throwIfError(result)
|
||||
const config = data.config
|
||||
|
||||
const expressionConfig = config.expression || {}
|
||||
|
||||
return {
|
||||
all_global: expressionConfig.all_global_jargon ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
// 读取硅基流动API配置
|
||||
export async function loadSiliconFlowConfig(): Promise<SiliconFlowConfig> {
|
||||
async function loadModelConfig(): Promise<ModelConfig> {
|
||||
const response = await fetchWithAuth('/api/webui/config/model', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
|
||||
const result = await parseResponse<{
|
||||
config: {
|
||||
api_providers?: Array<{ name: string; api_key?: string }>
|
||||
}
|
||||
}>(response)
|
||||
const result = await parseResponse<{ config: ModelConfig }>(response)
|
||||
const data = throwIfError(result)
|
||||
const modelConfig = data.config
|
||||
return data.config || {}
|
||||
}
|
||||
|
||||
// 获取SiliconFlow提供商的API Key
|
||||
const apiProviders = modelConfig.api_providers || []
|
||||
const siliconFlowProvider = apiProviders.find((p) => p.name === 'SiliconFlow')
|
||||
// 读取 API 提供商配置
|
||||
export async function loadApiProviderSetupConfig(): Promise<ApiProviderSetupConfig> {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const models = modelConfig.models || []
|
||||
const taskConfig = modelConfig.model_task_config || {}
|
||||
const plannerName = taskConfig.planner?.model_list?.[0] || ''
|
||||
const replyerName = taskConfig.replyer?.model_list?.[0] || ''
|
||||
const plannerModel = models.find((model) => model.name === plannerName)
|
||||
const replyerModel = models.find((model) => model.name === replyerName)
|
||||
const providerName =
|
||||
plannerModel?.api_provider ||
|
||||
replyerModel?.api_provider ||
|
||||
modelConfig.api_providers?.[0]?.name ||
|
||||
''
|
||||
const provider = modelConfig.api_providers?.find((item) => item.name === providerName)
|
||||
|
||||
return {
|
||||
api_key: siliconFlowProvider?.api_key || '',
|
||||
provider_name: providerName,
|
||||
base_url: provider?.base_url || '',
|
||||
api_key: '',
|
||||
}
|
||||
}
|
||||
|
||||
// 读取基础模型配置
|
||||
export async function loadModelSetupConfig(): Promise<ModelSetupConfig> {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const models = modelConfig.models || []
|
||||
const taskConfig = modelConfig.model_task_config || {}
|
||||
const plannerName = taskConfig.planner?.model_list?.[0] || ''
|
||||
const replyerName = taskConfig.replyer?.model_list?.[0] || ''
|
||||
const plannerModel = models.find((model) => model.name === plannerName)
|
||||
const replyerModel = models.find((model) => model.name === replyerName)
|
||||
|
||||
return {
|
||||
planner_model_name: plannerName,
|
||||
planner_model_identifier: plannerModel?.model_identifier || plannerName,
|
||||
planner_visual: Boolean(plannerModel?.visual),
|
||||
replyer_model_name: replyerName,
|
||||
replyer_model_identifier: replyerModel?.model_identifier || replyerName,
|
||||
replyer_visual: Boolean(replyerModel?.visual),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,19 +163,6 @@ export async function saveBotBasicConfig(config: BotBasicConfig) {
|
||||
// 保存人格配置
|
||||
export async function savePersonalityConfig(config: PersonalityConfig) {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot/section/personality', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(config),
|
||||
}
|
||||
)
|
||||
|
||||
const result = await parseResponse(response)
|
||||
return throwIfError(result)
|
||||
}
|
||||
|
||||
// 保存表情包配置
|
||||
export async function saveEmojiConfig(config: EmojiConfig) {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot/section/emoji', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(config),
|
||||
@@ -165,58 +172,62 @@ export async function saveEmojiConfig(config: EmojiConfig) {
|
||||
return throwIfError(result)
|
||||
}
|
||||
|
||||
// 保存其他基础配置(黑话)
|
||||
export async function saveOtherBasicConfig(config: OtherBasicConfig) {
|
||||
const response = await fetchWithAuth('/api/webui/config/bot/section/expression', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify({ all_global_jargon: config.all_global }),
|
||||
})
|
||||
|
||||
const result = await parseResponse(response)
|
||||
return throwIfError(result)
|
||||
function createBasicModel(
|
||||
modelName: string,
|
||||
modelIdentifier: string,
|
||||
providerName: string,
|
||||
visual: boolean,
|
||||
existing?: ModelInfo
|
||||
): ModelInfo {
|
||||
return {
|
||||
price_in: 0,
|
||||
cache: false,
|
||||
cache_price_in: 0,
|
||||
price_out: 0,
|
||||
force_stream_mode: false,
|
||||
extra_params: {},
|
||||
...existing,
|
||||
visual,
|
||||
model_identifier: modelIdentifier,
|
||||
name: modelName,
|
||||
api_provider: providerName,
|
||||
}
|
||||
}
|
||||
|
||||
// 保存硅基流动API配置
|
||||
export async function saveSiliconFlowConfig(config: SiliconFlowConfig) {
|
||||
// 1. 读取现有配置
|
||||
const response = await fetchWithAuth('/api/webui/config/model', {
|
||||
method: 'GET',
|
||||
headers: getAuthHeaders(),
|
||||
})
|
||||
function upsertModel(models: ModelInfo[], model: ModelInfo): ModelInfo[] {
|
||||
const index = models.findIndex((item) => item.name === model.name)
|
||||
if (index >= 0) {
|
||||
return models.map((item, itemIndex) => (itemIndex === index ? model : item))
|
||||
}
|
||||
return [...models, model]
|
||||
}
|
||||
|
||||
const result = await parseResponse<{
|
||||
config: {
|
||||
api_providers?: Array<Record<string, unknown>>
|
||||
}
|
||||
}>(response)
|
||||
const currentModelConfig = throwIfError(result)
|
||||
const modelConfig = currentModelConfig.config
|
||||
// 保存 API 提供商配置
|
||||
export async function saveApiProviderSetupConfig(config: ApiProviderSetupConfig) {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const providerName = config.provider_name.trim()
|
||||
|
||||
// 2. 更新SiliconFlow提供商的API Key
|
||||
const apiProviders = modelConfig.api_providers || []
|
||||
const siliconFlowIndex = apiProviders.findIndex((p) => p.name === 'SiliconFlow')
|
||||
|
||||
if (siliconFlowIndex >= 0) {
|
||||
// 更新现有提供商的API Key
|
||||
apiProviders[siliconFlowIndex] = {
|
||||
...apiProviders[siliconFlowIndex],
|
||||
api_key: config.api_key,
|
||||
}
|
||||
} else {
|
||||
// 如果不存在,创建新的SiliconFlow提供商
|
||||
apiProviders.push({
|
||||
name: 'SiliconFlow',
|
||||
base_url: 'https://api.siliconflow.cn/v1',
|
||||
api_key: config.api_key,
|
||||
client_type: 'openai',
|
||||
max_retry: 3,
|
||||
timeout: 120,
|
||||
retry_interval: 5,
|
||||
})
|
||||
const providerIndex = apiProviders.findIndex((provider) => provider.name === providerName)
|
||||
const providerConfig: ApiProviderConfig = {
|
||||
name: providerName,
|
||||
base_url: config.base_url.trim(),
|
||||
api_key: config.api_key.trim(),
|
||||
client_type: 'openai',
|
||||
max_retry: 3,
|
||||
timeout: 120,
|
||||
retry_interval: 5,
|
||||
}
|
||||
|
||||
if (providerIndex >= 0) {
|
||||
apiProviders[providerIndex] = {
|
||||
...apiProviders[providerIndex],
|
||||
...providerConfig,
|
||||
}
|
||||
} else {
|
||||
apiProviders.push(providerConfig)
|
||||
}
|
||||
|
||||
// 3. 保存更新后的配置
|
||||
const updatedConfig = {
|
||||
...modelConfig,
|
||||
api_providers: apiProviders,
|
||||
@@ -232,6 +243,77 @@ export async function saveSiliconFlowConfig(config: SiliconFlowConfig) {
|
||||
return throwIfError(saveResult)
|
||||
}
|
||||
|
||||
// 保存基础模型配置
|
||||
export async function saveModelSetupConfig(
|
||||
config: ModelSetupConfig,
|
||||
providerName: string
|
||||
) {
|
||||
const modelConfig = await loadModelConfig()
|
||||
const trimmedProviderName = providerName.trim()
|
||||
const plannerModelIdentifier = config.planner_model_identifier.trim()
|
||||
const plannerModelName = plannerModelIdentifier
|
||||
const replyerModelIdentifier = config.replyer_model_identifier.trim()
|
||||
const replyerModelName = replyerModelIdentifier
|
||||
|
||||
// 新增或更新 planner/replyer 模型,并仅同步 utils 到 planner。
|
||||
let models = modelConfig.models || []
|
||||
const existingPlannerModel = models.find((model) => model.name === plannerModelName)
|
||||
const existingReplyerModel = models.find((model) => model.name === replyerModelName)
|
||||
models = upsertModel(
|
||||
models,
|
||||
createBasicModel(
|
||||
plannerModelName,
|
||||
plannerModelIdentifier,
|
||||
trimmedProviderName,
|
||||
config.planner_visual,
|
||||
existingPlannerModel
|
||||
)
|
||||
)
|
||||
models = upsertModel(
|
||||
models,
|
||||
createBasicModel(
|
||||
replyerModelName,
|
||||
replyerModelIdentifier,
|
||||
trimmedProviderName,
|
||||
config.replyer_visual,
|
||||
existingReplyerModel
|
||||
)
|
||||
)
|
||||
|
||||
const modelTaskConfig = modelConfig.model_task_config || {}
|
||||
const updatedTaskConfig = {
|
||||
...modelTaskConfig,
|
||||
planner: {
|
||||
...(modelTaskConfig.planner || {}),
|
||||
model_list: [plannerModelName],
|
||||
},
|
||||
replyer: {
|
||||
...(modelTaskConfig.replyer || {}),
|
||||
model_list: [replyerModelName],
|
||||
},
|
||||
utils: {
|
||||
...(modelTaskConfig.utils || {}),
|
||||
model_list: [plannerModelName],
|
||||
},
|
||||
}
|
||||
|
||||
// vlm/voice/embedding 等其他任务配置保持原样。
|
||||
const updatedConfig = {
|
||||
...modelConfig,
|
||||
models,
|
||||
model_task_config: updatedTaskConfig,
|
||||
}
|
||||
|
||||
const saveResponse = await fetchWithAuth('/api/webui/config/model', {
|
||||
method: 'POST',
|
||||
headers: getAuthHeaders(),
|
||||
body: JSON.stringify(updatedConfig),
|
||||
})
|
||||
|
||||
const saveResult = await parseResponse(saveResponse)
|
||||
return throwIfError(saveResult)
|
||||
}
|
||||
|
||||
// 标记设置完成
|
||||
export async function completeSetup() {
|
||||
const response = await fetchWithAuth('/api/webui/setup/complete', {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useNavigate } from '@tanstack/react-router'
|
||||
import {
|
||||
ArrowRight,
|
||||
Brain,
|
||||
Bot,
|
||||
CheckCircle2,
|
||||
Globe,
|
||||
Key,
|
||||
Settings,
|
||||
SkipForward,
|
||||
Smile,
|
||||
Sparkles,
|
||||
User,
|
||||
} from 'lucide-react'
|
||||
@@ -38,31 +37,27 @@ import { cn } from '@/lib/utils'
|
||||
import { APP_NAME } from '@/lib/version'
|
||||
import { useToast } from '@/hooks/use-toast'
|
||||
import type {
|
||||
ApiProviderSetupConfig,
|
||||
SetupStep,
|
||||
BotBasicConfig,
|
||||
ModelSetupConfig,
|
||||
PersonalityConfig,
|
||||
EmojiConfig,
|
||||
OtherBasicConfig,
|
||||
SiliconFlowConfig,
|
||||
} from './types'
|
||||
import {
|
||||
ApiProviderSetupForm,
|
||||
BotBasicForm,
|
||||
ModelSetupForm,
|
||||
PersonalityForm,
|
||||
EmojiForm,
|
||||
OtherBasicForm,
|
||||
SiliconFlowForm,
|
||||
} from './StepForms'
|
||||
import {
|
||||
loadBotBasicConfig,
|
||||
loadPersonalityConfig,
|
||||
loadEmojiConfig,
|
||||
loadOtherBasicConfig,
|
||||
loadSiliconFlowConfig,
|
||||
loadApiProviderSetupConfig,
|
||||
loadModelSetupConfig,
|
||||
saveBotBasicConfig,
|
||||
savePersonalityConfig,
|
||||
saveEmojiConfig,
|
||||
saveOtherBasicConfig,
|
||||
saveSiliconFlowConfig,
|
||||
saveApiProviderSetupConfig,
|
||||
saveModelSetupConfig,
|
||||
completeSetup,
|
||||
} from './api'
|
||||
import { RestartProvider, useRestart } from '@/lib/restart-context'
|
||||
@@ -103,15 +98,6 @@ function SetupPageContent() {
|
||||
],
|
||||
multiple_probability: 0.2,
|
||||
})
|
||||
const createDefaultEmojiConfig = (): EmojiConfig => ({
|
||||
emoji_send_num: 25,
|
||||
max_reg_num: 64,
|
||||
do_replace: true,
|
||||
check_interval: 10,
|
||||
steal_emoji: true,
|
||||
content_filtration: false,
|
||||
filtration_prompt: t('setupPage.defaults.emoji.filtrationPrompt'),
|
||||
})
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [isCompleting, setIsCompleting] = useState(false)
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
@@ -131,17 +117,21 @@ function SetupPageContent() {
|
||||
createDefaultPersonalityConfig()
|
||||
)
|
||||
|
||||
// 步骤3:表情包配置
|
||||
const [emoji, setEmoji] = useState<EmojiConfig>(() => createDefaultEmojiConfig())
|
||||
|
||||
// 步骤4:其他基础配置
|
||||
const [otherBasic, setOtherBasic] = useState<OtherBasicConfig>({
|
||||
all_global: true,
|
||||
// 步骤3:API 提供商配置
|
||||
const [apiProviderSetup, setApiProviderSetup] = useState<ApiProviderSetupConfig>({
|
||||
provider_name: '',
|
||||
base_url: '',
|
||||
api_key: '',
|
||||
})
|
||||
|
||||
// 步骤5:硅基流动API配置
|
||||
const [siliconFlow, setSiliconFlow] = useState<SiliconFlowConfig>({
|
||||
api_key: '',
|
||||
// 步骤4:基础模型配置
|
||||
const [modelSetup, setModelSetup] = useState<ModelSetupConfig>({
|
||||
planner_model_name: '',
|
||||
planner_model_identifier: '',
|
||||
planner_visual: false,
|
||||
replyer_model_name: '',
|
||||
replyer_model_identifier: '',
|
||||
replyer_visual: false,
|
||||
})
|
||||
|
||||
const steps: SetupStep[] = [
|
||||
@@ -158,23 +148,17 @@ function SetupPageContent() {
|
||||
icon: User,
|
||||
},
|
||||
{
|
||||
id: 'emoji',
|
||||
title: t('setupPage.steps.emoji.title'),
|
||||
description: t('setupPage.steps.emoji.description'),
|
||||
icon: Smile,
|
||||
},
|
||||
{
|
||||
id: 'other',
|
||||
title: t('setupPage.steps.other.title'),
|
||||
description: t('setupPage.steps.other.description'),
|
||||
icon: Settings,
|
||||
},
|
||||
{
|
||||
id: 'siliconflow',
|
||||
title: t('setupPage.steps.siliconFlow.title'),
|
||||
description: t('setupPage.steps.siliconFlow.description'),
|
||||
id: 'api-provider',
|
||||
title: t('setupPage.steps.apiProvider.title'),
|
||||
description: t('setupPage.steps.apiProvider.description'),
|
||||
icon: Key,
|
||||
},
|
||||
{
|
||||
id: 'model-setup',
|
||||
title: t('setupPage.steps.modelSetup.title'),
|
||||
description: t('setupPage.steps.modelSetup.description'),
|
||||
icon: Brain,
|
||||
},
|
||||
]
|
||||
|
||||
const progress = ((currentStep + 1) / steps.length) * 100
|
||||
@@ -186,19 +170,17 @@ function SetupPageContent() {
|
||||
setIsLoading(true)
|
||||
|
||||
// 并行加载所有配置
|
||||
const [bot, personality, emoji, other, silicon] = await Promise.all([
|
||||
const [bot, personality, apiProvider, model] = await Promise.all([
|
||||
loadBotBasicConfig(),
|
||||
loadPersonalityConfig(),
|
||||
loadEmojiConfig(),
|
||||
loadOtherBasicConfig(),
|
||||
loadSiliconFlowConfig(),
|
||||
loadApiProviderSetupConfig(),
|
||||
loadModelSetupConfig(),
|
||||
])
|
||||
|
||||
setBotBasic(bot)
|
||||
setPersonality(personality)
|
||||
setEmoji(emoji)
|
||||
setOtherBasic(other)
|
||||
setSiliconFlow(silicon)
|
||||
setApiProviderSetup(apiProvider)
|
||||
setModelSetup(model)
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t('setupPage.toast.loadFailedTitle'),
|
||||
@@ -225,14 +207,11 @@ function SetupPageContent() {
|
||||
case 1: // 人格配置
|
||||
await savePersonalityConfig(personality)
|
||||
break
|
||||
case 2: // 表情包
|
||||
await saveEmojiConfig(emoji)
|
||||
case 2: // API 提供商
|
||||
await saveApiProviderSetupConfig(apiProviderSetup)
|
||||
break
|
||||
case 3: // 其他设置
|
||||
await saveOtherBasicConfig(otherBasic)
|
||||
break
|
||||
case 4: // 硅基流动API
|
||||
await saveSiliconFlowConfig(siliconFlow)
|
||||
case 3: // 基础模型
|
||||
await saveModelSetupConfig(modelSetup, apiProviderSetup.provider_name)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -272,6 +251,24 @@ function SetupPageContent() {
|
||||
return null
|
||||
}
|
||||
|
||||
function validateApiProviderSetup(config: ApiProviderSetupConfig): string | null {
|
||||
if (!config.provider_name.trim()) return t('setupPage.validation.enterProviderName')
|
||||
if (!config.base_url.trim()) return t('setupPage.validation.enterBaseUrl')
|
||||
if (!config.api_key.trim()) return t('setupPage.validation.enterApiKey')
|
||||
return null
|
||||
}
|
||||
|
||||
function validateModelSetup(config: ModelSetupConfig): string | null {
|
||||
if (!config.planner_model_identifier.trim()) {
|
||||
return t('setupPage.validation.enterPlannerModelIdentifier')
|
||||
}
|
||||
if (!config.replyer_model_identifier.trim()) {
|
||||
return t('setupPage.validation.enterReplyerModelIdentifier')
|
||||
}
|
||||
if (!apiProviderSetup.provider_name.trim()) return t('setupPage.validation.enterProviderName')
|
||||
return null
|
||||
}
|
||||
|
||||
const handleNext = async () => {
|
||||
// Step 1 验证
|
||||
if (currentStep === 0) {
|
||||
@@ -285,6 +282,28 @@ function SetupPageContent() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (currentStep === 2) {
|
||||
const error = validateApiProviderSetup(apiProviderSetup)
|
||||
if (error) {
|
||||
toast({
|
||||
title: t('setupPage.toast.validationFailedTitle'),
|
||||
description: error,
|
||||
variant: 'destructive',
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
if (currentStep === 3) {
|
||||
const error = validateModelSetup(modelSetup)
|
||||
if (error) {
|
||||
toast({
|
||||
title: t('setupPage.toast.validationFailedTitle'),
|
||||
description: error,
|
||||
variant: 'destructive',
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 保存当前步骤
|
||||
const saved = await saveCurrentStep()
|
||||
@@ -306,7 +325,18 @@ function SetupPageContent() {
|
||||
setIsCompleting(true)
|
||||
|
||||
try {
|
||||
// 1. 保存最后一步的配置(硅基流动API Key)
|
||||
const error = validateModelSetup(modelSetup)
|
||||
if (error) {
|
||||
toast({
|
||||
title: t('setupPage.toast.validationFailedTitle'),
|
||||
description: error,
|
||||
variant: 'destructive',
|
||||
})
|
||||
setIsCompleting(false)
|
||||
return
|
||||
}
|
||||
|
||||
// 1. 保存最后一步的基础模型配置
|
||||
const saved = await saveCurrentStep()
|
||||
if (!saved) {
|
||||
setIsCompleting(false)
|
||||
@@ -357,11 +387,9 @@ function SetupPageContent() {
|
||||
case 1:
|
||||
return <PersonalityForm config={personality} onChange={setPersonality} />
|
||||
case 2:
|
||||
return <EmojiForm config={emoji} onChange={setEmoji} />
|
||||
return <ApiProviderSetupForm config={apiProviderSetup} onChange={setApiProviderSetup} />
|
||||
case 3:
|
||||
return <OtherBasicForm config={otherBasic} onChange={setOtherBasic} />
|
||||
case 4:
|
||||
return <SiliconFlowForm config={siliconFlow} onChange={setSiliconFlow} />
|
||||
return <ModelSetupForm config={modelSetup} onChange={setModelSetup} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -24,23 +24,19 @@ export interface PersonalityConfig {
|
||||
multiple_probability: number
|
||||
}
|
||||
|
||||
// 步骤3:表情包配置
|
||||
export interface EmojiConfig {
|
||||
emoji_send_num: number
|
||||
max_reg_num: number
|
||||
do_replace: boolean
|
||||
check_interval: number
|
||||
steal_emoji: boolean
|
||||
content_filtration: boolean
|
||||
filtration_prompt: string
|
||||
}
|
||||
|
||||
// 步骤4:其他基础配置
|
||||
export interface OtherBasicConfig {
|
||||
all_global: boolean // 全局黑话模式(expression.all_global_jargon)
|
||||
}
|
||||
|
||||
// 步骤5:硅基流动API配置
|
||||
export interface SiliconFlowConfig {
|
||||
// 步骤3:API 提供商配置
|
||||
export interface ApiProviderSetupConfig {
|
||||
provider_name: string
|
||||
base_url: string
|
||||
api_key: string
|
||||
}
|
||||
|
||||
// 步骤4:基础模型配置
|
||||
export interface ModelSetupConfig {
|
||||
planner_model_name: string
|
||||
planner_model_identifier: string
|
||||
planner_visual: boolean
|
||||
replyer_model_name: string
|
||||
replyer_model_identifier: string
|
||||
replyer_visual: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user