mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-02 11:04:36 -04:00
fix(react-ui): stop traces page crash when switching trace tabs (#11387)
Switching from Backend Traces back to API Traces crashed the page with "can't access property status, e.response is undefined" (#11376). The API table briefly renders the previous tab's backend rows while the refetch effect is still pending, and those rows carry no `response` envelope. The status column dereferenced it unguarded. Render a neutral placeholder instead of throwing, and cover the tab-switch scenario with a regression spec. Assisted-by: opencode:big-pickle Signed-off-by: Nandana Dileep <110280757+nandanadileep@users.noreply.github.com>
This commit is contained in:
1 parent
102d91414e
commit
147a5ee783
2 files changed
+45
No files matched your search
@@ -20,3 +20,46 @@ test('marks an API trace with no response status as in progress', async ({ page
|
||||
await expect(row.locator('[title="In progress"]')).toBeVisible()
|
||||
await expect(row.locator('.fa-check-circle')).toHaveCount(0)
|
||||
})
|
||||
|
||||
// Regression for #11376: switching from Backend Traces back to API Traces
|
||||
// used to crash the page. `traces` holds whichever list was fetched last, so
|
||||
// right after `setActiveTab('api')` — before the refetch effect lands — the
|
||||
// API table renders the previous tab's backend rows, which carry no
|
||||
// `response` envelope. The status column must tolerate that instead of
|
||||
// dereferencing `trace.response.status` and tearing down the React tree.
|
||||
test('switching from backend to API traces with a response-less row does not crash', async ({ page }) => {
|
||||
const pageErrors = []
|
||||
page.on('pageerror', (e) => pageErrors.push(e.message))
|
||||
|
||||
await page.route('**/api/traces?*', route => route.fulfill({
|
||||
json: [{
|
||||
id: 'api-1',
|
||||
timestamp: '2026-08-05T02:00:00Z',
|
||||
request: { method: 'POST', path: '/v1/chat/completions' },
|
||||
response: { status: 200 },
|
||||
}],
|
||||
headers: { 'X-Total-Count': '1' },
|
||||
}))
|
||||
await page.route('**/api/backend-traces?*', route => route.fulfill({
|
||||
json: [{
|
||||
id: 'backend-1',
|
||||
type: 'llm',
|
||||
timestamp: '2026-08-05T02:00:00Z',
|
||||
model_name: 'mock-model',
|
||||
summary: 'generated a reply',
|
||||
}],
|
||||
headers: { 'X-Total-Count': '1' },
|
||||
}))
|
||||
|
||||
await page.goto('/app/traces')
|
||||
await expect(page.locator('tbody tr').filter({ hasText: '/v1/chat/completions' })).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: /Backend Traces/ }).click()
|
||||
await expect(page.locator('tbody tr').filter({ hasText: 'generated a reply' })).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: /API Traces/ }).click()
|
||||
// The stale backend row renders in the API table for one frame; the status
|
||||
// column falls back to a neutral placeholder rather than throwing.
|
||||
await expect(page.locator('tbody tr').filter({ hasText: '/v1/chat/completions' })).toBeVisible()
|
||||
expect(pageErrors).toEqual([])
|
||||
})
|
||||
@@ -667,6 +667,8 @@ export default function Traces() {
|
||||
<td>
|
||||
{trace.response?.status === 0
|
||||
? <span className="badge badge-info">Running</span>
|
||||
: trace.response?.status == null
|
||||
? <span className="badge badge--soft">-</span>
|
||||
: <span className={`badge ${trace.response.status < 400 ? 'badge-success' : 'badge-error'}`}>{trace.response.status}</span>}
|
||||
</td>
|
||||
<td><LatencyCell ns={trace.duration} max={slowestTrace} /></td>
|
||||
|
||||
Reference in new issue
Block a user