mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 18:11:51 +08:00
<img width="1510" height="862" alt="image" src="https://github.com/user-attachments/assets/f7157bad-9b23-4d73-a9aa-2a7a7c179318" /> ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature and bug fix. ## What is the current behavior? `query_logs` can return rows to the assistant, but the chat UI does not hydrate those rows into the query result by default. The query only becomes visible after clicking **Run query**, even though the same SQL and time range work when rerun manually. ## What is the new behavior? - Renders `query_logs` tool output through a dedicated logs message part using the shared assistant query cell. - Parses the exact MCP untrusted-data envelope into the initial query result, without changing what the assistant model receives. - Preserves the logs source and time range for manual reruns. - Infers a useful table or chart presentation from the returned rows while retaining explicit display settings. - Adds focused tests for MCP result parsing, timestamps, errors, query source handling, and visualization inference. ## How to test 1. Check out this PR and run Studio against a project that has recent logs. Generate some project activity first, such as an API request, if needed. 2. Open the AI Assistant and ask: `Show log counts by minute for the last 15 minutes and summarize any spikes.` 3. Wait for `query_logs` to finish. Verify the query cell appears with results already populated; do not click **Run query** first. 4. Verify the aggregate result opens as a chart, then switch to the table view and confirm the underlying rows are present. 5. Click **Run query** and verify the query runs successfully again using the same logs source and 15-minute time range. 6. Ask: `Show the 20 most recent log entries from the last 15 minutes.` Verify this non-aggregate result opens as a table with rows already populated. 7. Confirm the assistant's written summary agrees with the displayed rows and does not report zero rows when results are visible. ## Additional context This is the top PR in stack #49294 and depends on the back-end knowledge change in #49292. Verified with 59 focused tests across assistant context, Studio/MCP tools, query display, and logs result parsing. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added AI Assistant support for querying and displaying application logs. * Added automatic visualization selection, including charts for time-based and categorical data. * Added source-aware query handling with dedicated titles, time ranges, and result displays. * Added clearer loading, parsing, and error states for log queries. * **Bug Fixes** * Improved handling of streamed results, source changes, and query display updates. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
getAssistantQueryDisplay,
|
|
inferAssistantChartDisplay,
|
|
isChartableAssistantSql,
|
|
} from '@/components/ui/AIAssistantPanel/AssistantQueryCell.utils'
|
|
|
|
describe('getAssistantQueryDisplay', () => {
|
|
it('infers a chart from chartable rows when the assistant did not pick axes', () => {
|
|
expect(
|
|
getAssistantQueryDisplay({
|
|
rows: [
|
|
{ hour: '2024-01-01T00:00:00Z', count: 3 },
|
|
{ hour: '2024-01-01T01:00:00Z', count: 8 },
|
|
],
|
|
})
|
|
).toMatchObject({
|
|
view: 'chart',
|
|
chart: { type: 'line', x_column: 'hour', y_series: ['count'] },
|
|
})
|
|
})
|
|
|
|
it('defaults aggregating SQL to a chart before rows arrive', () => {
|
|
expect(
|
|
getAssistantQueryDisplay({
|
|
sql: 'select toStartOfHour(timestamp) as hour, count() as count from logs group by hour',
|
|
})
|
|
).toEqual({ view: 'chart', chart: undefined })
|
|
})
|
|
|
|
it('falls back to a table when aggregate rows cannot produce chart axes', () => {
|
|
expect(
|
|
getAssistantQueryDisplay({
|
|
sql: 'select count() as count from logs',
|
|
rows: [{ count: 42 }],
|
|
})
|
|
).toEqual({ view: 'table', chart: undefined })
|
|
})
|
|
})
|
|
|
|
describe('inferAssistantChartDisplay', () => {
|
|
it('returns a table when there are no rows or only one column', () => {
|
|
expect(inferAssistantChartDisplay([])).toEqual({ view: 'table', chart: undefined })
|
|
expect(inferAssistantChartDisplay([{ count: 1 }])).toEqual({ view: 'table', chart: undefined })
|
|
})
|
|
|
|
it('uses a line chart for a time column plus a metric', () => {
|
|
expect(
|
|
inferAssistantChartDisplay([
|
|
{ timestamp: '2024-06-20T14:00:00Z', count: 4 },
|
|
{ timestamp: '2024-06-20T15:00:00Z', count: 9 },
|
|
])
|
|
).toMatchObject({
|
|
view: 'chart',
|
|
chart: { type: 'line', x_column: 'timestamp', y_series: ['count'] },
|
|
})
|
|
})
|
|
|
|
it('uses a bar chart for a categorical column plus a metric', () => {
|
|
expect(
|
|
inferAssistantChartDisplay([
|
|
{ method: 'GET', count: 12 },
|
|
{ method: 'POST', count: 3 },
|
|
])
|
|
).toMatchObject({
|
|
view: 'chart',
|
|
chart: { type: 'bar', x_column: 'method', y_series: ['count'] },
|
|
})
|
|
})
|
|
|
|
it('keeps raw log dumps as a table', () => {
|
|
expect(
|
|
inferAssistantChartDisplay([
|
|
{ timestamp: '2024-06-20T14:00:00Z', event_message: 'connection reset' },
|
|
{ timestamp: '2024-06-20T14:01:00Z', event_message: 'timeout' },
|
|
])
|
|
).toEqual({ view: 'table', chart: undefined })
|
|
})
|
|
})
|
|
|
|
describe('isChartableAssistantSql', () => {
|
|
it('detects aggregations and ignores commented-out matches', () => {
|
|
expect(isChartableAssistantSql('select count() from logs')).toBe(true)
|
|
expect(isChartableAssistantSql('select status, count() from logs group by status')).toBe(true)
|
|
expect(
|
|
isChartableAssistantSql('-- count of errors\nselect timestamp, event_message from logs')
|
|
).toBe(false)
|
|
})
|
|
})
|