Files
supabase/apps/studio/lib/ai/tools/report-tools.ts
Joshen Lim 2c53ca4a79 Support listing and reading custom reports from Assistant (#48530)
## Context

This is pre-requisite work for adding support to managing custom reports
from the Assistant. Planning to break this into a number of PRs, briefly
- Adding read support for custom reports
- Adding write support for custom reports
- Adding run support for custom reports
  - Should be able to infer data from the results then

This PR starts with adding support for listing and reading custom
reports from the Assistant

## Other changes involved
- Updates setting up of the home page report to have better title and
description
- Swaps the variant of the ToggleGroup in the SQL block for custom
reports as the default variant blends into the background color of the
PopoverContent

## To test
- [ ] Assistant should be able to list custom reports + read its
contents
<img width="428" height="755" alt="image"
src="https://github.com/user-attachments/assets/6a15b660-c0ee-4a06-984c-87eff3943eec"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added AI-assisted tools to list reports and retrieve report details,
including chart counts, layouts, configurations, and SQL-backed chart
information.
  - Added clearer empty-state messaging when no snippets are available.

- **Improvements**
- New homepage reports now use the name “Homepage Report” and include a
descriptive project-home summary.
  - Updated query controls with refreshed visual styling.
  - Improved content requests to support additional request context.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-03 13:59:50 +07:00

93 lines
2.9 KiB
TypeScript

import { tool } from 'ai'
import { z } from 'zod'
import { getContentById } from '@/data/content/content-id-query'
import { getContent } from '@/data/content/content-query'
import type { Dashboards, SqlSnippets } from '@/types'
export type ReportToolsContext = {
projectRef?: string
authorization?: string
}
export const getReportTools = (ctx: ReportToolsContext = {}) => {
const { projectRef, authorization } = ctx
const authHeaders = authorization ? { Authorization: authorization } : undefined
return {
list_reports: tool({
description: 'List the custom reports saved for this project',
inputSchema: z.object({
limit: z
.number()
.int()
.positive()
.max(100)
.default(20)
.describe('Max number of reports to return.'),
}),
execute: async ({ limit }) => {
const { content } = await getContent(
{ projectRef, type: 'report', limit },
undefined,
authHeaders
)
return content.map((report) => ({
id: report.id,
name: report.name,
description: report.description,
visibility: report.visibility,
updated_at: report.updated_at,
chart_count: (report.content as Dashboards.Content).layout?.length ?? 0,
}))
},
}),
get_report: tool({
description:
'Get a single custom report by id, including the resolved SQL for any SQL-based chart blocks it contains.',
inputSchema: z.object({
id: z.string().describe('The id of the report to fetch.'),
}),
execute: async ({ id }) => {
const report = await getContentById({ projectRef, id }, undefined, authHeaders)
if (report.type !== 'report') {
throw new Error(`Content ${id} is not a report (type: ${report.type})`)
}
const content = report.content as Dashboards.Content
const layout = await Promise.all(
content.layout.map(async (chart) => {
if (!chart.attribute.startsWith('snippet_')) return chart
// A SQL-block chart's `id` is the id of its linked `type: 'sql'` content row.
const snippet = await getContentById(
{ projectRef, id: chart.id },
undefined,
authHeaders
).catch(() => null)
const sql =
snippet?.type === 'sql'
? (snippet.content as SqlSnippets.Content).unchecked_sql
: undefined
return { ...chart, sql }
})
)
return {
id: report.id,
name: report.name,
description: report.description,
visibility: report.visibility,
period_start: content.period_start,
period_end: content.period_end,
interval: content.interval,
layout,
}
},
}),
}
}