Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 121 additions & 82 deletions src/utils/chatRequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,70 +56,89 @@ export const createChatRequestHandler = (supabase: SupabaseApi): vscode.ChatRequ
return { metadata: { command: 'show' } };
} else if (request.command === 'migration') {
try {
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
if (model) {
try {
// Create new migration file (execute supabase migration new copilot).
const migrationName = `copilot`; // TODO: generate from prompt.
const cmd = `${Commands.NEW_MIGRATION} ${migrationName}`;
executeCommand(cmd);

// Get schema context.
const schema = await supabase.getSchema();
// TODO: figure out how to modify the prompt to only generate valid SQL. Currently Copilot generates a markdown response.
const messages = [
vscode.LanguageModelChatMessage.User(
`You're a friendly PostgreSQL assistant called Supabase Clippy, helping with writing database migrations.`
),
vscode.LanguageModelChatMessage.User(
`Please provide help with ${prompt}. The reference database schema for question is ${schema}. IMPORTANT: Be sure you only use the tables and columns from this schema in your answer!`
)
];
const chatResponse = await model.sendRequest(messages, {}, token);
let responseText = '';

for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
responseText += fragment;
}
// Check if language models are available
let models;
try {
models = await vscode.lm.selectChatModels(MODEL_SELECTOR);
} catch (err) {
stream.markdown('⚠️ AI-powered migration generation requires an active GitHub Copilot subscription.\n\n' +
'You can still create migrations manually using:\n' +
'- The "Create new migration" button in the Database view\n' +
'- Running `supabase migration new` in the terminal');
return { metadata: { command: 'migration' } };
}

// Open migration file in editor.
let filePath = await getFilePath();
while (!(await isFileEmpty(filePath))) {
await new Promise((resolve) => setTimeout(resolve, 500));
filePath = await getFilePath();
}
if (!models || models.length === 0) {
stream.markdown('⚠️ GitHub Copilot language models are not available.\n\n' +
'Please ensure you have:\n' +
'1. An active GitHub Copilot subscription\n' +
'2. Signed in to GitHub in VS Code\n\n' +
'You can still create migrations manually.');
return { metadata: { command: 'migration' } };
}

const openPath = vscode.Uri.file(filePath);
const doc = await vscode.workspace.openTextDocument(openPath);
await vscode.window.showTextDocument(doc);
const textEditor = vscode.window.activeTextEditor;

// Extract SQL from markdown and write to migration file.
const sql = extractCode(responseText);

if (textEditor) {
for await (const statement of sql) {
await textEditor.edit((edit) => {
const lastLine = textEditor.document.lineAt(textEditor.document.lineCount - 1);
const position = new vscode.Position(lastLine.lineNumber, lastLine.text.length);
edit.insert(position, statement);
});
}
await textEditor.document.save();
}
const [model] = models;
try {
// Create new migration file (execute supabase migration new copilot).
const migrationName = `copilot`; // TODO: generate from prompt.
const cmd = `${Commands.NEW_MIGRATION} ${migrationName}`;
executeCommand(cmd);

// Get schema context.
const schema = await supabase.getSchema();
// TODO: figure out how to modify the prompt to only generate valid SQL. Currently Copilot generates a markdown response.
const messages = [
vscode.LanguageModelChatMessage.User(
`You're a friendly PostgreSQL assistant called Supabase Clippy, helping with writing database migrations.`
),
vscode.LanguageModelChatMessage.User(
`Please provide help with ${prompt}. The reference database schema for question is ${schema}. IMPORTANT: Be sure you only use the tables and columns from this schema in your answer!`
)
];
const chatResponse = await model.sendRequest(messages, {}, token);
let responseText = '';

for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
responseText += fragment;
}

// Render button to apply migration.
stream.markdown('\n\nMake sure to review the migration file before applying it!');
stream.button({
command: 'databaseProvider.db_push',
title: vscode.l10n.t('Apply migration.')
});
} catch (err) {
stream.markdown(
"🤔 I can't find the schema for the database. Please check that `supabase start` is running."
);
// Open migration file in editor.
let filePath = await getFilePath();
while (!(await isFileEmpty(filePath))) {
await new Promise((resolve) => setTimeout(resolve, 500));
filePath = await getFilePath();
}

const openPath = vscode.Uri.file(filePath);
const doc = await vscode.workspace.openTextDocument(openPath);
await vscode.window.showTextDocument(doc);
const textEditor = vscode.window.activeTextEditor;

// Extract SQL from markdown and write to migration file.
const sql = extractCode(responseText);

if (textEditor) {
for await (const statement of sql) {
await textEditor.edit((edit) => {
const lastLine = textEditor.document.lineAt(textEditor.document.lineCount - 1);
const position = new vscode.Position(lastLine.lineNumber, lastLine.text.length);
edit.insert(position, statement);
});
}
await textEditor.document.save();
}

// Render button to apply migration.
stream.markdown('\n\nMake sure to review the migration file before applying it!');
stream.button({
command: 'databaseProvider.db_push',
title: vscode.l10n.t('Apply migration.')
});
} catch (err) {
stream.markdown(
"🤔 I can't find the schema for the database. Please check that `supabase start` is running."
);
}
} catch (err) {
handleError(err, stream);
Expand All @@ -128,29 +147,49 @@ export const createChatRequestHandler = (supabase: SupabaseApi): vscode.ChatRequ
return { metadata: { command: 'migration' } };
} else {
try {
const [model] = await vscode.lm.selectChatModels(MODEL_SELECTOR);
if (model) {
try {
const schema = await supabase.getSchema();

const messages = [
vscode.LanguageModelChatMessage.User(
`You're a friendly PostgreSQL assistant called Supabase Clippy, helping with writing SQL.`
),
vscode.LanguageModelChatMessage.User(
`Please provide help with ${prompt}. The reference database schema for this question is ${schema}. IMPORTANT: Be sure you only use the tables and columns from this schema in your answer.`
)
];

const chatResponse = await model.sendRequest(messages, {}, token);
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
} catch (err) {
stream.markdown(
"🤔 I can't find the schema for the database. Please check that `supabase start` is running."
);
// Check if language models are available
let models;
try {
models = await vscode.lm.selectChatModels(MODEL_SELECTOR);
} catch (err) {
stream.markdown('⚠️ AI features require an active GitHub Copilot subscription.\n\n' +
'However, you can still use all database management features:\n' +
'- View and explore tables\n' +
'- Create migrations manually\n' +
'- Run database commands\n' +
'- Generate TypeScript types');
return { metadata: { command: '' } };
}

if (!models || models.length === 0) {
stream.markdown('⚠️ GitHub Copilot language models are not available.\n\n' +
'Please ensure you have:\n' +
'1. An active GitHub Copilot subscription\n' +
'2. Signed in to GitHub in VS Code');
return { metadata: { command: '' } };
}

const [model] = models;
try {
const schema = await supabase.getSchema();

const messages = [
vscode.LanguageModelChatMessage.User(
`You're a friendly PostgreSQL assistant called Supabase Clippy, helping with writing SQL.`
),
vscode.LanguageModelChatMessage.User(
`Please provide help with ${prompt}. The reference database schema for this question is ${schema}. IMPORTANT: Be sure you only use the tables and columns from this schema in your answer.`
)
];

const chatResponse = await model.sendRequest(messages, {}, token);
for await (const fragment of chatResponse.text) {
stream.markdown(fragment);
}
} catch (err) {
stream.markdown(
"🤔 I can't find the schema for the database. Please check that `supabase start` is running."
);
}
} catch (err) {
handleError(err, stream);
Expand Down