mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
refactor(web): model & provider page
This commit is contained in:
@@ -10,7 +10,6 @@
|
|||||||
<form @submit="addModel">
|
<form @submit="addModel">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<!-- {{ $t("button.add", { msg: "Model" }) }} -->
|
|
||||||
{{ title === 'edit' ? '编辑Model' : '添加Model' }}
|
{{ title === 'edit' ? '编辑Model' : '添加Model' }}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription class="mb-4">
|
<DialogDescription class="mb-4">
|
||||||
@@ -18,55 +17,7 @@
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
<FormField
|
<!-- 1. Type(先选类型) -->
|
||||||
v-slot="{ componentField }"
|
|
||||||
name="name"
|
|
||||||
>
|
|
||||||
<FormItem>
|
|
||||||
<Label class="mb-2">
|
|
||||||
Name
|
|
||||||
</Label>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
v-bind="componentField"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
</FormField>
|
|
||||||
<FormField
|
|
||||||
v-slot="{ componentField }"
|
|
||||||
name="model_id"
|
|
||||||
>
|
|
||||||
<FormItem>
|
|
||||||
<Label class="mb-2">
|
|
||||||
Model ID
|
|
||||||
</Label>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
v-bind="componentField"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
</FormField>
|
|
||||||
<FormField
|
|
||||||
v-slot="{ componentField }"
|
|
||||||
name="dimensions"
|
|
||||||
>
|
|
||||||
<FormItem>
|
|
||||||
<Label class="mb-2">
|
|
||||||
Dimensions
|
|
||||||
</Label>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
v-bind="componentField"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</FormItem>
|
|
||||||
</FormField>
|
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
v-slot="{ componentField }"
|
v-slot="{ componentField }"
|
||||||
name="type"
|
name="type"
|
||||||
@@ -78,7 +29,7 @@
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Select v-bind="componentField">
|
<Select v-bind="componentField">
|
||||||
<SelectTrigger class="w-full">
|
<SelectTrigger class="w-full">
|
||||||
<SelectValue />
|
<SelectValue placeholder="选择模型类型" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
@@ -86,7 +37,7 @@
|
|||||||
Chat
|
Chat
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="embedding">
|
<SelectItem value="embedding">
|
||||||
embedding
|
Embedding
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
@@ -94,16 +45,77 @@
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
|
<!-- 2. Model(原 Model ID) -->
|
||||||
<FormField
|
<FormField
|
||||||
v-slot="{ componentField }"
|
v-slot="{ componentField }"
|
||||||
name="is_multimodal"
|
name="model_id"
|
||||||
>
|
>
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<Label class="mb-2">
|
<Label class="mb-2">
|
||||||
|
Model
|
||||||
|
</Label>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g. gpt-4o"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<!-- 3. Display Name(可选) -->
|
||||||
|
<FormField
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="name"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<Label class="mb-2">
|
||||||
|
Display Name
|
||||||
|
<span class="text-muted-foreground text-xs ml-1">(optional)</span>
|
||||||
|
</Label>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="自定义显示名称"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<!-- 4. Dimensions(仅 embedding 时显示) -->
|
||||||
|
<FormField
|
||||||
|
v-if="selectedType === 'embedding'"
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="dimensions"
|
||||||
|
>
|
||||||
|
<FormItem>
|
||||||
|
<Label class="mb-2">
|
||||||
|
Dimensions
|
||||||
|
</Label>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="e.g. 1536"
|
||||||
|
v-bind="componentField"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<!-- 5. 多模态(仅 chat 时显示) -->
|
||||||
|
<FormField
|
||||||
|
v-if="selectedType === 'chat'"
|
||||||
|
v-slot="{ componentField }"
|
||||||
|
name="is_multimodal"
|
||||||
|
>
|
||||||
|
<FormItem class="flex items-center justify-between">
|
||||||
|
<Label>
|
||||||
是否开启多模态
|
是否开启多模态
|
||||||
</Label>
|
</Label>
|
||||||
<Switch
|
<Switch
|
||||||
id="airplane-mode"
|
|
||||||
v-model="componentField.modelValue"
|
v-model="componentField.modelValue"
|
||||||
@update:model-value="componentField['onUpdate:modelValue']"
|
@update:model-value="componentField['onUpdate:modelValue']"
|
||||||
/>
|
/>
|
||||||
@@ -121,7 +133,7 @@
|
|||||||
:disabled="!form.meta.value.valid"
|
:disabled="!form.meta.value.valid"
|
||||||
>
|
>
|
||||||
<Spinner v-if="isLoading" />
|
<Spinner v-if="isLoading" />
|
||||||
{{ $t("button.add", { msg: "Model" }) }}
|
{{ title === 'edit' ? '保存' : $t("button.add", { msg: "Model" }) }}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</form>
|
</form>
|
||||||
@@ -154,40 +166,54 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Separator,
|
Separator,
|
||||||
Label,
|
Label,
|
||||||
Spinner
|
Spinner,
|
||||||
} from '@memoh/ui'
|
} from '@memoh/ui'
|
||||||
import { useForm } from 'vee-validate'
|
import { useForm } from 'vee-validate'
|
||||||
import { inject, watch, type Ref, ref } from 'vue'
|
import { inject, computed, watch, type Ref, ref } from 'vue'
|
||||||
import { toTypedSchema } from '@vee-validate/zod'
|
import { toTypedSchema } from '@vee-validate/zod'
|
||||||
import z from 'zod'
|
import z from 'zod'
|
||||||
import { type ModelInfo } from '@memoh/shared'
|
import { type ModelInfo } from '@memoh/shared'
|
||||||
import { useCreateModel } from '@/composables/api/useModels'
|
import { useCreateModel } from '@/composables/api/useModels'
|
||||||
|
|
||||||
const formSchema = toTypedSchema(z.object({
|
const formSchema = toTypedSchema(z.object({
|
||||||
is_multimodal: z.coerce.boolean(),
|
type: z.string().min(1, '请选择模型类型'),
|
||||||
model_id: z.string().min(1),
|
model_id: z.string().min(1),
|
||||||
name: z.string().min(1),
|
name: z.string().optional(),
|
||||||
type: z.string().min(1),
|
dimensions: z.coerce.number().min(1).optional(),
|
||||||
dimensions: z.coerce.number().min(1),
|
is_multimodal: z.coerce.boolean().optional(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
validationSchema: formSchema,
|
validationSchema: formSchema,
|
||||||
initialValues: {
|
|
||||||
dimensions: 1,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const selectedType = computed(() => form.values.type)
|
||||||
|
|
||||||
const { id } = defineProps<{ id: string }>()
|
const { id } = defineProps<{ id: string }>()
|
||||||
|
|
||||||
const { mutate: createModel, isLoading } = useCreateModel()
|
const { mutate: createModel, isLoading } = useCreateModel()
|
||||||
|
|
||||||
const addModel = form.handleSubmit(async (modelInfo) => {
|
const addModel = form.handleSubmit(async (values) => {
|
||||||
try {
|
try {
|
||||||
await createModel({
|
const payload: Record<string, unknown> = {
|
||||||
...modelInfo,
|
type: values.type,
|
||||||
|
model_id: values.model_id,
|
||||||
llm_provider_id: id,
|
llm_provider_id: id,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if (values.name) {
|
||||||
|
payload.name = values.name
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.type === 'embedding' && values.dimensions) {
|
||||||
|
payload.dimensions = values.dimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.type === 'chat') {
|
||||||
|
payload.is_multimodal = values.is_multimodal ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
await createModel(payload as any)
|
||||||
open.value = false
|
open.value = false
|
||||||
} catch {
|
} catch {
|
||||||
return
|
return
|
||||||
@@ -210,6 +236,6 @@ watch(open, () => {
|
|||||||
editInfo.value = null
|
editInfo.value = null
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
immediate: true
|
immediate: true,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -6,12 +6,12 @@ import type { Ref } from 'vue'
|
|||||||
// ---- Types ----
|
// ---- Types ----
|
||||||
|
|
||||||
export interface CreateModelRequest {
|
export interface CreateModelRequest {
|
||||||
name: string
|
|
||||||
model_id: string
|
model_id: string
|
||||||
type: string
|
type: string
|
||||||
dimensions: number
|
|
||||||
is_multimodal: boolean
|
|
||||||
llm_provider_id: string
|
llm_provider_id: string
|
||||||
|
name?: string
|
||||||
|
dimensions?: number
|
||||||
|
is_multimodal?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Query: 获取 Provider 下的模型列表 ----
|
// ---- Query: 获取 Provider 下的模型列表 ----
|
||||||
|
|||||||
Reference in New Issue
Block a user