mirror of
https://gitee.com/Doocs/md
synced 2025-04-29 09:32:27 +08:00
feat(ai): add default ai service (#666)
This commit is contained in:
parent
ba63ba75ba
commit
ece39cb816
@ -755,8 +755,13 @@ function onDrop(e: DragEvent) {
|
||||
|
||||
<TabsContent value="mp">
|
||||
<Form :validation-schema="mpSchema" :initial-values="mpConfig" @submit="mpSubmit">
|
||||
<Field v-slot="{ field, errorMessage }" name="proxyOrigin">
|
||||
<FormItem label="代理域名" :required="isProxyRequired" :error="errorMessage">
|
||||
<!-- 只有在需要代理时才显示 proxyOrigin 字段 -->
|
||||
<Field
|
||||
v-if="isProxyRequired"
|
||||
v-slot="{ field, errorMessage }"
|
||||
name="proxyOrigin"
|
||||
>
|
||||
<FormItem label="代理域名" required :error="errorMessage">
|
||||
<Input
|
||||
v-bind="field"
|
||||
v-model="field.value"
|
||||
|
@ -44,7 +44,7 @@ interface ChatMessage {
|
||||
}
|
||||
|
||||
const messages = ref<ChatMessage[]>([])
|
||||
const { apiKey, endpoint, model, temperature, maxToken } = useAIConfig()
|
||||
const { apiKey, endpoint, model, temperature, maxToken, type } = useAIConfig()
|
||||
|
||||
onMounted(async () => {
|
||||
const saved = localStorage.getItem(memoryKey)
|
||||
@ -160,6 +160,14 @@ async function sendMessage() {
|
||||
stream: true,
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': `application/json`,
|
||||
}
|
||||
|
||||
if (apiKey.value && type.value !== `default`) {
|
||||
headers.Authorization = `Bearer ${apiKey.value}`
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(endpoint.value)
|
||||
if (!url.pathname.endsWith(`/chat/completions`)) {
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { type ServiceOption, serviceOptions } from '@/config/ai-services'
|
||||
import { DEFAULT_SERVICE_ENDPOINT, type ServiceOption, serviceOptions } from '@/config/ai-services'
|
||||
|
||||
import { onMounted, reactive, ref, watch } from 'vue'
|
||||
|
||||
@ -23,7 +23,7 @@ const config = reactive<{
|
||||
temperature: number
|
||||
maxToken: number
|
||||
}>({
|
||||
type: `deepseek`,
|
||||
type: `default`,
|
||||
endpoint: ``,
|
||||
apiKey: ``,
|
||||
model: ``,
|
||||
@ -39,15 +39,21 @@ function currentService() {
|
||||
}
|
||||
|
||||
function initConfigFromStorage() {
|
||||
const savedType = localStorage.getItem(`openai_type`) || `deepseek`
|
||||
const savedType = localStorage.getItem(`openai_type`) || `default`
|
||||
const service = serviceOptions.find(s => s.value === savedType) || serviceOptions[0]
|
||||
|
||||
config.type = savedType
|
||||
config.endpoint = localStorage.getItem(`openai_endpoint`) || service.endpoint
|
||||
if (savedType === `default`) {
|
||||
config.endpoint = DEFAULT_SERVICE_ENDPOINT
|
||||
}
|
||||
else {
|
||||
config.endpoint = localStorage.getItem(`openai_endpoint`) || service.endpoint
|
||||
}
|
||||
config.apiKey = localStorage.getItem(`openai_key_${savedType}`) || ``
|
||||
config.model = service.models.includes(localStorage.getItem(`openai_model`) || ``)
|
||||
? localStorage.getItem(`openai_model`)!
|
||||
: service.models[0] || ``
|
||||
|
||||
const savedModel = localStorage.getItem(`openai_model`)
|
||||
config.model = savedModel && service.models.includes(savedModel) ? savedModel : (service.models[0] || ``)
|
||||
|
||||
config.temperature = Number(localStorage.getItem(`openai_temperature`) || 1)
|
||||
config.maxToken = Number(localStorage.getItem(`openai_max_token`) || 1024)
|
||||
}
|
||||
@ -56,20 +62,26 @@ onMounted(() => {
|
||||
initConfigFromStorage()
|
||||
})
|
||||
|
||||
watch(() => config.type, () => {
|
||||
const service = currentService()
|
||||
config.endpoint = service.endpoint
|
||||
const savedModel = localStorage.getItem(`openai_model`)
|
||||
config.model = savedModel && service.models.includes(savedModel) ? savedModel : (service.models[0] || ``)
|
||||
config.apiKey = localStorage.getItem(`openai_key_${config.type}`) || ``
|
||||
testResult.value = `` // ✅ 服务变化时,重置测试结果
|
||||
})
|
||||
|
||||
// 监听模型变化
|
||||
watch(() => config.model, () => {
|
||||
testResult.value = `` // ✅ 模型变化时,重置测试结果
|
||||
})
|
||||
|
||||
watch(() => config.type, () => {
|
||||
const service = currentService()
|
||||
if (config.type === `default`) {
|
||||
config.endpoint = DEFAULT_SERVICE_ENDPOINT
|
||||
config.model = service.models[0] || ``
|
||||
}
|
||||
else {
|
||||
const savedModel = localStorage.getItem(`openai_model`)
|
||||
config.endpoint = service.endpoint
|
||||
config.model = savedModel && service.models.includes(savedModel) ? savedModel : (service.models[0] || ``)
|
||||
config.apiKey = localStorage.getItem(`openai_key_${config.type}`) || ``
|
||||
}
|
||||
testResult.value = `` // ✅ 服务变化时,重置测试结果
|
||||
})
|
||||
|
||||
function saveConfig(emitEvent = true) {
|
||||
localStorage.setItem(`openai_type`, config.type)
|
||||
localStorage.setItem(`openai_endpoint`, config.endpoint)
|
||||
@ -102,6 +114,14 @@ async function testConnection() {
|
||||
testResult.value = ``
|
||||
loading.value = true
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': `application/json`,
|
||||
}
|
||||
|
||||
if (config.apiKey && config.type !== `default`) {
|
||||
headers.Authorization = `Bearer ${config.apiKey}`
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(config.endpoint)
|
||||
if (!url.pathname.endsWith(`/chat/completions`)) {
|
||||
@ -192,7 +212,7 @@ async function testConnection() {
|
||||
</div>
|
||||
|
||||
<!-- API 端点 -->
|
||||
<div>
|
||||
<div v-if="config.type !== 'default'">
|
||||
<Label class="mb-1 block text-sm font-medium">API 端点</Label>
|
||||
<Input
|
||||
v-model="config.endpoint"
|
||||
@ -201,8 +221,8 @@ async function testConnection() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- API 密钥 -->
|
||||
<div>
|
||||
<!-- API 密钥,仅非 default 显示 -->
|
||||
<div v-if="config.type !== 'default'">
|
||||
<Label class="mb-1 block text-sm font-medium">API 密钥</Label>
|
||||
<Input
|
||||
v-model="config.apiKey"
|
||||
|
@ -1,3 +1,5 @@
|
||||
export const DEFAULT_SERVICE_ENDPOINT = `https://proxy-ai.doocs.org/v1`
|
||||
|
||||
export interface ServiceOption {
|
||||
value: string
|
||||
label: string
|
||||
@ -6,6 +8,19 @@ export interface ServiceOption {
|
||||
}
|
||||
|
||||
export const serviceOptions: ServiceOption[] = [
|
||||
{
|
||||
value: `default`,
|
||||
label: `默认服务(无需配置 sk)`,
|
||||
endpoint: DEFAULT_SERVICE_ENDPOINT,
|
||||
models: [
|
||||
`Qwen/Qwen2.5-7B-Instruct`,
|
||||
`Qwen/Qwen2.5-Coder-7B-Instruct`,
|
||||
`deepseek-ai/DeepSeek-R1-Distill-Qwen-7B`,
|
||||
`deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`,
|
||||
`THUDM/GLM-Z1-9B-0414`,
|
||||
`THUDM/GLM-4-9B-0414`,
|
||||
],
|
||||
},
|
||||
{
|
||||
value: `deepseek`,
|
||||
label: `DeepSeek`,
|
||||
|
@ -525,8 +525,8 @@ export const useStore = defineStore(`store`, () => {
|
||||
const el = document.querySelector(` #output-wrapper>.preview`)! as HTMLElement
|
||||
toPng(el, {
|
||||
backgroundColor: isDark.value ? `` : `#fff`,
|
||||
skipFonts: true, // 如果加载字体控制台报错,打开这段的注释
|
||||
pixelRatio: Math.max(window.devicePixelRatio || 1, 2), // 添加 || 1 以防 devicePixelRatio 不可用
|
||||
skipFonts: true,
|
||||
pixelRatio: Math.max(window.devicePixelRatio || 1, 2),
|
||||
}).then((url) => {
|
||||
const a = document.createElement(`a`)
|
||||
a.download = filename
|
||||
|
Loading…
x
Reference in New Issue
Block a user