mirror of
https://github.com/supabase/supabase.git
synced 2026-10-19 08:09:11 +03:00
## Summary
Migrates four more assistant tools to the contract + `.server.ts`
pattern used by the notebook tools. Each tool now declares its schemas,
opt-in level, approval and model-visible output once.
## Changes
* Add contracts and server factories for `execute_sql`,
`deploy_edge_function`, `rename_chat`, `load_knowledge`;
`getStudioTools` now composes them.
* Add `execute-sql-output.ts` (output schema, `toModelRows`,
`NO_DATA_PERMISSIONS`) and `knowledge-topics.ts` (topic names + UI
labels, replacing the copy in `Message.Parts.utils.ts`).
* Delete `executeSqlSanitizer` and `ALL_TOOL_SANITIZERS`; history replay
goes through `sanitizeContractPart` only.
* Drop the four tools from `TOOL_CATEGORY_MAP`; the UI and evals import
schemas from the contracts; `rename_chat` input casts now use the
contract type.
## Deviations worth reviewing
* `execute_sql` replay: a bare-array legacy output is stamped `schema`
via a union transform (still hidden from the model, still shown in the
UI). A malformed non-array output is now dropped, where before it passed
through at the data level.
* The "no data permission" message now reaches the model as a JSON
string value instead of a text part. The text the model sees is
unchanged.
* `deploy_edge_function` output is now strictly `{ success: boolean }`
in the UI parse (was optional/passthrough).
39 lines
1.6 KiB
TypeScript
39 lines
1.6 KiB
TypeScript
import { untrustedSql } from '@supabase/pg-meta'
|
|
|
|
import { getAuthHeaders } from './auth-headers'
|
|
import { capReadQuery } from './cap-read-query'
|
|
import { contract } from './execute-sql'
|
|
import { approvalGatedToolFromContract } from './tool-from-contract'
|
|
import type { ApprovalGatedToolFactory } from './types'
|
|
import { DEFAULT_CELL_ROW_LIMIT } from '@/components/interfaces/Explorer/QueryCell/QueryCell.utils'
|
|
import { executeSql } from '@/data/sql/execute-sql-mutation'
|
|
|
|
export const createTool: ApprovalGatedToolFactory<typeof contract> = (ctx) => {
|
|
const { projectRef, connectionString, aiOptInLevel = 'schema' } = ctx
|
|
const authHeaders = getAuthHeaders(ctx.authorization, { contentType: true })
|
|
|
|
return approvalGatedToolFromContract(
|
|
contract,
|
|
ctx,
|
|
async ({ sql, isWriteQuery }, { abortSignal }, gate) => {
|
|
const approvedSql = gate.acceptUntrustedSql(untrustedSql(sql))
|
|
const { sql: limitedSql, isCappedInDatabase } = isWriteQuery
|
|
? { sql: approvedSql, isCappedInDatabase: false }
|
|
: capReadQuery(approvedSql, DEFAULT_CELL_ROW_LIMIT + 1)
|
|
const { result } = await executeSql(
|
|
{ projectRef, connectionString, sql: limitedSql },
|
|
abortSignal,
|
|
authHeaders
|
|
)
|
|
const rows = Array.isArray(result) ? result : []
|
|
const isCapped = rows.length > DEFAULT_CELL_ROW_LIMIT
|
|
const hasMoreRows = isCappedInDatabase && isCapped
|
|
return {
|
|
rows: rows.slice(0, DEFAULT_CELL_ROW_LIMIT),
|
|
...(hasMoreRows ? { hasMoreRows } : { totalRows: rows.length }),
|
|
optInLevel: aiOptInLevel,
|
|
}
|
|
}
|
|
)
|
|
}
|