fix: channel affinity (#2799)
* fix: channel affinity log styles * fix: Issue with incorrect data storage when switching key sources * feat: support not retrying after a single rule configuration fails * fix: render channel affinity tooltip as multiline content * feat: channel affinity cache hit * fix: prevent ChannelAffinityUsageCacheModal infinite loading and hide data before fetch * chore: format backend with gofmt and frontend with prettier/eslint autofix
This commit is contained in:
@@ -55,13 +55,20 @@ export const useModelDeploymentSettings = () => {
|
||||
|
||||
const isIoNetEnabled = settings['model_deployment.ionet.enabled'];
|
||||
|
||||
const buildConnectionError = (rawMessage, fallbackMessage = 'Connection failed') => {
|
||||
const buildConnectionError = (
|
||||
rawMessage,
|
||||
fallbackMessage = 'Connection failed',
|
||||
) => {
|
||||
const message = (rawMessage || fallbackMessage).trim();
|
||||
const normalized = message.toLowerCase();
|
||||
if (normalized.includes('expired') || normalized.includes('expire')) {
|
||||
return { type: 'expired', message };
|
||||
}
|
||||
if (normalized.includes('invalid') || normalized.includes('unauthorized') || normalized.includes('api key')) {
|
||||
if (
|
||||
normalized.includes('invalid') ||
|
||||
normalized.includes('unauthorized') ||
|
||||
normalized.includes('api key')
|
||||
) {
|
||||
return { type: 'invalid', message };
|
||||
}
|
||||
if (normalized.includes('network') || normalized.includes('timeout')) {
|
||||
@@ -85,7 +92,11 @@ export const useModelDeploymentSettings = () => {
|
||||
}
|
||||
|
||||
const message = response?.data?.message || 'Connection failed';
|
||||
setConnectionState({ loading: false, ok: false, error: buildConnectionError(message) });
|
||||
setConnectionState({
|
||||
loading: false,
|
||||
ok: false,
|
||||
error: buildConnectionError(message),
|
||||
});
|
||||
} catch (error) {
|
||||
if (error?.code === 'ERR_NETWORK') {
|
||||
setConnectionState({
|
||||
@@ -95,8 +106,13 @@ export const useModelDeploymentSettings = () => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const rawMessage = error?.response?.data?.message || error?.message || 'Unknown error';
|
||||
setConnectionState({ loading: false, ok: false, error: buildConnectionError(rawMessage, 'Connection failed') });
|
||||
const rawMessage =
|
||||
error?.response?.data?.message || error?.message || 'Unknown error';
|
||||
setConnectionState({
|
||||
loading: false,
|
||||
ok: false,
|
||||
error: buildConnectionError(rawMessage, 'Connection failed'),
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -231,7 +231,10 @@ export const useApiRequest = (
|
||||
if (data.choices?.[0]) {
|
||||
const choice = data.choices[0];
|
||||
let content = choice.message?.content || '';
|
||||
let reasoningContent = choice.message?.reasoning_content || choice.message?.reasoning || '';
|
||||
let reasoningContent =
|
||||
choice.message?.reasoning_content ||
|
||||
choice.message?.reasoning ||
|
||||
'';
|
||||
|
||||
const processed = processThinkTags(content, reasoningContent);
|
||||
|
||||
@@ -318,8 +321,8 @@ export const useApiRequest = (
|
||||
isStreamComplete = true; // 标记流正常完成
|
||||
source.close();
|
||||
sseSourceRef.current = null;
|
||||
setDebugData((prev) => ({
|
||||
...prev,
|
||||
setDebugData((prev) => ({
|
||||
...prev,
|
||||
response: responseData,
|
||||
sseMessages: [...(prev.sseMessages || []), '[DONE]'], // 添加 DONE 标记
|
||||
isStreaming: false,
|
||||
|
||||
@@ -36,18 +36,23 @@ import { processIncompleteThinkTags } from '../../helpers';
|
||||
|
||||
export const usePlaygroundState = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
// 使用惰性初始化,确保只在组件首次挂载时加载配置和消息
|
||||
const [savedConfig] = useState(() => loadConfig());
|
||||
const [initialMessages] = useState(() => {
|
||||
const loaded = loadMessages();
|
||||
// 检查是否是旧的中文默认消息,如果是则清除
|
||||
if (loaded && loaded.length === 2 && loaded[0].id === '2' && loaded[1].id === '3') {
|
||||
const hasOldChinese =
|
||||
loaded[0].content === '你好' ||
|
||||
if (
|
||||
loaded &&
|
||||
loaded.length === 2 &&
|
||||
loaded[0].id === '2' &&
|
||||
loaded[1].id === '3'
|
||||
) {
|
||||
const hasOldChinese =
|
||||
loaded[0].content === '你好' ||
|
||||
loaded[1].content === '你好,请问有什么可以帮助您的吗?' ||
|
||||
loaded[1].content === '你好!很高兴见到你。有什么我可以帮助你的吗?';
|
||||
|
||||
|
||||
if (hasOldChinese) {
|
||||
// 清除旧的默认消息
|
||||
localStorage.removeItem('playground_messages');
|
||||
@@ -81,8 +86,10 @@ export const usePlaygroundState = () => {
|
||||
const [status, setStatus] = useState({});
|
||||
|
||||
// 消息相关状态 - 使用加载的消息或默认消息初始化
|
||||
const [message, setMessage] = useState(() => initialMessages || getDefaultMessages(t));
|
||||
|
||||
const [message, setMessage] = useState(
|
||||
() => initialMessages || getDefaultMessages(t),
|
||||
);
|
||||
|
||||
// 当语言改变时,如果是默认消息则更新
|
||||
useEffect(() => {
|
||||
// 只在没有保存的消息时才更新默认消息
|
||||
|
||||
@@ -112,6 +112,14 @@ export const useLogsData = () => {
|
||||
const [showUserInfo, setShowUserInfoModal] = useState(false);
|
||||
const [userInfoData, setUserInfoData] = useState(null);
|
||||
|
||||
// Channel affinity usage cache stats modal state (admin only)
|
||||
const [
|
||||
showChannelAffinityUsageCacheModal,
|
||||
setShowChannelAffinityUsageCacheModal,
|
||||
] = useState(false);
|
||||
const [channelAffinityUsageCacheTarget, setChannelAffinityUsageCacheTarget] =
|
||||
useState(null);
|
||||
|
||||
// Load saved column preferences from localStorage
|
||||
useEffect(() => {
|
||||
const savedColumns = localStorage.getItem(STORAGE_KEY);
|
||||
@@ -304,6 +312,17 @@ export const useLogsData = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const openChannelAffinityUsageCacheModal = (affinity) => {
|
||||
const a = affinity || {};
|
||||
setChannelAffinityUsageCacheTarget({
|
||||
rule_name: a.rule_name || a.reason || '',
|
||||
using_group: a.using_group || '',
|
||||
key_hint: a.key_hint || '',
|
||||
key_fp: a.key_fp || '',
|
||||
});
|
||||
setShowChannelAffinityUsageCacheModal(true);
|
||||
};
|
||||
|
||||
// Format logs data
|
||||
const setLogsFormat = (logs) => {
|
||||
const requestConversionDisplayValue = (conversionChain) => {
|
||||
@@ -372,9 +391,13 @@ export const useLogsData = () => {
|
||||
other.cache_ratio || 1.0,
|
||||
other.cache_creation_ratio || 1.0,
|
||||
other.cache_creation_tokens_5m || 0,
|
||||
other.cache_creation_ratio_5m || other.cache_creation_ratio || 1.0,
|
||||
other.cache_creation_ratio_5m ||
|
||||
other.cache_creation_ratio ||
|
||||
1.0,
|
||||
other.cache_creation_tokens_1h || 0,
|
||||
other.cache_creation_ratio_1h || other.cache_creation_ratio || 1.0,
|
||||
other.cache_creation_ratio_1h ||
|
||||
other.cache_creation_ratio ||
|
||||
1.0,
|
||||
)
|
||||
: renderLogContent(
|
||||
other?.model_ratio,
|
||||
@@ -524,8 +547,8 @@ export const useLogsData = () => {
|
||||
localCountMode = t('上游返回');
|
||||
}
|
||||
expandDataLocal.push({
|
||||
key: t('计费模式'),
|
||||
value: localCountMode,
|
||||
key: t('计费模式'),
|
||||
value: localCountMode,
|
||||
});
|
||||
}
|
||||
expandDatesLocal[logs[i].key] = expandDataLocal;
|
||||
@@ -680,6 +703,12 @@ export const useLogsData = () => {
|
||||
userInfoData,
|
||||
showUserInfoFunc,
|
||||
|
||||
// Channel affinity usage cache stats modal
|
||||
showChannelAffinityUsageCacheModal,
|
||||
setShowChannelAffinityUsageCacheModal,
|
||||
channelAffinityUsageCacheTarget,
|
||||
openChannelAffinityUsageCacheModal,
|
||||
|
||||
// Functions
|
||||
loadLogs,
|
||||
handlePageChange,
|
||||
|
||||
Reference in New Issue
Block a user