mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-15 23:10:59 +00:00
fix(monitoring): tolerate null API payloads
Normalize monitoring API responses before rendering so empty or error payloads with data:null cannot crash the dashboard. Also guard chart, token, and box session arrays before reading length/map.
This commit is contained in:
@@ -164,7 +164,7 @@ export default function TokenMonitoring({
|
|||||||
}, [fetchStats]);
|
}, [fetchStats]);
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
if (!stats) return [];
|
if (!stats || !Array.isArray(stats.timeseries)) return [];
|
||||||
return stats.timeseries.map((p) => ({
|
return stats.timeseries.map((p) => ({
|
||||||
bucket: p.bucket,
|
bucket: p.bucket,
|
||||||
input: p.input_tokens,
|
input: p.input_tokens,
|
||||||
@@ -198,7 +198,7 @@ export default function TokenMonitoring({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stats || stats.summary.total_calls === 0) {
|
if (!stats || !stats.summary || stats.summary.total_calls === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-card rounded-xl border p-6">
|
<div className="bg-card rounded-xl border p-6">
|
||||||
<div className="h-[260px] flex flex-col items-center justify-center text-muted-foreground gap-2">
|
<div className="h-[260px] flex flex-col items-center justify-center text-muted-foreground gap-2">
|
||||||
@@ -209,7 +209,8 @@ export default function TokenMonitoring({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { summary, by_model } = stats;
|
const summary = stats.summary;
|
||||||
|
const by_model = Array.isArray(stats.by_model) ? stats.by_model : [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function SystemStatusCard({
|
|||||||
: await httpClient.getBoxSessions().catch(() => [] as BoxSessionInfo[]);
|
: await httpClient.getBoxSessions().catch(() => [] as BoxSessionInfo[]);
|
||||||
setPluginStatus(plugin);
|
setPluginStatus(plugin);
|
||||||
setBoxStatus(box);
|
setBoxStatus(box);
|
||||||
setBoxSessions(sessions);
|
setBoxSessions(Array.isArray(sessions) ? sessions : []);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,14 +34,16 @@ export default function TrafficChart({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
if (!messages.length && !llmCalls.length) {
|
const safeMessages = Array.isArray(messages) ? messages : [];
|
||||||
|
const safeLlmCalls = Array.isArray(llmCalls) ? llmCalls : [];
|
||||||
|
if (!safeMessages.length && !safeLlmCalls.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine all timestamps and find the range
|
// Combine all timestamps and find the range
|
||||||
const allTimestamps = [
|
const allTimestamps = [
|
||||||
...messages.map((m) => m.timestamp.getTime()),
|
...safeMessages.map((m) => m.timestamp.getTime()),
|
||||||
...llmCalls.map((c) => c.timestamp.getTime()),
|
...safeLlmCalls.map((c) => c.timestamp.getTime()),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (allTimestamps.length === 0) return [];
|
if (allTimestamps.length === 0) return [];
|
||||||
@@ -99,7 +101,7 @@ export default function TrafficChart({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Count messages per bucket
|
// Count messages per bucket
|
||||||
messages.forEach((msg) => {
|
safeMessages.forEach((msg) => {
|
||||||
const bucket =
|
const bucket =
|
||||||
Math.floor(msg.timestamp.getTime() / bucketSize) * bucketSize;
|
Math.floor(msg.timestamp.getTime() / bucketSize) * bucketSize;
|
||||||
const point = buckets.get(bucket);
|
const point = buckets.get(bucket);
|
||||||
@@ -109,7 +111,7 @@ export default function TrafficChart({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Count LLM calls per bucket
|
// Count LLM calls per bucket
|
||||||
llmCalls.forEach((call) => {
|
safeLlmCalls.forEach((call) => {
|
||||||
const bucket =
|
const bucket =
|
||||||
Math.floor(call.timestamp.getTime() / bucketSize) * bucketSize;
|
Math.floor(call.timestamp.getTime() / bucketSize) * bucketSize;
|
||||||
const point = buckets.get(bucket);
|
const point = buckets.get(bucket);
|
||||||
|
|||||||
@@ -92,18 +92,46 @@ export function useMonitoringData(filterState: FilterState) {
|
|||||||
limit: 50,
|
limit: 50,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const overview = response?.overview ?? {
|
||||||
|
total_messages: 0,
|
||||||
|
llm_calls: 0,
|
||||||
|
embedding_calls: 0,
|
||||||
|
model_calls: 0,
|
||||||
|
success_rate: 100,
|
||||||
|
active_sessions: 0,
|
||||||
|
};
|
||||||
|
const messages = Array.isArray(response?.messages)
|
||||||
|
? response.messages
|
||||||
|
: [];
|
||||||
|
const llmCalls = Array.isArray(response?.llmCalls)
|
||||||
|
? response.llmCalls
|
||||||
|
: [];
|
||||||
|
const embeddingCalls = Array.isArray(response?.embeddingCalls)
|
||||||
|
? response.embeddingCalls
|
||||||
|
: [];
|
||||||
|
const sessions = Array.isArray(response?.sessions)
|
||||||
|
? response.sessions
|
||||||
|
: [];
|
||||||
|
const errors = Array.isArray(response?.errors) ? response.errors : [];
|
||||||
|
const totalCount = response?.totalCount ?? {
|
||||||
|
messages: messages.length,
|
||||||
|
llmCalls: llmCalls.length,
|
||||||
|
embeddingCalls: embeddingCalls.length,
|
||||||
|
sessions: sessions.length,
|
||||||
|
errors: errors.length,
|
||||||
|
};
|
||||||
|
|
||||||
// Transform the response to match MonitoringData interface
|
// Transform the response to match MonitoringData interface
|
||||||
const transformedData: MonitoringData = {
|
const transformedData: MonitoringData = {
|
||||||
overview: {
|
overview: {
|
||||||
totalMessages: response.overview.total_messages,
|
totalMessages: overview.total_messages,
|
||||||
llmCalls: response.overview.llm_calls,
|
llmCalls: overview.llm_calls,
|
||||||
embeddingCalls: response.overview.embedding_calls || 0,
|
embeddingCalls: overview.embedding_calls || 0,
|
||||||
modelCalls:
|
modelCalls: overview.model_calls || overview.llm_calls,
|
||||||
response.overview.model_calls || response.overview.llm_calls,
|
successRate: overview.success_rate,
|
||||||
successRate: response.overview.success_rate,
|
activeSessions: overview.active_sessions,
|
||||||
activeSessions: response.overview.active_sessions,
|
|
||||||
},
|
},
|
||||||
messages: response.messages.map(
|
messages: messages.map(
|
||||||
(msg: {
|
(msg: {
|
||||||
id: string;
|
id: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -136,7 +164,7 @@ export function useMonitoringData(filterState: FilterState) {
|
|||||||
variables: msg.variables,
|
variables: msg.variables,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
llmCalls: response.llmCalls.map(
|
llmCalls: llmCalls.map(
|
||||||
(call: {
|
(call: {
|
||||||
id: string;
|
id: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -173,7 +201,7 @@ export function useMonitoringData(filterState: FilterState) {
|
|||||||
messageId: call.message_id,
|
messageId: call.message_id,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
embeddingCalls: (response.embeddingCalls || []).map(
|
embeddingCalls: embeddingCalls.map(
|
||||||
(call: {
|
(call: {
|
||||||
id: string;
|
id: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -208,7 +236,7 @@ export function useMonitoringData(filterState: FilterState) {
|
|||||||
),
|
),
|
||||||
// Create merged modelCalls array from llmCalls and embeddingCalls
|
// Create merged modelCalls array from llmCalls and embeddingCalls
|
||||||
modelCalls: [] as ModelCall[], // Will be populated after transform
|
modelCalls: [] as ModelCall[], // Will be populated after transform
|
||||||
sessions: response.sessions.map(
|
sessions: sessions.map(
|
||||||
(session: {
|
(session: {
|
||||||
session_id: string;
|
session_id: string;
|
||||||
bot_id: string;
|
bot_id: string;
|
||||||
@@ -236,7 +264,7 @@ export function useMonitoringData(filterState: FilterState) {
|
|||||||
userId: session.user_id,
|
userId: session.user_id,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
errors: response.errors.map(
|
errors: errors.map(
|
||||||
(error: {
|
(error: {
|
||||||
id: string;
|
id: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -264,11 +292,11 @@ export function useMonitoringData(filterState: FilterState) {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
totalCount: {
|
totalCount: {
|
||||||
messages: response.totalCount.messages,
|
messages: totalCount.messages,
|
||||||
llmCalls: response.totalCount.llmCalls,
|
llmCalls: totalCount.llmCalls,
|
||||||
embeddingCalls: response.totalCount.embeddingCalls || 0,
|
embeddingCalls: totalCount.embeddingCalls || 0,
|
||||||
sessions: response.totalCount.sessions,
|
sessions: totalCount.sessions,
|
||||||
errors: response.totalCount.errors,
|
errors: totalCount.errors,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user