perf: Ai model validate

This commit is contained in:
fit2cloud-chenyw
2025-07-30 14:19:37 +08:00
parent f559d2b2a4
commit 907a362271
4 changed files with 118 additions and 30 deletions
+27 -15
View File
@@ -1,5 +1,7 @@
import json
from typing import List, Union
from fastapi.responses import StreamingResponse
from apps.ai_model.model_factory import LLMConfig, LLMFactory
from apps.system.schemas.ai_model_schema import AiModelConfigItem, AiModelCreator, AiModelEditor, AiModelGridItem
from fastapi import APIRouter, Query
@@ -14,21 +16,31 @@ router = APIRouter(tags=["system/aimodel"], prefix="/system/aimodel")
@router.post("/status")
async def check_llm(info: AiModelCreator, trans: Trans):
try:
additional_params = {item.key: item.val for item in info.config_list}
config = LLMConfig(
model_type="openai",
model_name=info.base_model,
api_key=info.api_key,
api_base_url=info.api_domain,
additional_params=additional_params,
)
llm_instance = LLMFactory.create_llm(config)
result = llm_instance.llm.invoke("who are you?")
SQLBotLogUtil.info(f"check_llm result: {result}")
except Exception as e:
SQLBotLogUtil.error(f"Error checking LLM: {e}")
raise Exception(trans('i18n_llm.validate_error', msg = str(e)))
async def generate():
try:
additional_params = {item.key: item.val for item in info.config_list}
config = LLMConfig(
model_type="openai",
model_name=info.base_model,
api_key=info.api_key,
api_base_url=info.api_domain,
additional_params=additional_params,
)
llm_instance = LLMFactory.create_llm(config)
res = llm_instance.llm.stream("who are you?")
for chunk in res:
if chunk and chunk.content:
SQLBotLogUtil.info(chunk)
yield json.dumps({"content": chunk.content}) + "\n"
except Exception as e:
SQLBotLogUtil.error(f"Error checking LLM: {e}")
error_msg = trans('i18n_llm.validate_error', msg=str(e))
yield json.dumps({"error": error_msg}) + "\n"
return StreamingResponse(generate(), media_type="application/x-ndjson")
@router.get("", response_model=list[AiModelGridItem])
async def query(
+1 -2
View File
@@ -8,6 +8,5 @@ export const modelApi = {
delete: (id: number) => request.delete(`/system/aimodel/${id}`),
query: (id: number) => request.get(`/system/aimodel/${id}`),
setDefault: (id: number) => request.put(`/system/aimodel/default/${id}`),
check: (data: any) =>
request.post('/system/aimodel/status', data, { requestOptions: { silent: true } }),
check: (data: any) => request.fetchStream('/system/aimodel/status', data),
}
+43 -2
View File
@@ -2,7 +2,7 @@
import delIcon from '@/assets/svg/icon_delete.svg'
import edit from '@/assets/svg/icon_edit_outlined.svg'
import { get_supplier } from '@/entity/supplier'
import { computed } from 'vue'
import { computed, ref } from 'vue'
const props = withDefaults(
defineProps<{
name: string
@@ -21,12 +21,22 @@ const props = withDefaults(
supplier: 0,
}
)
const errorMsg = ref('')
const current_supplier = computed(() => {
if (!props.supplier) {
return null
}
return get_supplier(props.supplier)
})
const showErrorMask = (msg?: string) => {
if (!msg) {
return
}
errorMsg.value = msg
setTimeout(() => {
errorMsg.value = ''
}, 3000)
}
const emits = defineEmits(['edit', 'del'])
const handleEdit = () => {
@@ -36,10 +46,17 @@ const handleEdit = () => {
const handleDel = () => {
emits('del', { id: props.id, name: props.name, default_model: props.isDefault })
}
defineExpose({ showErrorMask })
</script>
<template>
<div class="card">
<div
v-loading="!!errorMsg"
class="card"
:element-loading-text="errorMsg"
element-loading-custom-class="model-card-loading"
>
<div class="name-icon">
<img :src="current_supplier?.icon" width="32px" height="32px" />
<span :title="name" class="name ellipsis">{{ name }}</span>
@@ -163,5 +180,29 @@ const handleDel = () => {
display: flex;
}
}
:deep(.model-card-loading) {
border-radius: 12px;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: end;
background-color: rgb(122 122 122 / 87%);
.ed-loading-spinner {
top: auto;
margin: 8px 4px;
display: flex;
position: relative;
justify-content: flex-end;
align-items: center;
width: calc(100% - 8px);
}
svg {
display: none;
}
p {
text-align: left;
color: var(--ed-color-danger);
}
}
}
</style>
+47 -11
View File
@@ -34,7 +34,8 @@ const activeStep = ref(0)
const activeName = ref('')
const activeType = ref('')
const modelFormRef = ref()
const cardRefs = ref<any[]>([])
const showCardError = ref(true)
reactive({
form: {
id: '',
@@ -69,16 +70,46 @@ const defaultModelListWithSearch = computed(() => {
})
})
const modelCheckHandler = (item: any) => {
const modelCheckHandler = async (item: any) => {
const response = await modelApi.check(item)
const reader = response.body.getReader()
const decoder = new TextDecoder()
let checkTimeout = false
setTimeout(() => {
modelApi.check(item).catch((err: any) => {
if (err.response?.data?.msg) {
ElMessage.error(t('model.check_failed', { msg: err.response.data.msg || '' }))
} else {
ElMessage.error(t('model.check_failed', { msg: err.message || '' }))
checkTimeout = true
}, 9000)
let checkMsg = ''
while (true) {
if (checkTimeout) {
break
}
const { done, value } = await reader.read()
if (done) break
const lines = decoder.decode(value).trim().split('\n')
for (const line of lines) {
const data = JSON.parse(line)
if (data.error) {
checkMsg += data.error
} else if (data.content) {
console.debug(data.content)
}
})
}, 1000)
}
}
if (!checkMsg) {
return
}
console.error(checkMsg)
if (!showCardError.value) {
ElMessage.error(checkMsg)
return
}
nextTick(() => {
const index = modelListWithSearch.value.findIndex((el: any) => el.id === item.id)
if (index > -1) {
const currentRef = cardRefs.value[index]
currentRef?.showErrorMask(checkMsg)
}
})
}
const duplicateName = async (item: any) => {
const res = await modelApi.queryAll()
@@ -218,7 +249,11 @@ const preStep = () => {
const saveModel = () => {
modelFormRef.value.submitModel()
}
const setCardRef = (el: any, index: number) => {
if (el) {
cardRefs.value[index] = el
}
}
const search = () => {
modelApi.queryAll().then((res: any) => {
modelList.value = res
@@ -308,8 +343,9 @@ const submit = (item: any) => {
<div v-else class="card-content">
<card
v-for="ele in modelListWithSearch"
v-for="(ele, index) in modelListWithSearch"
:id="ele.id"
:ref="(el: any) => setCardRef(el, index)"
:key="ele.id"
:name="ele.name"
:supplier="ele.supplier"