|
@@ -1,6 +1,8 @@
|
|
|
import { ref, h, createApp, Teleport, type Directive, type App } from 'vue';
|
|
import { ref, h, createApp, Teleport, type Directive, type App } from 'vue';
|
|
|
import Antd from 'ant-design-vue';
|
|
import Antd from 'ant-design-vue';
|
|
|
import { fetchTunnelInterpretation, fetchDeviceInterpretation } from './api';
|
|
import { fetchTunnelInterpretation, fetchDeviceInterpretation } from './api';
|
|
|
|
|
+// [审计-第四轮 M2] 重放自我方 fix/base-agent-token(M1/M2/M3):formatClickError 需要识别 StreamError 的 status
|
|
|
|
|
+import { StreamError } from '/@/views/ventAI/manageAssistent/api/client';
|
|
|
import type { TodoItem, ToolCallRecord, SSERawEvent } from './types';
|
|
import type { TodoItem, ToolCallRecord, SSERawEvent } from './types';
|
|
|
import DataPickerModal from './components/DataPickerModal.vue';
|
|
import DataPickerModal from './components/DataPickerModal.vue';
|
|
|
import aiCloseIcon from '@/assets/images/ventAI/dataPicker/AI-close.svg';
|
|
import aiCloseIcon from '@/assets/images/ventAI/dataPicker/AI-close.svg';
|
|
@@ -338,6 +340,43 @@ export function useDataPicker(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 点击解读失败时,把「结构化错误」转成用户能看懂的中文文案。
|
|
|
|
|
+ *
|
|
|
|
|
+ * [审计-第四轮 M2] 重放自我方 fix/base-agent-token(M1/M2/M3):
|
|
|
|
|
+ * 背景:TfAgents 对失败请求返回结构化错误体,形如
|
|
|
|
|
+ * HTTP 429 → { "detail": { "code": "...", "message": "今日额度已用完…", "retry_after": 900 } }
|
|
|
|
|
+ * HTTP 413 → { "detail": { "code": "...", "message": "…" } }
|
|
|
|
|
+ * HTTP 404 → { "detail": { "code": "...", "message": "…" } }
|
|
|
|
|
+ * 原行为:只有一行 `error.value = e?.message || '请求失败'`,429/413/404 只能显示 `HTTP错误: xxx`。
|
|
|
|
|
+ * 现行为:复用 manageAssistent/api/client.ts 的 throwStreamHttpError(不新写一套解析器)
|
|
|
|
|
+ * + 本函数的兜底文案,得到可读提示。
|
|
|
|
|
+ * 为什么:429 带 retryAfter 换算分钟、413/404 服务端无文案时用本地兜底。
|
|
|
|
|
+ * 保守策略:只对「确认是 StreamError」的情况改写文案;普通 Error 一律走 fallback,
|
|
|
|
|
+ * 保证行为不回归(例如网络中断、JSON 解析失败等仍显示原始 message)。
|
|
|
|
|
+ */
|
|
|
|
|
+ function formatClickError(e: any): string {
|
|
|
|
|
+ const fallback = e?.message || '请求失败';
|
|
|
|
|
+ // 429:额度用完 / 并发闸 / 每用户任务上限,服务端会给 retry_after(秒)
|
|
|
|
|
+ if (e instanceof StreamError && e.status === 429) {
|
|
|
|
|
+ if (typeof e.retryAfter === 'number') {
|
|
|
|
|
+ const minutes = Math.ceil(e.retryAfter / 60);
|
|
|
|
|
+ // 不足 1 分钟时不显示「0 分钟」
|
|
|
|
|
+ return `${fallback}(约${minutes < 1 ? '不到 1 分钟' : `${minutes}分钟`}后恢复)`;
|
|
|
|
|
+ }
|
|
|
|
|
+ return `${fallback}(请稍后再试)`;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 413:上传体积超限;仅当服务端没给出可读文案时才用本地兜底
|
|
|
|
|
+ if (e instanceof StreamError && e.status === 413 && (!e.message || e.message === 'HTTP错误: 413')) {
|
|
|
|
|
+ return '附件体积超过上限,请减小文件后重试';
|
|
|
|
|
+ }
|
|
|
|
|
+ // 404:会话不存在或无权访问(TfAgents 的会话归属校验)
|
|
|
|
|
+ if (e instanceof StreamError && e.status === 404 && (!e.message || e.message === 'HTTP错误: 404')) {
|
|
|
|
|
+ return '会话不存在或无权访问';
|
|
|
|
|
+ }
|
|
|
|
|
+ return fallback;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 点击触发
|
|
// 点击触发
|
|
|
function handleClick(item: any) {
|
|
function handleClick(item: any) {
|
|
|
modalVisible.value = true;
|
|
modalVisible.value = true;
|
|
@@ -356,7 +395,8 @@ export function useDataPicker(
|
|
|
|
|
|
|
|
request
|
|
request
|
|
|
.catch((e: any) => {
|
|
.catch((e: any) => {
|
|
|
- error.value = e?.message || '请求失败';
|
|
|
|
|
|
|
+ // [审计-第四轮 M2] 重放自我方 fix/base-agent-token(M1/M2/M3):结构化错误 → 可读文案
|
|
|
|
|
+ error.value = formatClickError(e);
|
|
|
})
|
|
})
|
|
|
.finally(() => {
|
|
.finally(() => {
|
|
|
loading.value = false;
|
|
loading.value = false;
|