|
|
|
@@ -218,6 +218,7 @@
|
|
|
|
|
<el-option label="多行" value="textarea" />
|
|
|
|
|
<el-option label="数字" value="number" />
|
|
|
|
|
<el-option label="开关" value="boolean" />
|
|
|
|
|
<el-option label="JSON" value="json" />
|
|
|
|
|
</el-select>
|
|
|
|
|
<el-input v-model="param.defaultValue" placeholder="默认值" style="width: 100px" />
|
|
|
|
|
<el-checkbox v-model="param.required">必填</el-checkbox>
|
|
|
|
@@ -298,6 +299,7 @@
|
|
|
|
|
<el-input v-if="field.type === 'textarea'" v-model="field.value" type="textarea" :rows="3" :placeholder="field.name" @input="syncEndpointJsonFromFields" />
|
|
|
|
|
<el-input-number v-if="field.type === 'number'" v-model="field.value" :placeholder="field.name" @change="syncEndpointJsonFromFields" style="width: 100%" />
|
|
|
|
|
<el-switch v-if="field.type === 'boolean'" v-model="field.value" @change="syncEndpointJsonFromFields" />
|
|
|
|
|
<el-input v-if="field.type === 'json'" v-model="field.value" type="textarea" :rows="4" :placeholder="field.placeholder || field.name" @input="syncEndpointJsonFromFields" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<el-collapse style="margin-top: 12px">
|
|
|
|
@@ -336,6 +338,8 @@ import {
|
|
|
|
|
deleteAiEndpoint,
|
|
|
|
|
deleteAiProvider,
|
|
|
|
|
deleteAiScene,
|
|
|
|
|
getEndpointTestTemplate,
|
|
|
|
|
getSceneTestTemplate,
|
|
|
|
|
listAiCallLogs,
|
|
|
|
|
listAiEndpoints,
|
|
|
|
|
listAiProviders,
|
|
|
|
@@ -348,7 +352,7 @@ import {
|
|
|
|
|
testAiRuntime,
|
|
|
|
|
testEndpointRuntime
|
|
|
|
|
} from '@/api/aiconfig'
|
|
|
|
|
import type { AiCallLog, AiEndpointConfig, AiProvider, AiRuntimeTestResponse, AiSceneBinding, TestParamField, ParamDefinition } from '@/types/aiconfig'
|
|
|
|
|
import type { AiCallLog, AiEndpointConfig, AiProvider, AiRuntimeTestResponse, AiSceneBinding, TestParamField, ParamDefinition, AiTestTemplateResponse } from '@/types/aiconfig'
|
|
|
|
|
|
|
|
|
|
const activeTab = ref('providers')
|
|
|
|
|
const loading = ref(false)
|
|
|
|
@@ -478,27 +482,67 @@ function openScene(row?: AiSceneBinding) {
|
|
|
|
|
sceneDialog.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openRuntimeTest() {
|
|
|
|
|
async function openRuntimeTest() {
|
|
|
|
|
testForm.sceneCode = scenes.value.find(item => item.isEnabled === 1 && item.endpointId)?.sceneCode || scenes.value[0]?.sceneCode || ''
|
|
|
|
|
testResult.value = null
|
|
|
|
|
nonStreamResult.value = null
|
|
|
|
|
await loadSceneTemplate(testForm.sceneCode)
|
|
|
|
|
testDialog.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openSceneRuntimeTest(row: AiSceneBinding) {
|
|
|
|
|
async function openSceneRuntimeTest(row: AiSceneBinding) {
|
|
|
|
|
testForm.sceneCode = row.sceneCode
|
|
|
|
|
testResult.value = null
|
|
|
|
|
nonStreamResult.value = null
|
|
|
|
|
await loadSceneTemplate(row.sceneCode)
|
|
|
|
|
testDialog.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openEndpointTest(row: AiEndpointConfig) {
|
|
|
|
|
async function openEndpointTest(row: AiEndpointConfig) {
|
|
|
|
|
if (!row.id) return
|
|
|
|
|
endpointTestRow.value = row
|
|
|
|
|
endpointParamFields.value = parseParamFields(row.defaultInputs)
|
|
|
|
|
endpointInputsJson.value = buildInputsJson(endpointParamFields.value)
|
|
|
|
|
endpointTestResult.value = null
|
|
|
|
|
endpointNonStreamResult.value = null
|
|
|
|
|
endpointTestDialog.value = true
|
|
|
|
|
endpointTesting.value = true
|
|
|
|
|
try {
|
|
|
|
|
const res = await getEndpointTestTemplate(row.id!)
|
|
|
|
|
applyEndpointTemplate(res.data as AiTestTemplateResponse)
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
ElMessage.warning(error?.message || '测试样例加载失败,已使用本地默认参数')
|
|
|
|
|
} finally {
|
|
|
|
|
endpointTesting.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadSceneTemplate(sceneCode: string) {
|
|
|
|
|
if (!sceneCode) {
|
|
|
|
|
testInputsJson.value = '{\n "prompt": "请用一句中文回复测试成功。"\n}'
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const res = await getSceneTestTemplate(sceneCode)
|
|
|
|
|
const template = res.data as AiTestTemplateResponse
|
|
|
|
|
testInputsJson.value = JSON.stringify(template.inputs || {}, null, 2)
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
testInputsJson.value = '{\n "prompt": "请用一句中文回复测试成功。"\n}'
|
|
|
|
|
ElMessage.warning(error?.message || '场景测试样例加载失败,已使用通用测试参数')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function applyEndpointTemplate(template: AiTestTemplateResponse) {
|
|
|
|
|
const inputs = template.inputs || {}
|
|
|
|
|
endpointInputsJson.value = JSON.stringify(inputs, null, 2)
|
|
|
|
|
endpointParamFields.value = (template.paramFields || []).map(field => ({
|
|
|
|
|
...field,
|
|
|
|
|
value: field.type === 'json'
|
|
|
|
|
? JSON.stringify(field.value ?? {}, null, 2)
|
|
|
|
|
: field.value,
|
|
|
|
|
defaultValue: field.value,
|
|
|
|
|
required: Boolean(field.required)
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseParamFields(defaultInputs?: string): TestParamField[] {
|
|
|
|
@@ -533,16 +577,28 @@ function parseParamFields(defaultInputs?: string): TestParamField[] {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function inferParamType(val: any): 'string' | 'textarea' | 'number' | 'boolean' {
|
|
|
|
|
function inferParamType(val: any): 'string' | 'textarea' | 'number' | 'boolean' | 'json' {
|
|
|
|
|
if (typeof val === 'number') return 'number'
|
|
|
|
|
if (typeof val === 'boolean') return 'boolean'
|
|
|
|
|
if (val && typeof val === 'object') return 'json'
|
|
|
|
|
if (typeof val === 'string' && val.length > 80) return 'textarea'
|
|
|
|
|
return 'string'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildInputsJson(fields: TestParamField[]): string {
|
|
|
|
|
const obj: Record<string, any> = {}
|
|
|
|
|
fields.forEach(f => { obj[f.name] = f.value })
|
|
|
|
|
fields.forEach(f => {
|
|
|
|
|
if (!f.name) return
|
|
|
|
|
if (f.type === 'json') {
|
|
|
|
|
try {
|
|
|
|
|
obj[f.name] = typeof f.value === 'string' ? JSON.parse(f.value || '{}') : f.value
|
|
|
|
|
} catch {
|
|
|
|
|
obj[f.name] = f.value
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
obj[f.name] = f.value
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return JSON.stringify(obj, null, 2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -555,7 +611,9 @@ function syncEndpointFieldsFromJson() {
|
|
|
|
|
const parsed = JSON.parse(endpointInputsJson.value || '{}')
|
|
|
|
|
endpointParamFields.value.forEach(field => {
|
|
|
|
|
if (field.name in parsed) {
|
|
|
|
|
field.value = parsed[field.name]
|
|
|
|
|
field.value = field.type === 'json'
|
|
|
|
|
? JSON.stringify(parsed[field.name] ?? {}, null, 2)
|
|
|
|
|
: parsed[field.name]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} catch { /* ignore parse errors */ }
|
|
|
|
@@ -726,6 +784,7 @@ async function submitRuntimeTest() {
|
|
|
|
|
|
|
|
|
|
async function submitEndpointNonStreamTest() {
|
|
|
|
|
if (!endpointTestRow.value) return
|
|
|
|
|
if (!validateEndpointFields()) return
|
|
|
|
|
let inputs: Record<string, any>
|
|
|
|
|
try {
|
|
|
|
|
inputs = JSON.parse(endpointInputsJson.value || '{}')
|
|
|
|
@@ -757,6 +816,7 @@ async function submitEndpointNonStreamTest() {
|
|
|
|
|
|
|
|
|
|
async function submitEndpointStreamTest() {
|
|
|
|
|
if (!endpointTestRow.value) return
|
|
|
|
|
if (!validateEndpointFields()) return
|
|
|
|
|
let inputs: Record<string, any>
|
|
|
|
|
try {
|
|
|
|
|
inputs = JSON.parse(endpointInputsJson.value || '{}')
|
|
|
|
@@ -796,6 +856,26 @@ async function submitEndpointStreamTest() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function validateEndpointFields() {
|
|
|
|
|
for (const field of endpointParamFields.value) {
|
|
|
|
|
if (!field.required) continue
|
|
|
|
|
const value = field.value
|
|
|
|
|
if (value === null || value === undefined || String(value).trim() === '') {
|
|
|
|
|
ElMessage.error(`请填写必填参数:${field.label || field.name}`)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const field of endpointParamFields.value.filter(item => item.type === 'json')) {
|
|
|
|
|
try {
|
|
|
|
|
JSON.parse(field.value || '{}')
|
|
|
|
|
} catch {
|
|
|
|
|
ElMessage.error(`参数 ${field.label || field.name} 不是有效 JSON`)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(loadAll)
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|