Files
LocalAI/core/http/react-ui/e2e/traces-metadata.spec.js
localai-org-maint-bot 4be6e22b5f feat(webui): surface user, client IP and user agent in API traces (#10886, #10887) (#10907)
The Operate → Traces "API Traces" panel already recorded who made each
request (user_id/user_name) but never showed it, and did not capture the
caller's network identity at all. Operators asked to see the requesting
user (#10886) and the client IP + user agent (#10887) so a trace can be
attributed to who/what issued it.

Backend: add ClientIP and UserAgent to APIExchange and populate them from
echo's c.RealIP() (honours X-Forwarded-For / X-Real-IP behind a trusted
proxy) and the request's User-Agent header. Both are omitempty and the
/api/traces swagger response is map[string]any, so this is additive.

UI: add a sortable "User" column to the API traces table and a metadata
block (User / Client IP / User Agent) at the top of the expanded row
detail. Fields render only when present, so older buffered traces and
unauthenticated/local requests degrade cleanly.

Adds an e2e spec covering the new column value and the expanded metadata.


Assisted-by: Claude:opus-4.8 [Claude Code]

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-07-17 23:47:31 +02:00

45 lines
1.8 KiB
JavaScript

import { test, expect } from './coverage-fixtures.js'
// Pin the API-trace metadata surface (issues #10886 / #10887): the API
// traces table exposes the requesting user in a dedicated column, and the
// expanded row detail lists User, Client IP and User Agent so an operator
// can tell who/what issued each request.
test.describe('Traces - API request metadata', () => {
test.beforeEach(async ({ page }) => {
await page.route('**/api/traces', (route) => {
route.fulfill({
contentType: 'application/json',
body: JSON.stringify([
{
request: { method: 'POST', path: '/v1/chat/completions' },
response: { status: 200 },
user_id: 'user-123',
user_name: 'alice',
client_ip: '203.0.113.7',
user_agent: 'curl/8.4.0',
},
]),
})
})
await page.route('**/api/backend-traces', (route) => {
route.fulfill({ contentType: 'application/json', body: '[]' })
})
await page.goto('/app/traces')
await expect(page.locator('text=Tracing is')).toBeVisible({ timeout: 10_000 })
})
test('shows the User column value in the API traces table', async ({ page }) => {
await expect(page.locator('th', { hasText: 'User' })).toBeVisible()
await expect(page.locator('td', { hasText: 'alice' }).first()).toBeVisible()
})
test('expands the row to reveal Client IP and User Agent', async ({ page }) => {
await page.locator('tr', { hasText: '/v1/chat/completions' }).first().click()
await expect(page.locator('text=Client IP').first()).toBeVisible()
await expect(page.locator('text=203.0.113.7').first()).toBeVisible()
await expect(page.locator('text=User Agent').first()).toBeVisible()
await expect(page.locator('text=curl/8.4.0').first()).toBeVisible()
})
})