feat: 增强智能体模型调用兼容能力
- 增加模型流式开关和 HTTP 传输策略配置 - 使用 AgentScope 执行基础连接、流式与 VLM 双阶段验证 - 固定多模态校验图片并统一验证状态展示
This commit is contained in:
@@ -37,6 +37,21 @@ export async function verifyModelConfig(id: string) {
|
||||
return api.get('/api/v1/model/verifyLlmConfig', { params: { id } });
|
||||
}
|
||||
|
||||
export type ModelVerificationStageStatus =
|
||||
| 'FAILED'
|
||||
| 'PARTIAL'
|
||||
| 'PASSED'
|
||||
| 'SKIPPED';
|
||||
|
||||
export interface ModelVerificationData {
|
||||
dimension?: number;
|
||||
effectiveHttpVersion?: string;
|
||||
message?: string;
|
||||
nonStreaming?: ModelVerificationStageStatus;
|
||||
status?: ModelVerificationStageStatus;
|
||||
streaming?: ModelVerificationStageStatus;
|
||||
}
|
||||
|
||||
export interface ModelInvokeConfigPayload {
|
||||
id: string;
|
||||
invokeCode?: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable vue/no-mutating-props */
|
||||
import type {AgentInfo, AgentOption} from '../types';
|
||||
import type { AgentInfo, AgentOption } from '../types';
|
||||
|
||||
import {InfoFilled} from '@element-plus/icons-vue';
|
||||
import { InfoFilled } from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ElInputNumber,
|
||||
ElOption,
|
||||
ElSelect,
|
||||
ElSwitch,
|
||||
ElTooltip,
|
||||
} from 'element-plus';
|
||||
|
||||
@@ -75,6 +76,27 @@ const emit = defineEmits<{ change: [] }>();
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<template #label>
|
||||
<span class="agent-form__label">
|
||||
模型流式响应
|
||||
<ElTooltip
|
||||
content="关闭后,模型会在生成完成后一次性返回回答。"
|
||||
effect="light"
|
||||
placement="top"
|
||||
>
|
||||
<ElIcon class="agent-form__info" aria-label="模型流式响应说明">
|
||||
<InfoFilled />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</span>
|
||||
</template>
|
||||
<ElSwitch
|
||||
v-model="agent.generationConfigJson!.stream"
|
||||
aria-label="模型流式响应"
|
||||
@change="emit('change')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="系统提示词">
|
||||
<ElInput
|
||||
v-model="agent.promptConfigJson!.systemPrompt"
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
createEmptyAgent,
|
||||
useAgentDesignerState,
|
||||
} from './useAgentDesignerState';
|
||||
|
||||
describe('useAgentDesignerState generation stream', () => {
|
||||
it('defaults new and legacy agents to streaming', () => {
|
||||
expect(createEmptyAgent().generationConfigJson?.stream).toBe(true);
|
||||
|
||||
const designer = useAgentDesignerState();
|
||||
designer.reset({ name: '旧智能体' });
|
||||
|
||||
expect(designer.state.agent.generationConfigJson?.stream).toBe(true);
|
||||
expect(designer.buildPayloadAgent().generationConfigJson?.stream).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves an explicitly disabled stream value in the payload', () => {
|
||||
const designer = useAgentDesignerState();
|
||||
designer.reset({
|
||||
name: '非流式智能体',
|
||||
generationConfigJson: { stream: false },
|
||||
});
|
||||
|
||||
expect(designer.state.agent.generationConfigJson?.stream).toBe(false);
|
||||
expect(designer.buildPayloadAgent().generationConfigJson?.stream).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -84,6 +84,7 @@ export function createEmptyAgent(): AgentInfo {
|
||||
categoryId: '',
|
||||
modelId: '',
|
||||
promptConfigJson: { systemPrompt: '' },
|
||||
generationConfigJson: { stream: true },
|
||||
memoryConfigJson: {
|
||||
compressionParameter: {
|
||||
enabled: true,
|
||||
@@ -111,6 +112,10 @@ function normalizeAgent(agent?: AgentInfo): AgentInfo {
|
||||
systemPrompt: '',
|
||||
...source.promptConfigJson,
|
||||
},
|
||||
generationConfigJson: {
|
||||
...source.generationConfigJson,
|
||||
stream: source.generationConfigJson?.stream !== false,
|
||||
},
|
||||
memoryConfigJson: {
|
||||
...memoryConfig,
|
||||
compressionParameter: {
|
||||
@@ -356,6 +361,10 @@ export function useAgentDesignerState() {
|
||||
interactionConfigJson: buildInteractionConfigPayload(
|
||||
state.agent.interactionConfigJson,
|
||||
),
|
||||
generationConfigJson: {
|
||||
...state.agent.generationConfigJson,
|
||||
stream: state.agent.generationConfigJson?.stream !== false,
|
||||
},
|
||||
memoryConfigJson: {
|
||||
...restMemoryConfigJson,
|
||||
compressionParameter: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { llmType } from '#/api';
|
||||
import type { ModelAbilityItem } from '#/views/ai/model/modelUtils/model-ability';
|
||||
import type { VerifyButtonStatus } from '#/views/ai/model/modelUtils/model-verification';
|
||||
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
Edit,
|
||||
Loading,
|
||||
Select,
|
||||
Warning,
|
||||
} from '@element-plus/icons-vue';
|
||||
import {
|
||||
ElButton,
|
||||
@@ -31,6 +33,10 @@ import { $t } from '#/locales';
|
||||
import ModelProviderBadge from '#/views/ai/model/ModelProviderBadge.vue';
|
||||
import { getDefaultModelAbility } from '#/views/ai/model/modelUtils/model-ability';
|
||||
import { mapLlmToModelAbility } from '#/views/ai/model/modelUtils/model-ability-utils';
|
||||
import {
|
||||
getVerifyButtonText as getStatusButtonText,
|
||||
resolveModelVerificationFeedback,
|
||||
} from '#/views/ai/model/modelUtils/model-verification';
|
||||
|
||||
interface ProviderOption {
|
||||
id: string;
|
||||
@@ -63,7 +69,6 @@ const isActionLoading = ref(false);
|
||||
const modelRows = ref<llmType[]>([]);
|
||||
const selectedRows = ref<llmType[]>([]);
|
||||
const lastErrorMessage = ref('');
|
||||
type VerifyButtonStatus = 'error' | 'idle' | 'loading' | 'success';
|
||||
const verifyStatusMap = ref<Record<string, VerifyButtonStatus>>({});
|
||||
|
||||
const filterState = reactive<FilterState>({
|
||||
@@ -121,17 +126,7 @@ const setVerifyStatus = (id: string, status: VerifyButtonStatus) => {
|
||||
};
|
||||
const isVerifying = (row: llmType) => getVerifyStatus(row) === 'loading';
|
||||
const getVerifyButtonText = (row: llmType) => {
|
||||
const status = getVerifyStatus(row);
|
||||
if (status === 'loading') {
|
||||
return '验证中';
|
||||
}
|
||||
if (status === 'success') {
|
||||
return '验证成功';
|
||||
}
|
||||
if (status === 'error') {
|
||||
return '验证失败';
|
||||
}
|
||||
return '验证配置';
|
||||
return getStatusButtonText(getVerifyStatus(row));
|
||||
};
|
||||
const getVerifyButtonIcon = (row: llmType) => {
|
||||
const status = getVerifyStatus(row);
|
||||
@@ -141,6 +136,9 @@ const getVerifyButtonIcon = (row: llmType) => {
|
||||
if (status === 'success') {
|
||||
return CircleCheck;
|
||||
}
|
||||
if (status === 'warning') {
|
||||
return Warning;
|
||||
}
|
||||
if (status === 'error') {
|
||||
return CircleClose;
|
||||
}
|
||||
@@ -285,17 +283,16 @@ const handleVerify = async (row: llmType) => {
|
||||
try {
|
||||
const res = await verifyModelConfig(modelId);
|
||||
|
||||
if (res.errorCode === 0) {
|
||||
setVerifyStatus(modelId, 'success');
|
||||
if (row.modelType === 'embeddingModel' && res?.data?.dimension) {
|
||||
ElMessage.success(`验证成功,向量维度:${res.data.dimension}`);
|
||||
} else {
|
||||
ElMessage.success('验证成功');
|
||||
}
|
||||
const feedback = resolveModelVerificationFeedback(res, row.modelType);
|
||||
setVerifyStatus(modelId, feedback.status);
|
||||
|
||||
if (feedback.status === 'success') {
|
||||
ElMessage.success(feedback.message);
|
||||
} else if (feedback.status === 'warning') {
|
||||
ElMessage.warning(feedback.message);
|
||||
} else {
|
||||
setVerifyStatus(modelId, 'error');
|
||||
if (!res.message) {
|
||||
ElMessage.error($t('ui.actionMessage.operationFailed'));
|
||||
ElMessage.error(feedback.message);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -717,6 +714,10 @@ defineExpose({
|
||||
color: hsl(var(--success));
|
||||
}
|
||||
|
||||
.active-workspace__verify-btn.is-warning {
|
||||
color: hsl(var(--warning));
|
||||
}
|
||||
|
||||
.active-workspace__verify-btn.is-error {
|
||||
color: hsl(var(--destructive));
|
||||
}
|
||||
@@ -732,6 +733,7 @@ defineExpose({
|
||||
}
|
||||
|
||||
.active-workspace__verify-icon.is-success,
|
||||
.active-workspace__verify-icon.is-warning,
|
||||
.active-workspace__verify-icon.is-error {
|
||||
animation: active-workspace-verify-pop 0.32s ease;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,14 @@ import { computed, reactive, ref, watch } from 'vue';
|
||||
import { EasyFlowFormModal } from '@easyflow/common-ui';
|
||||
import { IconifyIcon } from '@easyflow/icons';
|
||||
|
||||
import { ElForm, ElFormItem, ElInput, ElMessage } from 'element-plus';
|
||||
import {
|
||||
ElForm,
|
||||
ElFormItem,
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElOption,
|
||||
ElSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import { api } from '#/api/request';
|
||||
import { $t } from '#/locales';
|
||||
@@ -19,6 +26,13 @@ import {
|
||||
resetModelAbility,
|
||||
} from '#/views/ai/model/modelUtils/model-ability-utils';
|
||||
|
||||
type AgentHttpVersionPolicy = 'AUTO' | 'HTTP_1_1' | 'HTTP_2_PREFERRED';
|
||||
|
||||
interface ModelOptions {
|
||||
agentHttpVersionPolicy: AgentHttpVersionPolicy;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface FormData {
|
||||
id?: string;
|
||||
modelType: string;
|
||||
@@ -39,6 +53,7 @@ interface FormData {
|
||||
supportVideo: boolean;
|
||||
supportImageB64Only: boolean;
|
||||
supportToolMessage: boolean;
|
||||
options: ModelOptions;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
@@ -85,8 +100,39 @@ const formData = reactive<FormData>({
|
||||
supportVideo: false,
|
||||
supportImageB64Only: false,
|
||||
supportToolMessage: true,
|
||||
options: {
|
||||
agentHttpVersionPolicy: 'AUTO',
|
||||
},
|
||||
});
|
||||
|
||||
const agentHttpVersionOptions = [
|
||||
{ label: '自动', value: 'AUTO' },
|
||||
{ label: 'HTTP/1.1 兼容', value: 'HTTP_1_1' },
|
||||
{ label: 'HTTP/2 优先', value: 'HTTP_2_PREFERRED' },
|
||||
] as const;
|
||||
|
||||
const normalizeAgentHttpVersionPolicy = (
|
||||
value?: unknown,
|
||||
): AgentHttpVersionPolicy => {
|
||||
if (value === 'HTTP_1_1' || value === 'HTTP_2_PREFERRED') {
|
||||
return value;
|
||||
}
|
||||
return 'AUTO';
|
||||
};
|
||||
|
||||
const normalizeModelOptions = (options?: unknown): ModelOptions => {
|
||||
const source =
|
||||
options && typeof options === 'object' && !Array.isArray(options)
|
||||
? (options as Record<string, unknown>)
|
||||
: {};
|
||||
return {
|
||||
...source,
|
||||
agentHttpVersionPolicy: normalizeAgentHttpVersionPolicy(
|
||||
source.agentHttpVersionPolicy,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const modelAbility = ref<ModelAbilityItem[]>(getDefaultModelAbility());
|
||||
const visibleModelAbility = computed(() =>
|
||||
modelAbility.value.filter(
|
||||
@@ -189,6 +235,7 @@ const resetFormData = () => {
|
||||
supportImageB64Only: false,
|
||||
supportFree: false,
|
||||
supportToolMessage: true,
|
||||
options: normalizeModelOptions(),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -231,6 +278,7 @@ defineExpose({
|
||||
supportFree: item.supportFree || false,
|
||||
supportToolMessage:
|
||||
item.supportToolMessage === undefined ? true : item.supportToolMessage,
|
||||
options: normalizeModelOptions(item.options),
|
||||
});
|
||||
selectedModelType.value = normalizeSelectableModelType(item.modelType);
|
||||
if (selectedModelType.value) {
|
||||
@@ -386,6 +434,20 @@ const save = async () => {
|
||||
</div>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem v-if="!hasSpecialModelType" label="Agent HTTP 传输">
|
||||
<ElSelect
|
||||
v-model="formData.options.agentHttpVersionPolicy"
|
||||
aria-label="Agent HTTP 传输"
|
||||
>
|
||||
<ElOption
|
||||
v-for="item in agentHttpVersionOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
</div>
|
||||
</ElForm>
|
||||
</div>
|
||||
|
||||
@@ -12,10 +12,11 @@ import {
|
||||
ElSelect,
|
||||
} from 'element-plus';
|
||||
|
||||
import { api } from '#/api/request';
|
||||
import { getModelList, verifyModelConfig } from '#/api/ai/llm';
|
||||
import { $t } from '#/locales';
|
||||
import { resolveModelVerificationFeedback } from '#/views/ai/model/modelUtils/model-verification';
|
||||
|
||||
type VerifyStatus = 'error' | 'idle' | 'success';
|
||||
type VerifyStatus = 'error' | 'idle' | 'success' | 'warning';
|
||||
|
||||
const options = ref<any[]>([]);
|
||||
const modelType = ref('');
|
||||
@@ -39,11 +40,15 @@ const resultTitle = computed(() => {
|
||||
return '验证失败';
|
||||
}
|
||||
|
||||
if (verifyStatus.value === 'warning') {
|
||||
return '流式不可用';
|
||||
}
|
||||
|
||||
return '等待验证';
|
||||
});
|
||||
|
||||
const getLlmList = async (providerId: string) => {
|
||||
const res = await api.get(`/api/v1/model/list?providerId=${providerId}`, {});
|
||||
const res = await getModelList({ providerId });
|
||||
if (res.errorCode === 0) {
|
||||
options.value = res.data;
|
||||
}
|
||||
@@ -87,17 +92,21 @@ const save = async () => {
|
||||
|
||||
try {
|
||||
await formDataRef.value.validate();
|
||||
const res = await api.get(
|
||||
`/api/v1/model/verifyLlmConfig?id=${formData.llmId}`,
|
||||
{},
|
||||
);
|
||||
const res = await verifyModelConfig(formData.llmId);
|
||||
|
||||
if (res.errorCode === 0) {
|
||||
verifyStatus.value = 'success';
|
||||
verifyMessage.value = $t('llm.testSuccess');
|
||||
ElMessage.success($t('llm.testSuccess'));
|
||||
if (modelType.value === 'embeddingModel' && res?.data?.dimension) {
|
||||
vectorDimension.value = res.data.dimension;
|
||||
const feedback = resolveModelVerificationFeedback(res, modelType.value);
|
||||
verifyStatus.value = feedback.status;
|
||||
verifyMessage.value = feedback.message;
|
||||
if (feedback.status === 'warning') {
|
||||
ElMessage.warning(feedback.message);
|
||||
} else if (feedback.status === 'success') {
|
||||
ElMessage.success(feedback.message);
|
||||
} else {
|
||||
ElMessage.error(feedback.message);
|
||||
}
|
||||
if (modelType.value === 'embeddingModel' && feedback.dimension) {
|
||||
vectorDimension.value = String(feedback.dimension);
|
||||
}
|
||||
} else {
|
||||
verifyStatus.value = 'error';
|
||||
@@ -136,9 +145,7 @@ const save = async () => {
|
||||
<section class="verify-modal__section">
|
||||
<div class="verify-modal__section-head">
|
||||
<h3>1. 选择待验证模型</h3>
|
||||
<p>
|
||||
会用当前保存的服务商配置发起一次真实请求,帮助你确认密钥和路径是否正确。
|
||||
</p>
|
||||
<p>会用当前保存的配置检查基础连接和流式响应。</p>
|
||||
</div>
|
||||
|
||||
<ElForm
|
||||
@@ -170,13 +177,14 @@ const save = async () => {
|
||||
<section class="verify-modal__section verify-modal__section--result">
|
||||
<div class="verify-modal__section-head">
|
||||
<h3>2. 查看验证结果</h3>
|
||||
<p>成功后会返回可用状态;如果是向量模型,还会展示向量维度。</p>
|
||||
<p>会展示可用状态;向量模型还会展示向量维度。</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="verify-result-card"
|
||||
:class="{
|
||||
'is-success': verifyStatus === 'success',
|
||||
'is-warning': verifyStatus === 'warning',
|
||||
'is-error': verifyStatus === 'error',
|
||||
}"
|
||||
>
|
||||
@@ -253,6 +261,11 @@ const save = async () => {
|
||||
border-color: hsl(var(--success) / 36%);
|
||||
}
|
||||
|
||||
.verify-result-card.is-warning {
|
||||
background: hsl(var(--warning) / 6%);
|
||||
border-color: hsl(var(--warning) / 36%);
|
||||
}
|
||||
|
||||
.verify-result-card.is-error {
|
||||
background: hsl(var(--destructive) / 5%);
|
||||
border-color: hsl(var(--destructive) / 36%);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { PropType } from 'vue';
|
||||
|
||||
import type { llmType } from '#/api';
|
||||
import type { ModelAbilityItem } from '#/views/ai/model/modelUtils/model-ability';
|
||||
import type { VerifyButtonStatus } from '#/views/ai/model/modelUtils/model-verification';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
Edit,
|
||||
Loading,
|
||||
Select,
|
||||
Warning,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { ElButton, ElIcon, ElMessage, ElTag } from 'element-plus';
|
||||
|
||||
@@ -20,6 +22,10 @@ import { verifyModelConfig } from '#/api/ai/llm';
|
||||
import ModelProviderBadge from '#/views/ai/model/ModelProviderBadge.vue';
|
||||
import { getDefaultModelAbility } from '#/views/ai/model/modelUtils/model-ability';
|
||||
import { mapLlmToModelAbility } from '#/views/ai/model/modelUtils/model-ability-utils';
|
||||
import {
|
||||
getVerifyButtonText as getStatusButtonText,
|
||||
resolveModelVerificationFeedback,
|
||||
} from '#/views/ai/model/modelUtils/model-verification';
|
||||
|
||||
const props = defineProps({
|
||||
llmList: {
|
||||
@@ -33,7 +39,6 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const emit = defineEmits(['deleteLlm', 'editLlm']);
|
||||
type VerifyButtonStatus = 'error' | 'idle' | 'loading' | 'success';
|
||||
const verifyStatusMap = ref<Record<string, VerifyButtonStatus>>({});
|
||||
const getModelId = (llm: llmType) =>
|
||||
String((llm as any).id || (llm as any).llmId || (llm as any).modelId || '');
|
||||
@@ -47,17 +52,7 @@ const setVerifyStatus = (id: string, status: VerifyButtonStatus) => {
|
||||
verifyStatusMap.value[id] = status;
|
||||
};
|
||||
const getVerifyButtonText = (llm: llmType) => {
|
||||
const status = getVerifyStatus(llm);
|
||||
if (status === 'loading') {
|
||||
return '验证中';
|
||||
}
|
||||
if (status === 'success') {
|
||||
return '验证成功';
|
||||
}
|
||||
if (status === 'error') {
|
||||
return '验证失败';
|
||||
}
|
||||
return '验证配置';
|
||||
return getStatusButtonText(getVerifyStatus(llm));
|
||||
};
|
||||
const getVerifyButtonIcon = (llm: llmType) => {
|
||||
const status = getVerifyStatus(llm);
|
||||
@@ -67,6 +62,9 @@ const getVerifyButtonIcon = (llm: llmType) => {
|
||||
if (status === 'success') {
|
||||
return CircleCheck;
|
||||
}
|
||||
if (status === 'warning') {
|
||||
return Warning;
|
||||
}
|
||||
if (status === 'error') {
|
||||
return CircleClose;
|
||||
}
|
||||
@@ -97,17 +95,16 @@ const handleVerifyLlm = async (llm: llmType) => {
|
||||
try {
|
||||
const res = await verifyModelConfig(modelId);
|
||||
|
||||
if (res.errorCode === 0) {
|
||||
setVerifyStatus(modelId, 'success');
|
||||
if (llm.modelType === 'embeddingModel' && res?.data?.dimension) {
|
||||
ElMessage.success(`验证成功,向量维度:${res.data.dimension}`);
|
||||
} else {
|
||||
ElMessage.success('验证成功');
|
||||
}
|
||||
const feedback = resolveModelVerificationFeedback(res, llm.modelType);
|
||||
setVerifyStatus(modelId, feedback.status);
|
||||
|
||||
if (feedback.status === 'success') {
|
||||
ElMessage.success(feedback.message);
|
||||
} else if (feedback.status === 'warning') {
|
||||
ElMessage.warning(feedback.message);
|
||||
} else {
|
||||
setVerifyStatus(modelId, 'error');
|
||||
if (!res.message) {
|
||||
ElMessage.error('验证失败');
|
||||
ElMessage.error(feedback.message);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -309,6 +306,10 @@ const getSelectedAbilityTagsForLlm = (llm: llmType): ModelAbilityItem[] => {
|
||||
color: hsl(var(--success));
|
||||
}
|
||||
|
||||
.llm-item__verify-btn.is-warning {
|
||||
color: hsl(var(--warning));
|
||||
}
|
||||
|
||||
.llm-item__verify-btn.is-error {
|
||||
color: hsl(var(--destructive));
|
||||
}
|
||||
@@ -324,6 +325,7 @@ const getSelectedAbilityTagsForLlm = (llm: llmType): ModelAbilityItem[] => {
|
||||
}
|
||||
|
||||
.llm-item__verify-icon.is-success,
|
||||
.llm-item__verify-icon.is-warning,
|
||||
.llm-item__verify-icon.is-error {
|
||||
animation: llm-item-verify-pop 0.32s ease;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
getVerifyButtonText,
|
||||
resolveModelVerificationFeedback,
|
||||
} from '../model-verification';
|
||||
|
||||
describe('model verification helpers', () => {
|
||||
it('双阶段通过时返回成功状态', () => {
|
||||
expect(
|
||||
resolveModelVerificationFeedback(
|
||||
{
|
||||
data: { status: 'PASSED' },
|
||||
errorCode: 0,
|
||||
},
|
||||
'chatModel',
|
||||
),
|
||||
).toEqual({
|
||||
dimension: undefined,
|
||||
message: '验证成功',
|
||||
status: 'success',
|
||||
});
|
||||
});
|
||||
|
||||
it('基础连接通过但流式失败时返回警告状态', () => {
|
||||
const feedback = resolveModelVerificationFeedback(
|
||||
{
|
||||
data: {
|
||||
message: '连接成功,流式响应不可用,可关闭智能体的模型流式响应。',
|
||||
status: 'PARTIAL',
|
||||
},
|
||||
errorCode: 0,
|
||||
},
|
||||
'chatModel',
|
||||
);
|
||||
|
||||
expect(feedback.status).toBe('warning');
|
||||
expect(feedback.message).toContain('流式响应不可用');
|
||||
expect(getVerifyButtonText('warning')).toBe('流式不可用');
|
||||
});
|
||||
|
||||
it('向量模型验证保留维度结果', () => {
|
||||
expect(
|
||||
resolveModelVerificationFeedback(
|
||||
{ data: { dimension: 1024 }, errorCode: 0 },
|
||||
'embeddingModel',
|
||||
),
|
||||
).toEqual({
|
||||
dimension: 1024,
|
||||
message: '验证成功,向量维度:1024',
|
||||
status: 'success',
|
||||
});
|
||||
});
|
||||
|
||||
it('接口失败时返回错误状态', () => {
|
||||
expect(
|
||||
resolveModelVerificationFeedback(
|
||||
{ errorCode: 1, message: '密钥无效' },
|
||||
'chatModel',
|
||||
),
|
||||
).toEqual({ message: '密钥无效', status: 'error' });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import type { ModelVerificationData } from '#/api/ai/llm';
|
||||
|
||||
export type VerifyButtonStatus =
|
||||
| 'error'
|
||||
| 'idle'
|
||||
| 'loading'
|
||||
| 'success'
|
||||
| 'warning';
|
||||
|
||||
export interface ModelVerificationFeedback {
|
||||
dimension?: number;
|
||||
message: string;
|
||||
status: 'error' | 'success' | 'warning';
|
||||
}
|
||||
|
||||
interface ModelVerificationResponse {
|
||||
data?: ModelVerificationData;
|
||||
errorCode: number;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const STREAMING_UNAVAILABLE_MESSAGE =
|
||||
'连接成功,流式响应不可用,可关闭智能体的模型流式响应。';
|
||||
|
||||
export function resolveModelVerificationFeedback(
|
||||
response: ModelVerificationResponse,
|
||||
modelType: string,
|
||||
): ModelVerificationFeedback {
|
||||
if (response.errorCode !== 0) {
|
||||
return {
|
||||
message: response.message || '验证失败',
|
||||
status: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
if (response.data?.status === 'PARTIAL') {
|
||||
return {
|
||||
message: response.data.message || STREAMING_UNAVAILABLE_MESSAGE,
|
||||
status: 'warning',
|
||||
};
|
||||
}
|
||||
|
||||
if (response.data?.status === 'FAILED') {
|
||||
return {
|
||||
message: response.data.message || '验证失败',
|
||||
status: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
const dimension = response.data?.dimension;
|
||||
return {
|
||||
dimension,
|
||||
message:
|
||||
modelType === 'embeddingModel' && dimension
|
||||
? `验证成功,向量维度:${dimension}`
|
||||
: response.data?.message || '验证成功',
|
||||
status: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
export function getVerifyButtonText(status: VerifyButtonStatus): string {
|
||||
if (status === 'loading') {
|
||||
return '验证中';
|
||||
}
|
||||
if (status === 'success') {
|
||||
return '验证成功';
|
||||
}
|
||||
if (status === 'warning') {
|
||||
return '流式不可用';
|
||||
}
|
||||
if (status === 'error') {
|
||||
return '验证失败';
|
||||
}
|
||||
return '验证配置';
|
||||
}
|
||||
Reference in New Issue
Block a user