mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 01:40:13 -04:00
8f69a10cc9
A variety of fixes and improvements to the Cmd+K AI completions endpoint in the [SQL Editor](https://supabase.com/dashboard/project/_/sql/new): - Pre-load table definitions for the public schema and any other schemas referenced in the editor, so the model has real column names without needing to fetch them dynamically - Replace the generic tool suite with a single streamlined `getSchemaDefinitions` tool the model can still call to look up additional schemas on demand without behavior differences across platform & self-hosted - Swap generic chat system prompt for a purpose-built `COMPLETION_PROMPT`; fix role (`assistant` → `user`) for consistency with other endpoints - Validate and type the request body with `zod`, which was previously untyped (`any`) - Improve Cmd+K behavior when nothing is selected — use the full editor content as context, return the complete query rather than just the changed fragment, and switch to a generation mode when the editor is blank - Escape single quotes in schema names when fetching entity definitions in `pg-meta` to prevent schema names from breaking out of the SQL string and injecting arbitrary content into the prompt ## Before Before, the SQL Editor would often hallucinate tables / columns that don't exist in the user's database making it less helpful if you don't know the exact table/column names. Even with maximum Assistant opt-in level on the org, it would often fail to call the necessary tools to gather database context. <img width="5062" height="1522" alt="image" src="https://github.com/user-attachments/assets/fbe1130f-6b5a-41a8-99d7-7268880af188" /> <img width="2540" height="658" alt="image" src="https://github.com/user-attachments/assets/a31c2967-7751-4fce-a9b7-60bd77660b1a" /> Sometimes it also silently fails and generates empty queries: <img width="1352" height="398" alt="CleanShot 2026-04-09 at 17 46 06@2x" src="https://github.com/user-attachments/assets/e17c103a-d47d-47e6-8c2e-101f0fae5651" /> Or echos back the user's prompt: <img width="1368" height="282" alt="CleanShot 2026-04-09 at 23 04 56@2x" src="https://github.com/user-attachments/assets/7dff6e64-f54e-45b5-8e86-5399e5a2fe41" /> ## After In this example, the completion correctly interpreted my request for "completed" todos as a query on the `completed_foo` column in my `public` schema, instead of assuming existence of a `completed` column. <img width="1452" height="838" alt="CleanShot 2026-04-09 at 17 43 13@2x" src="https://github.com/user-attachments/assets/7a575589-78b4-448d-810a-0330ff08ef8b" /> In this example, the completion was correctly aware of an `other` schema because it was detected in my existing query. I didn't have to select the text, it included the full query in context when unselected. Notice how it correctly used the `is_done` column when I asked for "completed" cakes: <img width="1372" height="534" alt="CleanShot 2026-04-09 at 17 39 07@2x" src="https://github.com/user-attachments/assets/e6b7eb6f-f3e8-4fa1-90a3-b5e34ddc14e4" /> Supersedes #44151 Closes AI-544
48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
import type { editor as monacoEditor } from 'monaco-editor'
|
|
|
|
export type EditorSelection = {
|
|
selection: string
|
|
beforeSelection: string
|
|
afterSelection: string
|
|
startLineNumber: number
|
|
endLineNumber: number
|
|
}
|
|
|
|
export function getEditorSelectionParts(
|
|
editor: monacoEditor.IStandaloneCodeEditor
|
|
): EditorSelection | null {
|
|
const selection = editor.getSelection()
|
|
const model = editor.getModel()
|
|
if (!model || !selection) return null
|
|
|
|
const allLines = model.getLinesContent()
|
|
|
|
const noSelection =
|
|
selection.startLineNumber === selection.endLineNumber &&
|
|
selection.startColumn === selection.endColumn
|
|
|
|
if (noSelection) {
|
|
return {
|
|
selection: allLines.join('\n'),
|
|
beforeSelection: '',
|
|
afterSelection: '',
|
|
startLineNumber: selection.startLineNumber,
|
|
endLineNumber: selection.endLineNumber,
|
|
}
|
|
}
|
|
|
|
const startLineIndex = selection.startLineNumber - 1
|
|
const endLineIndex = selection.endLineNumber
|
|
|
|
const beforeLines = allLines.slice(0, startLineIndex)
|
|
const afterLines = allLines.slice(endLineIndex)
|
|
|
|
return {
|
|
selection: allLines.slice(startLineIndex, endLineIndex).join('\n'),
|
|
beforeSelection: beforeLines.length > 0 ? beforeLines.join('\n') + '\n' : '',
|
|
afterSelection: afterLines.length > 0 ? '\n' + afterLines.join('\n') : '',
|
|
startLineNumber: selection.startLineNumber,
|
|
endLineNumber: selection.endLineNumber,
|
|
}
|
|
}
|