feat: improve model price error UX with role-aware messages and cleaner UI

- Backend: differentiate error messages for admin vs regular users in price.go
- Backend: include error_code in channel test response for structured error handling
- Frontend: render model_price_error as a styled card in Playground with admin nav button
- Frontend: show inline error details and settings link in channel test modal
- Frontend: parse error codes from both SSE and non-streaming API responses
- i18n: remove redundant "Settings" suffix from setting tab translations (en/fr/ru/ja/vi)
- i18n: update "Group & Model Pricing" translations across all locales
This commit is contained in:
CaIon
2026-04-11 17:19:38 +08:00
parent 040e8c1da8
commit ed7f839911
16 changed files with 224 additions and 84 deletions
+5 -3
View File
@@ -890,7 +890,7 @@ export const useChannelsData = () => {
return Promise.resolve();
}
const { success, message, time } = res.data;
const { success, message, time, error_code } = res.data;
// 更新测试结果
setModelTestResults((prev) => ({
@@ -900,6 +900,7 @@ export const useChannelsData = () => {
message,
time: time || 0,
timestamp: Date.now(),
errorCode: error_code || null,
},
}));
@@ -927,7 +928,7 @@ export const useChannelsData = () => {
);
}
} else {
showError(`${t('模型')} ${model}: ${message}`);
showError(message);
}
} catch (error) {
// 处理网络错误
@@ -939,9 +940,10 @@ export const useChannelsData = () => {
message: error.message || t('网络错误'),
time: 0,
timestamp: Date.now(),
errorCode: null,
},
}));
showError(`${t('模型')} ${model}: ${error.message || t('测试失败')}`);
showError(error.message || t('测试失败'));
} finally {
// 从正在测试的模型集合中移除
setTestingModels((prev) => {
+43 -6
View File
@@ -196,10 +196,17 @@ export const useApiRequest = (
if (!response.ok) {
let errorBody = '';
let parsedError = null;
try {
errorBody = await response.text();
const errorJson = JSON.parse(errorBody);
if (errorJson?.error) {
parsedError = errorJson.error;
}
} catch (e) {
errorBody = '无法读取错误响应体';
if (!errorBody) {
errorBody = '无法读取错误响应体';
}
}
const errorInfo = handleApiError(
@@ -215,9 +222,13 @@ export const useApiRequest = (
}));
setActiveDebugTab(DEBUG_TABS.RESPONSE);
throw new Error(
`HTTP error! status: ${response.status}, body: ${errorBody}`,
const err = new Error(
parsedError?.message ||
`HTTP error! status: ${response.status}, body: ${errorBody}`,
);
err.errorCode = parsedError?.code || null;
err.errorType = parsedError?.type || null;
throw err;
}
const data = await response.json();
@@ -277,6 +288,7 @@ export const useApiRequest = (
newMessages[newMessages.length - 1] = {
...lastMessage,
content: t('请求发生错误: ') + error.message,
errorCode: error.errorCode || null,
status: MESSAGE_STATUS.ERROR,
...autoCollapseState,
};
@@ -379,7 +391,20 @@ export const useApiRequest = (
// 只有在流没有正常完成且连接状态异常时才处理错误
if (!isStreamComplete && source.readyState !== 2) {
console.error('SSE Error:', e);
const errorMessage = e.data || t('请求发生错误');
let errorMessage = e.data || t('请求发生错误');
let errorCode = null;
if (e.data) {
try {
const errorJson = JSON.parse(e.data);
if (errorJson?.error) {
errorMessage = errorJson.error.message || errorMessage;
errorCode = errorJson.error.code || null;
}
} catch (_) {
// not JSON, use raw data as error message
}
}
const errorInfo = handleApiError(new Error(errorMessage));
errorInfo.readyState = source.readyState;
@@ -393,8 +418,19 @@ export const useApiRequest = (
}));
setActiveDebugTab(DEBUG_TABS.RESPONSE);
streamMessageUpdate(errorMessage, 'content');
completeMessage(MESSAGE_STATUS.ERROR);
setMessage((prevMessage) => {
const newMessages = [...prevMessage];
const lastMessage = newMessages[newMessages.length - 1];
if (lastMessage && lastMessage.status !== MESSAGE_STATUS.COMPLETE && lastMessage.status !== MESSAGE_STATUS.ERROR) {
newMessages[newMessages.length - 1] = {
...lastMessage,
content: (lastMessage.content || '') + errorMessage,
errorCode: errorCode,
status: MESSAGE_STATUS.ERROR,
};
}
return newMessages;
});
sseSourceRef.current = null;
source.close();
}
@@ -446,6 +482,7 @@ export const useApiRequest = (
[
setDebugData,
setActiveDebugTab,
setMessage,
streamMessageUpdate,
completeMessage,
t,