Compare commits
5 Commits
52a4ffd7a9
...
b715b8e859
| Author | SHA1 | Date | |
|---|---|---|---|
| b715b8e859 | |||
| afa3026585 | |||
| 44ce7156cf | |||
| a104046fbd | |||
| 0c63c3b44d |
@@ -58,6 +58,7 @@ type UpdateModelRequest struct {
|
|||||||
type TestModelRequest struct {
|
type TestModelRequest struct {
|
||||||
Provider string `json:"provider" binding:"required"`
|
Provider string `json:"provider" binding:"required"`
|
||||||
Model string `json:"model" binding:"required"`
|
Model string `json:"model" binding:"required"`
|
||||||
|
ModelType string `json:"model_type" binding:"required"`
|
||||||
APIKey string `json:"api_key" binding:"required"`
|
APIKey string `json:"api_key" binding:"required"`
|
||||||
BaseURL string `json:"base_url" binding:"required"`
|
BaseURL string `json:"base_url" binding:"required"`
|
||||||
APIEndpoint string `json:"api_endpoint"`
|
APIEndpoint string `json:"api_endpoint"`
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ func (s *ModelService) Delete(id string) error {
|
|||||||
|
|
||||||
// TestConnection 测试模型连接
|
// TestConnection 测试模型连接
|
||||||
func (s *ModelService) TestConnection(req model.TestModelRequest) (*model.TestModelResponse, error) {
|
func (s *ModelService) TestConnection(req model.TestModelRequest) (*model.TestModelResponse, error) {
|
||||||
log.Printf("[TestConnection] 开始测试连接: provider=%s, model=%s, base_url=%s, api_endpoint=%s", req.Provider, req.Model, req.BaseURL, req.APIEndpoint)
|
|
||||||
|
|
||||||
// 构建请求 URL
|
// 构建请求 URL
|
||||||
baseURL := req.BaseURL
|
baseURL := req.BaseURL
|
||||||
// 去掉 base_url 末尾的斜杠
|
// 去掉 base_url 末尾的斜杠
|
||||||
@@ -126,31 +124,49 @@ func (s *ModelService) TestConnection(req model.TestModelRequest) (*model.TestMo
|
|||||||
apiEndpoint := strings.TrimLeft(req.APIEndpoint, "/")
|
apiEndpoint := strings.TrimLeft(req.APIEndpoint, "/")
|
||||||
baseURL = baseURL + "/" + apiEndpoint
|
baseURL = baseURL + "/" + apiEndpoint
|
||||||
} else {
|
} else {
|
||||||
// 默认端点 - 根据不同 provider 设置
|
// 根据 model_type 确定端点
|
||||||
switch req.Provider {
|
switch req.ModelType {
|
||||||
case "OpenAI":
|
case "embedding":
|
||||||
baseURL = baseURL + "/v1/chat/completions"
|
// embedding 模型使用 /v1/embeddings
|
||||||
case "Ollama":
|
switch req.Provider {
|
||||||
baseURL = baseURL + "/api/chat"
|
case "Ollama":
|
||||||
case "ali", "Ali", "aliyun", "Aliyun":
|
baseURL = baseURL + "/api/embeddings"
|
||||||
// 阿里云 DashScope 兼容 OpenAI 格式,需要添加 /chat/completions
|
default:
|
||||||
// base_url 格式: https://dashscope.aliyuncs.com/compatible-mode/v1
|
baseURL = baseURL + "/v1/embeddings"
|
||||||
baseURL = baseURL + "/chat/completions"
|
}
|
||||||
default:
|
default:
|
||||||
// 默认使用 OpenAI 兼容格式
|
// chat 模型使用 /chat/completions
|
||||||
baseURL = baseURL + "/v1/chat/completions"
|
switch req.Provider {
|
||||||
|
case "OpenAI":
|
||||||
|
baseURL = baseURL + "/v1/chat/completions"
|
||||||
|
case "Ollama":
|
||||||
|
baseURL = baseURL + "/api/chat"
|
||||||
|
case "ali", "Ali", "aliyun", "Aliyun":
|
||||||
|
// 阿里云 DashScope 兼容 OpenAI 格式
|
||||||
|
baseURL = baseURL + "/chat/completions"
|
||||||
|
default:
|
||||||
|
// 默认使用 OpenAI 兼容格式
|
||||||
|
baseURL = baseURL + "/v1/chat/completions"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[TestConnection] 请求 URL: %s", baseURL)
|
// 构建请求体 - 根据 model_type 使用不同的格式
|
||||||
|
var requestBody map[string]interface{}
|
||||||
// 构建请求体
|
if req.ModelType == "embedding" {
|
||||||
requestBody := map[string]interface{}{
|
requestBody = map[string]interface{}{
|
||||||
"model": req.Model,
|
"model": req.Model,
|
||||||
"messages": []map[string]string{
|
"input": "Hello",
|
||||||
{"role": "user", "content": "Hello"},
|
"format": "float",
|
||||||
},
|
}
|
||||||
"max_tokens": 10,
|
} else {
|
||||||
|
requestBody = map[string]interface{}{
|
||||||
|
"model": req.Model,
|
||||||
|
"messages": []map[string]string{
|
||||||
|
{"role": "user", "content": "Hello"},
|
||||||
|
},
|
||||||
|
"max_tokens": 10,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := json.Marshal(requestBody)
|
body, err := json.Marshal(requestBody)
|
||||||
@@ -191,8 +207,6 @@ func (s *ModelService) TestConnection(req model.TestModelRequest) (*model.TestMo
|
|||||||
return &model.TestModelResponse{Success: false, Message: err.Error()}, nil
|
return &model.TestModelResponse{Success: false, Message: err.Error()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[TestConnection] 响应状态码: %d, 响应体: %s", resp.StatusCode, string(respBody))
|
|
||||||
|
|
||||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||||
return &model.TestModelResponse{Success: true, Message: "Connection successful"}, nil
|
return &model.TestModelResponse{Success: true, Message: "Connection successful"}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
130
web/src/components/FormDialog.vue
Normal file
130
web/src/components/FormDialog.vue
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
modelValue: boolean
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
icon?: string
|
||||||
|
iconClass?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
emit('update:modelValue', false)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Teleport to="body">
|
||||||
|
<div v-if="modelValue" class="dialog-overlay" @click.self="close">
|
||||||
|
<div class="dialog-container">
|
||||||
|
<!-- 头部 -->
|
||||||
|
<div class="dialog-header">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div v-if="icon" :class="['dialog-icon', iconClass]">
|
||||||
|
<i :class="icon"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="dialog-title">{{ title }}</h3>
|
||||||
|
<p v-if="description" class="dialog-desc">{{ description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn-icon" @click="close">
|
||||||
|
<i class="fa-solid fa-xmark text-xl"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="dialog-content">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部 -->
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-container {
|
||||||
|
background-color: #121218;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 520px;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #2a2a3a;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid #2a2a3a;
|
||||||
|
background-color: rgba(30, 30, 40, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-icon i {
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-desc {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #9ca3af;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid #2a2a3a;
|
||||||
|
background-color: rgba(30, 30, 40, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,8 +1,64 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { useModelSettings } from './settings/useModelSettings'
|
||||||
import './knowledge/knowledge.css'
|
import './knowledge/knowledge.css'
|
||||||
|
|
||||||
|
// 获取已配置的模型列表
|
||||||
|
const { models, fetchModels } = useModelSettings()
|
||||||
|
|
||||||
|
// 步骤验证
|
||||||
|
const step1Valid = computed(() => !!newKbForm.value.name.trim())
|
||||||
|
const step2Valid = computed(() => !!embeddingConfig.value.modelId)
|
||||||
|
const step3Valid = computed(() => true) // Parsing - 暂时默认通过
|
||||||
|
const step4Valid = computed(() => true) // Storage - 暂时默认通过
|
||||||
|
|
||||||
|
// 获取当前步骤是否有效
|
||||||
|
const isCurrentStepValid = computed(() => {
|
||||||
|
switch (createStep.value) {
|
||||||
|
case 1: return step1Valid.value
|
||||||
|
case 2: return step2Valid.value
|
||||||
|
case 3: return step3Valid.value
|
||||||
|
case 4: return step4Valid.value
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 步骤是否可以点击(只能点击当前步骤或已完成的步骤)
|
||||||
|
const canClickStep = (step: number) => {
|
||||||
|
if (step === 1) return true
|
||||||
|
if (step === createStep.value) return true
|
||||||
|
// 可以点击已完成的前面的步骤
|
||||||
|
if (step < createStep.value) {
|
||||||
|
switch (step) {
|
||||||
|
case 1: return step1Valid.value
|
||||||
|
case 2: return step2Valid.value
|
||||||
|
case 3: return step3Valid.value
|
||||||
|
case 4: return step4Valid.value
|
||||||
|
default: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下一步
|
||||||
|
const nextStep = () => {
|
||||||
|
if (!isCurrentStepValid.value) {
|
||||||
|
ElMessage.warning('Please complete the current step first')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (createStep.value < 4) {
|
||||||
|
createStep.value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上一步
|
||||||
|
const prevStep = () => {
|
||||||
|
if (createStep.value > 1) {
|
||||||
|
createStep.value--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 模拟知识库数据
|
// 模拟知识库数据
|
||||||
const knowledgeBases = ref([
|
const knowledgeBases = ref([
|
||||||
{
|
{
|
||||||
@@ -55,8 +111,7 @@ const newKbForm = ref({
|
|||||||
description: '',
|
description: '',
|
||||||
})
|
})
|
||||||
const embeddingConfig = ref({
|
const embeddingConfig = ref({
|
||||||
provider: '',
|
modelId: '',
|
||||||
model: '',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const parsingConfig = ref({
|
const parsingConfig = ref({
|
||||||
@@ -71,7 +126,9 @@ const parsingConfig = ref({
|
|||||||
const openCreateDialog = () => {
|
const openCreateDialog = () => {
|
||||||
createStep.value = 1
|
createStep.value = 1
|
||||||
newKbForm.value = { name: '', description: '' }
|
newKbForm.value = { name: '', description: '' }
|
||||||
embeddingConfig.value = { provider: '', model: '' }
|
embeddingConfig.value = { modelId: '' }
|
||||||
|
// 获取已配置的模型列表
|
||||||
|
fetchModels()
|
||||||
parsingConfig.value = {
|
parsingConfig.value = {
|
||||||
enablePdf: true,
|
enablePdf: true,
|
||||||
engine: 'default',
|
engine: 'default',
|
||||||
@@ -85,7 +142,7 @@ const openCreateDialog = () => {
|
|||||||
|
|
||||||
const cancelCreate = () => {
|
const cancelCreate = () => {
|
||||||
newKbForm.value = { name: '', description: '' }
|
newKbForm.value = { name: '', description: '' }
|
||||||
embeddingConfig.value = { provider: '', model: '' }
|
embeddingConfig.value = { modelId: '' }
|
||||||
parsingConfig.value = {
|
parsingConfig.value = {
|
||||||
enablePdf: true,
|
enablePdf: true,
|
||||||
engine: 'default',
|
engine: 'default',
|
||||||
@@ -97,18 +154,6 @@ const cancelCreate = () => {
|
|||||||
showCreateDialog.value = false
|
showCreateDialog.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextStep = () => {
|
|
||||||
if (!newKbForm.value.name) {
|
|
||||||
ElMessage.warning('Please enter knowledge base name')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
createStep.value = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevStep = () => {
|
|
||||||
createStep.value = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const createKnowledgeBase = () => {
|
const createKnowledgeBase = () => {
|
||||||
knowledgeBases.value.push({
|
knowledgeBases.value.push({
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
@@ -120,7 +165,7 @@ const createKnowledgeBase = () => {
|
|||||||
status: 'ready'
|
status: 'ready'
|
||||||
})
|
})
|
||||||
newKbForm.value = { name: '', description: '' }
|
newKbForm.value = { name: '', description: '' }
|
||||||
embeddingConfig.value = { provider: '', model: '' }
|
embeddingConfig.value = { modelId: '' }
|
||||||
showCreateDialog.value = false
|
showCreateDialog.value = false
|
||||||
ElMessage.success('Knowledge base created successfully')
|
ElMessage.success('Knowledge base created successfully')
|
||||||
}
|
}
|
||||||
@@ -300,11 +345,12 @@ const viewDetail = (kb: any) => {
|
|||||||
<span class="menu-desc">Name and description</span>
|
<span class="menu-desc">Name and description</span>
|
||||||
</div>
|
</div>
|
||||||
<i v-if="createStep === 1" class="fa-solid fa-chevron-right menu-arrow"></i>
|
<i v-if="createStep === 1" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||||
|
<i v-else-if="createStep > 1 && step1Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{ active: createStep === 2 }"
|
:class="{ active: createStep === 2, disabled: !canClickStep(2) }"
|
||||||
@click="createStep = 2"
|
@click="canClickStep(2) && (createStep = 2)"
|
||||||
>
|
>
|
||||||
<i class="fa-solid fa-robot menu-icon"></i>
|
<i class="fa-solid fa-robot menu-icon"></i>
|
||||||
<div class="menu-content">
|
<div class="menu-content">
|
||||||
@@ -312,11 +358,12 @@ const viewDetail = (kb: any) => {
|
|||||||
<span class="menu-desc">Embedding & rerank models</span>
|
<span class="menu-desc">Embedding & rerank models</span>
|
||||||
</div>
|
</div>
|
||||||
<i v-if="createStep === 2" class="fa-solid fa-chevron-right menu-arrow"></i>
|
<i v-if="createStep === 2" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||||
|
<i v-else-if="createStep > 2 && step2Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{ active: createStep === 3 }"
|
:class="{ active: createStep === 3, disabled: !canClickStep(3) }"
|
||||||
@click="createStep = 3"
|
@click="canClickStep(3) && (createStep = 3)"
|
||||||
>
|
>
|
||||||
<i class="fa-solid fa-file-lines menu-icon"></i>
|
<i class="fa-solid fa-file-lines menu-icon"></i>
|
||||||
<div class="menu-content">
|
<div class="menu-content">
|
||||||
@@ -324,11 +371,12 @@ const viewDetail = (kb: any) => {
|
|||||||
<span class="menu-desc">Document parsing settings</span>
|
<span class="menu-desc">Document parsing settings</span>
|
||||||
</div>
|
</div>
|
||||||
<i v-if="createStep === 3" class="fa-solid fa-chevron-right menu-arrow"></i>
|
<i v-if="createStep === 3" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||||
|
<i v-else-if="createStep > 3 && step3Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{ active: createStep === 4 }"
|
:class="{ active: createStep === 4, disabled: !canClickStep(4) }"
|
||||||
@click="createStep = 4"
|
@click="canClickStep(4) && (createStep = 4)"
|
||||||
>
|
>
|
||||||
<i class="fa-solid fa-hard-drive menu-icon"></i>
|
<i class="fa-solid fa-hard-drive menu-icon"></i>
|
||||||
<div class="menu-content">
|
<div class="menu-content">
|
||||||
@@ -336,42 +384,7 @@ const viewDetail = (kb: any) => {
|
|||||||
<span class="menu-desc">Vector & file storage</span>
|
<span class="menu-desc">Vector & file storage</span>
|
||||||
</div>
|
</div>
|
||||||
<i v-if="createStep === 4" class="fa-solid fa-chevron-right menu-arrow"></i>
|
<i v-if="createStep === 4" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||||
</div>
|
<i v-else-if="createStep > 4 && step4Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||||
<div
|
|
||||||
class="menu-item"
|
|
||||||
:class="{ active: createStep === 5 }"
|
|
||||||
@click="createStep = 5"
|
|
||||||
>
|
|
||||||
<i class="fa-solid fa-scissors menu-icon"></i>
|
|
||||||
<div class="menu-content">
|
|
||||||
<span class="menu-title">Chunking</span>
|
|
||||||
<span class="menu-desc">Text chunking strategy</span>
|
|
||||||
</div>
|
|
||||||
<i v-if="createStep === 5" class="fa-solid fa-chevron-right menu-arrow"></i>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="menu-item"
|
|
||||||
:class="{ active: createStep === 6 }"
|
|
||||||
@click="createStep = 6"
|
|
||||||
>
|
|
||||||
<i class="fa-solid fa-project-diagram menu-icon"></i>
|
|
||||||
<div class="menu-content">
|
|
||||||
<span class="menu-title">Knowledge Graph</span>
|
|
||||||
<span class="menu-desc">Graph extraction settings</span>
|
|
||||||
</div>
|
|
||||||
<i v-if="createStep === 6" class="fa-solid fa-chevron-right menu-arrow"></i>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="menu-item"
|
|
||||||
:class="{ active: createStep === 7 }"
|
|
||||||
@click="createStep = 7"
|
|
||||||
>
|
|
||||||
<i class="fa-solid fa-sliders menu-icon"></i>
|
|
||||||
<div class="menu-content">
|
|
||||||
<span class="menu-title">Advanced</span>
|
|
||||||
<span class="menu-desc">Additional settings</span>
|
|
||||||
</div>
|
|
||||||
<i v-if="createStep === 7" class="fa-solid fa-chevron-right menu-arrow"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -400,31 +413,21 @@ const viewDetail = (kb: any) => {
|
|||||||
<span class="section-title">Model Config</span>
|
<span class="section-title">Model Config</span>
|
||||||
</div>
|
</div>
|
||||||
<el-form label-position="top" class="kb-form">
|
<el-form label-position="top" class="kb-form">
|
||||||
<el-form-item label="Embedding Provider">
|
<el-form-item label="Embedding Model">
|
||||||
<el-select v-model="embeddingConfig.provider" placeholder="Select embedding provider" class="w-full">
|
<el-select v-model="embeddingConfig.modelId" placeholder="Select a configured model" class="w-full" popper-class="dark-select-dropdown">
|
||||||
<el-option label="OpenAI" value="openai">
|
<el-option
|
||||||
<div class="provider-option">
|
v-for="model in models"
|
||||||
<i class="fa-solid fa-robot"></i>
|
:key="model.id"
|
||||||
<span>OpenAI</span>
|
:label="model.name"
|
||||||
</div>
|
:value="model.id"
|
||||||
</el-option>
|
>
|
||||||
<el-option label="Ollama" value="ollama">
|
<div class="model-option">
|
||||||
<div class="provider-option">
|
<span class="model-name">{{ model.name }}</span>
|
||||||
<i class="fa-solid fa-microchip"></i>
|
<span class="model-info">{{ model.provider }} - {{ model.model }}</span>
|
||||||
<span>Ollama</span>
|
|
||||||
</div>
|
|
||||||
</el-option>
|
|
||||||
<el-option label="Azure OpenAI" value="azure">
|
|
||||||
<div class="provider-option">
|
|
||||||
<i class="fa-brands fa-microsoft"></i>
|
|
||||||
<span>Azure OpenAI</span>
|
|
||||||
</div>
|
</div>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="Embedding Model">
|
|
||||||
<el-input v-model="embeddingConfig.model" placeholder="e.g., text-embedding-ada-002" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -510,76 +513,25 @@ const viewDetail = (kb: any) => {
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Step 5: Chunking -->
|
|
||||||
<div v-if="createStep === 5" class="form-content">
|
|
||||||
<div class="section-header">
|
|
||||||
<i class="fa-solid fa-scissors section-icon"></i>
|
|
||||||
<span class="section-title">Chunking</span>
|
|
||||||
</div>
|
|
||||||
<el-form label-position="top" class="kb-form">
|
|
||||||
<el-form-item label="Chunk Size">
|
|
||||||
<el-input type="number" placeholder="e.g., 500" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Chunk Overlap">
|
|
||||||
<el-input type="number" placeholder="e.g., 50" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Step 6: Knowledge Graph -->
|
|
||||||
<div v-if="createStep === 6" class="form-content">
|
|
||||||
<div class="section-header">
|
|
||||||
<i class="fa-solid fa-project-diagram section-icon"></i>
|
|
||||||
<span class="section-title">Knowledge Graph</span>
|
|
||||||
</div>
|
|
||||||
<el-form label-position="top" class="kb-form">
|
|
||||||
<el-form-item label="Enable Knowledge Graph">
|
|
||||||
<el-switch />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Graph Extraction">
|
|
||||||
<el-select placeholder="Select extraction level" class="w-full">
|
|
||||||
<el-option label="Entities Only" value="entities" />
|
|
||||||
<el-option label="Entities & Relations" value="relations" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Step 7: Advanced -->
|
|
||||||
<div v-if="createStep === 7" class="form-content">
|
|
||||||
<div class="section-header">
|
|
||||||
<i class="fa-solid fa-sliders section-icon"></i>
|
|
||||||
<span class="section-title">Advanced</span>
|
|
||||||
</div>
|
|
||||||
<el-form label-position="top" class="kb-form">
|
|
||||||
<el-form-item label="Top K">
|
|
||||||
<el-input type="number" placeholder="e.g., 5" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Score Threshold">
|
|
||||||
<el-input type="number" placeholder="e.g., 0.7" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<div class="footer-left">
|
<div class="footer-left">
|
||||||
<span class="step-hint">Step {{ createStep }} of 7</span>
|
<span class="step-hint">Step {{ createStep }} of 4</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-right">
|
<div class="footer-right">
|
||||||
<el-button @click="cancelCreate" class="cancel-btn">Cancel</el-button>
|
<el-button @click="cancelCreate" class="cancel-btn">Cancel</el-button>
|
||||||
<el-button v-if="createStep > 1" @click="createStep--" class="prev-btn">
|
<el-button v-if="createStep > 1" @click="prevStep" class="prev-btn">
|
||||||
<i class="fa-solid fa-arrow-left"></i>
|
<i class="fa-solid fa-arrow-left"></i>
|
||||||
Previous
|
Previous
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="createStep < 7" @click="createStep++" class="next-btn">
|
<el-button v-if="createStep < 4" :disabled="!isCurrentStepValid" @click="nextStep" class="next-btn">
|
||||||
Next
|
Next
|
||||||
<i class="fa-solid fa-arrow-right"></i>
|
<i class="fa-solid fa-arrow-right"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-if="createStep === 7" @click="createKnowledgeBase" class="confirm-btn">
|
<el-button v-if="createStep === 4" :disabled="!isCurrentStepValid" @click="createKnowledgeBase" class="confirm-btn">
|
||||||
<i class="fa-solid fa-plus"></i>
|
<i class="fa-solid fa-plus"></i>
|
||||||
Create
|
Create
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { useModelSettings } from './settings/useModelSettings'
|
import { useModelSettings } from './settings/useModelSettings'
|
||||||
|
import FormDialog from '@/components/FormDialog.vue'
|
||||||
import './settings/settings.css'
|
import './settings/settings.css'
|
||||||
import './settings/settings-parsing.css'
|
import './settings/settings-parsing.css'
|
||||||
import './settings/modelSettings.css'
|
import './settings/modelSettings.css'
|
||||||
@@ -476,7 +477,7 @@ const saveStorageSettings = () => {
|
|||||||
{{ model.model }}
|
{{ model.model }}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<span class="model-type-tag">{{ model.model_type }}</span>
|
<span class="model-type-tag" :class="model.model_type">{{ model.model_type }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center text-sm">
|
<td class="text-center text-sm">
|
||||||
{{ model.base_url }}
|
{{ model.base_url }}
|
||||||
@@ -498,22 +499,28 @@ const saveStorageSettings = () => {
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- 新增模型弹窗 -->
|
<!-- 新增模型弹窗 -->
|
||||||
<el-dialog
|
<FormDialog
|
||||||
v-model="showAddModelForm"
|
:model-value="showAddModelForm"
|
||||||
|
@update:model-value="showAddModelForm = $event"
|
||||||
title="Add New Model"
|
title="Add New Model"
|
||||||
width="500px"
|
description="Configure your model settings"
|
||||||
:close-on-click-modal="false"
|
icon="fa-solid fa-brain"
|
||||||
class="add-model-dialog"
|
icon-class="bg-gradient-to-br from-primary-orange to-red-500"
|
||||||
>
|
>
|
||||||
<p class="dialog-desc">Configure your model settings</p>
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">Model Name</label>
|
||||||
|
<input
|
||||||
|
v-model="newModelForm.name"
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g., My GPT-4 Model"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-form label-position="top" class="settings-form">
|
<div>
|
||||||
<el-form-item label="Model Name">
|
<label class="block text-sm font-medium text-gray-300 mb-2">Model Type</label>
|
||||||
<el-input v-model="newModelForm.name" placeholder="e.g., My GPT-4 Model" />
|
<el-select v-model="newModelForm.modelType" placeholder="Select model type" class="w-full" size="large" popper-class="dark-select-dropdown">
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="Model Type">
|
|
||||||
<el-select v-model="newModelForm.modelType" placeholder="Select model type">
|
|
||||||
<el-option
|
<el-option
|
||||||
v-for="type in modelTypeOptions"
|
v-for="type in modelTypeOptions"
|
||||||
:key="type.value"
|
:key="type.value"
|
||||||
@@ -521,10 +528,11 @@ const saveStorageSettings = () => {
|
|||||||
:value="type.value"
|
:value="type.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</div>
|
||||||
|
|
||||||
<el-form-item label="Provider">
|
<div>
|
||||||
<el-select v-model="newModelForm.provider" placeholder="Select provider">
|
<label class="block text-sm font-medium text-gray-300 mb-2">Provider</label>
|
||||||
|
<el-select v-model="newModelForm.provider" placeholder="Select provider" class="w-full" size="large" popper-class="dark-select-dropdown">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="provider in providerOptions"
|
v-for="provider in providerOptions"
|
||||||
:key="provider.value"
|
:key="provider.value"
|
||||||
@@ -532,60 +540,86 @@ const saveStorageSettings = () => {
|
|||||||
:value="provider.value"
|
:value="provider.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="Model">
|
|
||||||
<el-input v-model="newModelForm.model" placeholder="e.g., gpt-4o (OpenAI) or llama3 (Ollama)" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="API Key">
|
|
||||||
<el-input v-model="newModelForm.apiKey" type="password" placeholder="sk-xxxxx (required for OpenAI)" show-password />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="Base URL">
|
|
||||||
<el-input v-model="newModelForm.baseUrl" placeholder="https://api.openai.com/v1 (OpenAI) or http://localhost:11434 (Ollama)" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button text @click="cancelAddModel">Cancel</el-button>
|
|
||||||
<el-button
|
|
||||||
:loading="testingConnection"
|
|
||||||
@click="testConnection"
|
|
||||||
class="test-btn"
|
|
||||||
>
|
|
||||||
<i v-if="!testingConnection" class="fa-solid fa-plug"></i>
|
|
||||||
Test Connection
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" @click="addModel">Confirm</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">Model</label>
|
||||||
|
<input
|
||||||
|
v-model="newModelForm.model"
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g., gpt-4o (OpenAI) or llama3 (Ollama)"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">API Key</label>
|
||||||
|
<input
|
||||||
|
v-model="newModelForm.apiKey"
|
||||||
|
type="password"
|
||||||
|
placeholder="sk-xxxxx (required for OpenAI)"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">Base URL</label>
|
||||||
|
<input
|
||||||
|
v-model="newModelForm.baseUrl"
|
||||||
|
type="text"
|
||||||
|
placeholder="https://api.openai.com/v1 (OpenAI) or http://localhost:11434 (Ollama)"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 连接状态提示 -->
|
||||||
<div v-if="connectionStatus === 'success'" class="connection-status success">
|
<div v-if="connectionStatus === 'success'" class="connection-status success">
|
||||||
<i class="fa-solid fa-check-circle"></i> Connection successful
|
<i class="fa-solid fa-check-circle"></i> Connection successful
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="connectionStatus === 'error'" class="connection-status error">
|
<div v-else-if="connectionStatus === 'error'" class="connection-status error">
|
||||||
<i class="fa-solid fa-xmark-circle"></i> Connection failed
|
<i class="fa-solid fa-xmark-circle"></i> Connection failed
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<button class="btn-secondary" @click="cancelAddModel">Cancel</button>
|
||||||
|
<button
|
||||||
|
:disabled="testingConnection"
|
||||||
|
class="btn-primary"
|
||||||
|
@click="testConnection"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-plug"></i>
|
||||||
|
Test Connection
|
||||||
|
</button>
|
||||||
|
<button class="btn-primary" @click="addModel">
|
||||||
|
Confirm
|
||||||
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</FormDialog>
|
||||||
|
|
||||||
<!-- 编辑模型弹窗 -->
|
<!-- 编辑模型弹窗 -->
|
||||||
<el-dialog
|
<FormDialog
|
||||||
v-model="showEditModelForm"
|
:model-value="showEditModelForm"
|
||||||
|
@update:model-value="showEditModelForm = $event"
|
||||||
title="Edit Model"
|
title="Edit Model"
|
||||||
width="500px"
|
description="Update your model settings"
|
||||||
:close-on-click-modal="false"
|
icon="fa-solid fa-pen-to-square"
|
||||||
class="add-model-dialog"
|
icon-class="bg-gradient-to-br from-primary-cyan to-blue-500"
|
||||||
>
|
>
|
||||||
<p class="dialog-desc">Update your model settings</p>
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">Model Name</label>
|
||||||
|
<input
|
||||||
|
v-model="editForm.name"
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g., My GPT-4 Model"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-form label-position="top" class="settings-form">
|
<div>
|
||||||
<el-form-item label="Model Name">
|
<label class="block text-sm font-medium text-gray-300 mb-2">Provider</label>
|
||||||
<el-input v-model="editForm.name" placeholder="e.g., My GPT-4 Model" />
|
<el-select v-model="editForm.provider" placeholder="Select provider" class="w-full" size="large" popper-class="dark-select-dropdown">
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="Provider">
|
|
||||||
<el-select v-model="editForm.provider" placeholder="Select provider">
|
|
||||||
<el-option
|
<el-option
|
||||||
v-for="option in providerOptions"
|
v-for="option in providerOptions"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
@@ -593,14 +627,21 @@ const saveStorageSettings = () => {
|
|||||||
:value="option.value"
|
:value="option.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</div>
|
||||||
|
|
||||||
<el-form-item label="Model">
|
<div>
|
||||||
<el-input v-model="editForm.model" placeholder="e.g., gpt-4, llama2" />
|
<label class="block text-sm font-medium text-gray-300 mb-2">Model</label>
|
||||||
</el-form-item>
|
<input
|
||||||
|
v-model="editForm.model"
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g., gpt-4, llama2"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-form-item label="Model Type">
|
<div>
|
||||||
<el-select v-model="editForm.modelType" placeholder="Select model type">
|
<label class="block text-sm font-medium text-gray-300 mb-2">Model Type</label>
|
||||||
|
<el-select v-model="editForm.modelType" placeholder="Select model type" class="w-full" size="large" popper-class="dark-select-dropdown">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="option in modelTypeOptions"
|
v-for="option in modelTypeOptions"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
@@ -608,38 +649,52 @@ const saveStorageSettings = () => {
|
|||||||
:value="option.value"
|
:value="option.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="API Key">
|
|
||||||
<el-input v-model="editForm.apiKey" type="password" placeholder="sk-..." show-password />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="Base URL">
|
|
||||||
<el-input v-model="editForm.baseUrl" placeholder="e.g., https://api.openai.com/v1" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<button class="test-btn" @click="cancelEditModel">Cancel</button>
|
|
||||||
<el-button
|
|
||||||
:loading="testingEditConnection"
|
|
||||||
@click="testConnectionEdit"
|
|
||||||
class="test-btn"
|
|
||||||
>
|
|
||||||
<i v-if="!testingEditConnection" class="fa-solid fa-plug"></i>
|
|
||||||
Test Connection
|
|
||||||
</el-button>
|
|
||||||
<el-button type="primary" @click="updateModel">Confirm</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">API Key</label>
|
||||||
|
<input
|
||||||
|
v-model="editForm.apiKey"
|
||||||
|
type="password"
|
||||||
|
placeholder="sk-..."
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-300 mb-2">Base URL</label>
|
||||||
|
<input
|
||||||
|
v-model="editForm.baseUrl"
|
||||||
|
type="text"
|
||||||
|
placeholder="e.g., https://api.openai.com/v1"
|
||||||
|
class="input-field"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 连接状态提示 -->
|
||||||
<div v-if="editConnectionStatus === 'success'" class="connection-status success">
|
<div v-if="editConnectionStatus === 'success'" class="connection-status success">
|
||||||
<i class="fa-solid fa-check-circle"></i> Connection successful
|
<i class="fa-solid fa-check-circle"></i> Connection successful
|
||||||
</div>
|
</div>
|
||||||
<div v-if="editConnectionStatus === 'error'" class="connection-status error">
|
<div v-if="editConnectionStatus === 'error'" class="connection-status error">
|
||||||
<i class="fa-solid fa-xmark-circle"></i> Connection failed
|
<i class="fa-solid fa-xmark-circle"></i> Connection failed
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<button class="btn-secondary" @click="cancelEditModel">Cancel</button>
|
||||||
|
<button
|
||||||
|
:disabled="testingEditConnection"
|
||||||
|
class="btn-primary"
|
||||||
|
@click="testConnectionEdit"
|
||||||
|
>
|
||||||
|
<i class="fa-solid fa-plug"></i>
|
||||||
|
Test Connection
|
||||||
|
</button>
|
||||||
|
<button class="btn-primary" @click="updateModel">
|
||||||
|
Confirm
|
||||||
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</FormDialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-input:focus {
|
.search-input:focus {
|
||||||
border-color: #f97316;
|
border-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input::placeholder {
|
.search-input::placeholder {
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.kb-dialog :deep(.el-dialog__headerbtn:hover .el-dialog__close) {
|
.kb-dialog :deep(.el-dialog__headerbtn:hover .el-dialog__close) {
|
||||||
color: #f97316;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kb-dialog :deep(.el-dialog__body) {
|
.kb-dialog :deep(.el-dialog__body) {
|
||||||
@@ -135,8 +135,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.kb-dialog :deep(.el-button--primary) {
|
.kb-dialog :deep(.el-button--primary) {
|
||||||
background-color: #f97316;
|
background-color: #ffffff;
|
||||||
border-color: #f97316;
|
border-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kb-dialog :deep(.el-button--primary:hover) {
|
.kb-dialog :deep(.el-button--primary:hover) {
|
||||||
@@ -226,6 +226,20 @@
|
|||||||
border-color: rgba(54, 191, 250, 0.25);
|
border-color: rgba(54, 191, 250, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-item.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item.disabled .menu-content {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-check {
|
||||||
|
color: #22c55e;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.menu-icon {
|
.menu-icon {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
@@ -241,9 +255,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-item.active .menu-icon {
|
.menu-item.active .menu-icon {
|
||||||
background: linear-gradient(135deg, #36bffa 0%, #0ea5e9 100%);
|
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||||
color: white;
|
color: white;
|
||||||
box-shadow: 0 4px 12px rgba(54, 191, 250, 0.3);
|
box-shadow: 0 4px 12px rgba(249, 115, 22, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-content {
|
.menu-content {
|
||||||
@@ -262,7 +276,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-item.active .menu-title {
|
.menu-item.active .menu-title {
|
||||||
color: #36bffa;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-desc {
|
.menu-desc {
|
||||||
@@ -272,7 +286,7 @@
|
|||||||
|
|
||||||
.menu-arrow {
|
.menu-arrow {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #36bffa;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 右侧内容区 */
|
/* 右侧内容区 */
|
||||||
@@ -311,11 +325,11 @@
|
|||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
background: linear-gradient(135deg, rgba(54, 191, 250, 0.2) 0%, rgba(14, 165, 233, 0.12) 100%);
|
background: linear-gradient(135deg, rgba(249, 115, 22, 0.2) 0%, rgba(234, 88, 12, 0.12) 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #36bffa;
|
color: #ffffff;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,11 +356,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.kb-form :deep(.el-input__wrapper:hover) {
|
.kb-form :deep(.el-input__wrapper:hover) {
|
||||||
border-color: #36bffa;
|
border-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kb-form :deep(.el-input__wrapper.is-focus) {
|
.kb-form :deep(.el-input__wrapper.is-focus) {
|
||||||
border-color: #36bffa;
|
border-color: #ffffff;
|
||||||
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,11 +384,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.kb-form :deep(.el-textarea__inner:hover) {
|
.kb-form :deep(.el-textarea__inner:hover) {
|
||||||
border-color: #36bffa;
|
border-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kb-form :deep(.el-textarea__inner:focus) {
|
.kb-form :deep(.el-textarea__inner:focus) {
|
||||||
border-color: #36bffa;
|
border-color: #ffffff;
|
||||||
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,11 +405,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.kb-form :deep(.el-select .el-input__wrapper:hover) {
|
.kb-form :deep(.el-select .el-input__wrapper:hover) {
|
||||||
border-color: #36bffa;
|
border-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kb-form :deep(.el-select .el-input__wrapper.is-focus) {
|
.kb-form :deep(.el-select .el-input__wrapper.is-focus) {
|
||||||
border-color: #36bffa;
|
border-color: #ffffff;
|
||||||
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,7 +422,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.provider-option i {
|
.provider-option i {
|
||||||
color: #36bffa;
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 模型选项 */
|
||||||
|
.model-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
color: #e8eaed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-option .model-name {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-option .model-info {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parsing 配置样式 */
|
/* Parsing 配置样式 */
|
||||||
@@ -454,8 +485,8 @@
|
|||||||
|
|
||||||
/* Switch 样式 */
|
/* Switch 样式 */
|
||||||
:deep(.el-switch.is-checked .el-switch__core) {
|
:deep(.el-switch.is-checked .el-switch__core) {
|
||||||
background-color: #36bffa;
|
background-color: #ffffff;
|
||||||
border-color: #36bffa;
|
border-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parsing-divider {
|
.parsing-divider {
|
||||||
@@ -566,7 +597,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.next-btn {
|
.next-btn {
|
||||||
background: linear-gradient(135deg, #36bffa 0%, #0ea5e9 100%);
|
background: linear-gradient(135deg, #ffffff 0%, #0ea5e9 100%);
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
@@ -580,7 +611,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.next-btn:hover {
|
.next-btn:hover {
|
||||||
background: linear-gradient(135deg, #4dc3ff 0%, #36bffa 100%);
|
background: linear-gradient(135deg, #4dc3ff 0%, #ffffff 100%);
|
||||||
box-shadow: 0 6px 16px rgba(54, 191, 250, 0.35);
|
box-shadow: 0 6px 16px rgba(54, 191, 250, 0.35);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,14 +65,33 @@
|
|||||||
|
|
||||||
/* Model Type 标签 */
|
/* Model Type 标签 */
|
||||||
.model-type-tag {
|
.model-type-tag {
|
||||||
background-color: rgba(249, 115, 22, 0.2);
|
|
||||||
color: #f97316;
|
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 不同类型的不同颜色 */
|
||||||
|
.model-type-tag.chat {
|
||||||
|
background-color: rgba(59, 130, 246, 0.2);
|
||||||
|
color: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-type-tag.embedding {
|
||||||
|
background-color: rgba(34, 197, 94, 0.2);
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-type-tag.rerank {
|
||||||
|
background-color: rgba(168, 85, 247, 0.2);
|
||||||
|
color: #a855f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-type-tag.vlm {
|
||||||
|
background-color: rgba(249, 115, 22, 0.2);
|
||||||
|
color: #f97316;
|
||||||
|
}
|
||||||
|
|
||||||
/* 操作按钮 */
|
/* 操作按钮 */
|
||||||
.btn-icon {
|
.btn-icon {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export function useModelSettings() {
|
|||||||
|
|
||||||
// 默认 Base URL 映射
|
// 默认 Base URL 映射
|
||||||
const defaultBaseUrls: Record<string, string> = {
|
const defaultBaseUrls: Record<string, string> = {
|
||||||
|
OpenAI: 'http://localhost:1234/v1',
|
||||||
|
Ollama: 'http://localhost:11434',
|
||||||
ali: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
ali: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +122,7 @@ export function useModelSettings() {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
provider: newModelForm.value.provider,
|
provider: newModelForm.value.provider,
|
||||||
model: newModelForm.value.model,
|
model: newModelForm.value.model,
|
||||||
|
model_type: newModelForm.value.modelType || 'chat',
|
||||||
api_key: newModelForm.value.apiKey,
|
api_key: newModelForm.value.apiKey,
|
||||||
base_url: newModelForm.value.baseUrl,
|
base_url: newModelForm.value.baseUrl,
|
||||||
api_endpoint: newModelForm.value.apiEndpoint,
|
api_endpoint: newModelForm.value.apiEndpoint,
|
||||||
|
|||||||
Reference in New Issue
Block a user