Add description (optional) to SQL query

This commit is contained in:
Will Chen
2025-04-23 12:37:30 -07:00
parent 1d0176d1e9
commit 09deb98ba1
6 changed files with 50 additions and 19 deletions

View File

@@ -38,6 +38,7 @@ import {
SuggestedAction, SuggestedAction,
ProposalResult, ProposalResult,
FileChange, FileChange,
SqlQuery,
} from "@/lib/schemas"; } from "@/lib/schemas";
import type { Message } from "@/ipc/ipc_types"; import type { Message } from "@/ipc/ipc_types";
import { isPreviewOpenAtom } from "@/atoms/viewAtoms"; import { isPreviewOpenAtom } from "@/atoms/viewAtoms";
@@ -547,7 +548,7 @@ function ProposalSummary({
packagesAdded = [], packagesAdded = [],
filesChanged = [], filesChanged = [],
}: { }: {
sqlQueries?: string[]; sqlQueries?: Array<SqlQuery>;
serverFunctions?: FileChange[]; serverFunctions?: FileChange[];
packagesAdded?: string[]; packagesAdded?: string[];
filesChanged?: FileChange[]; filesChanged?: FileChange[];
@@ -600,9 +601,12 @@ function ProposalSummary({
} }
// SQL Query item with expandable functionality // SQL Query item with expandable functionality
function SqlQueryItem({ query }: { query: string }) { function SqlQueryItem({ query }: { query: SqlQuery }) {
const [isExpanded, setIsExpanded] = useState(false); const [isExpanded, setIsExpanded] = useState(false);
const queryContent = query.content;
const queryDescription = query.description;
return ( return (
<li <li
className="bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-3 py-2 border border-border cursor-pointer" className="bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-3 py-2 border border-border cursor-pointer"
@@ -611,7 +615,9 @@ function SqlQueryItem({ query }: { query: string }) {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Database size={16} className="text-muted-foreground flex-shrink-0" /> <Database size={16} className="text-muted-foreground flex-shrink-0" />
<span className="text-sm font-medium">SQL Query</span> <span className="text-sm font-medium">
{queryDescription || "SQL Query"}
</span>
</div> </div>
<div> <div>
{isExpanded ? ( {isExpanded ? (
@@ -623,7 +629,9 @@ function SqlQueryItem({ query }: { query: string }) {
</div> </div>
{isExpanded && ( {isExpanded && (
<div className="mt-2 text-xs max-h-[200px] overflow-auto"> <div className="mt-2 text-xs max-h-[200px] overflow-auto">
<CodeHighlight className="language-sql ">{query}</CodeHighlight> <CodeHighlight className="language-sql ">
{queryContent}
</CodeHighlight>
</div> </div>
)} )}
</li> </li>

View File

@@ -14,16 +14,19 @@ import { CustomTagState } from "./stateTypes";
interface DyadExecuteSqlProps { interface DyadExecuteSqlProps {
children?: ReactNode; children?: ReactNode;
node?: any; node?: any;
description?: string;
} }
export const DyadExecuteSql: React.FC<DyadExecuteSqlProps> = ({ export const DyadExecuteSql: React.FC<DyadExecuteSqlProps> = ({
children, children,
node, node,
description,
}) => { }) => {
const [isContentVisible, setIsContentVisible] = useState(false); const [isContentVisible, setIsContentVisible] = useState(false);
const state = node?.properties?.state as CustomTagState; const state = node?.properties?.state as CustomTagState;
const inProgress = state === "pending"; const inProgress = state === "pending";
const aborted = state === "aborted"; const aborted = state === "aborted";
const queryDescription = description || node?.properties?.description;
return ( return (
<div <div
@@ -40,7 +43,7 @@ export const DyadExecuteSql: React.FC<DyadExecuteSqlProps> = ({
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Database size={16} /> <Database size={16} />
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm"> <span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
SQL Query {queryDescription || "SQL Query"}
</span> </span>
{inProgress && ( {inProgress && (
<div className="flex items-center text-amber-600 text-xs"> <div className="flex items-center text-amber-600 text-xs">

View File

@@ -3,6 +3,7 @@ import type {
CodeProposal, CodeProposal,
FileChange, FileChange,
ProposalResult, ProposalResult,
SqlQuery,
} from "../../lib/schemas"; } from "../../lib/schemas";
import { db } from "../../db"; import { db } from "../../db";
import { messages } from "../../db/schema"; import { messages } from "../../db/schema";
@@ -105,7 +106,11 @@ const getProposalHandler = async (
})), })),
]; ];
// Check if we have enough information to create a proposal // Check if we have enough information to create a proposal
if (filesChanged.length > 0 || packagesAdded.length > 0) { if (
filesChanged.length > 0 ||
packagesAdded.length > 0 ||
proposalExecuteSqlQueries.length > 0
) {
const proposal: CodeProposal = { const proposal: CodeProposal = {
type: "code-proposal", type: "code-proposal",
// Use parsed title or a default title if summary tag is missing but write tags exist // Use parsed title or a default title if summary tag is missing but write tags exist
@@ -113,7 +118,10 @@ const getProposalHandler = async (
securityRisks: [], // Keep empty securityRisks: [], // Keep empty
filesChanged, filesChanged,
packagesAdded, packagesAdded,
sqlQueries: proposalExecuteSqlQueries, sqlQueries: proposalExecuteSqlQueries.map((query) => ({
content: query.content,
description: query.description,
})),
}; };
logger.log( logger.log(
"Generated code proposal. title=", "Generated code proposal. title=",

View File

@@ -15,6 +15,7 @@ import {
executeSupabaseSql, executeSupabaseSql,
} from "../../supabase_admin/supabase_management_client"; } from "../../supabase_admin/supabase_management_client";
import { isServerFunction } from "../../supabase_admin/supabase_utils"; import { isServerFunction } from "../../supabase_admin/supabase_utils";
import { SqlQuery } from "../../lib/schemas";
const readFile = fs.promises.readFile; const readFile = fs.promises.readFile;
const logger = log.scope("response_processor"); const logger = log.scope("response_processor");
@@ -108,14 +109,18 @@ export function getDyadChatSummaryTag(fullResponse: string): string | null {
return null; return null;
} }
export function getDyadExecuteSqlTags(fullResponse: string): string[] { export function getDyadExecuteSqlTags(fullResponse: string): SqlQuery[] {
const dyadExecuteSqlRegex = const dyadExecuteSqlRegex =
/<dyad-execute-sql>([\s\S]*?)<\/dyad-execute-sql>/g; /<dyad-execute-sql([^>]*)>([\s\S]*?)<\/dyad-execute-sql>/g;
const descriptionRegex = /description="([^"]+)"/;
let match; let match;
const queries: string[] = []; const queries: { content: string; description?: string }[] = [];
while ((match = dyadExecuteSqlRegex.exec(fullResponse)) !== null) { while ((match = dyadExecuteSqlRegex.exec(fullResponse)) !== null) {
let content = match[1].trim(); const attributesString = match[1] || "";
let content = match[2].trim();
const descriptionMatch = descriptionRegex.exec(attributesString);
const description = descriptionMatch?.[1];
// Handle markdown code blocks if present // Handle markdown code blocks if present
const contentLines = content.split("\n"); const contentLines = content.split("\n");
@@ -127,7 +132,7 @@ export function getDyadExecuteSqlTags(fullResponse: string): string[] {
} }
content = contentLines.join("\n"); content = contentLines.join("\n");
queries.push(content); queries.push({ content, description });
} }
return queries; return queries;
@@ -209,11 +214,11 @@ export async function processFullResponseActions(
try { try {
const result = await executeSupabaseSql({ const result = await executeSupabaseSql({
supabaseProjectId: chatWithApp.app.supabaseProjectId!, supabaseProjectId: chatWithApp.app.supabaseProjectId!,
query, query: query.content,
}); });
} catch (error) { } catch (error) {
errors.push({ errors.push({
message: `Failed to execute SQL query: ${query}`, message: `Failed to execute SQL query: ${query.content}`,
error: error, error: error,
}); });
} }

View File

@@ -132,13 +132,18 @@ export interface FileChange {
isServerFunction: boolean; isServerFunction: boolean;
} }
export interface SqlQuery {
content: string;
description?: string;
}
export interface CodeProposal { export interface CodeProposal {
type: "code-proposal"; type: "code-proposal";
title: string; title: string;
securityRisks: SecurityRisk[]; securityRisks: SecurityRisk[];
filesChanged: FileChange[]; filesChanged: FileChange[];
packagesAdded: string[]; packagesAdded: string[];
sqlQueries: string[]; sqlQueries: SqlQuery[];
} }
export interface SuggestedAction { export interface SuggestedAction {

View File

@@ -89,12 +89,14 @@ function Login() {
## Database ## Database
If the user wants to use the database, use the following code: If the user wants to use the database, use the following syntax:
<dyad-execute-sql> <dyad-execute-sql description="Get all users">
SELECT * FROM users; SELECT * FROM users;
</dyad-execute-sql> </dyad-execute-sql>
The description should be a short description of what the code is doing and be understandable by semi-technical users.
You will need to setup the database schema. You will need to setup the database schema.
## Creating User Profiles ## Creating User Profiles
@@ -103,7 +105,7 @@ If the user wants to create a user profile, use the following code:
### Create profiles table in public schema ### Create profiles table in public schema
<dyad-execute-sql> <dyad-execute-sql description="Create profiles table in public schema">
CREATE TABLE public.profiles ( CREATE TABLE public.profiles (
id UUID NOT NULL REFERENCES auth.users ON DELETE CASCADE, id UUID NOT NULL REFERENCES auth.users ON DELETE CASCADE,
first_name TEXT, first_name TEXT,
@@ -130,7 +132,7 @@ create policy "Users can update own profile." on profiles for update using ( aut
### Function to insert profile when user signs up ### Function to insert profile when user signs up
<dyad-execute-sql> <dyad-execute-sql description="Create function to insert profile when user signs up">
CREATE FUNCTION public.handle_new_user() CREATE FUNCTION public.handle_new_user()
RETURNS TRIGGER RETURNS TRIGGER
LANGUAGE PLPGSQL LANGUAGE PLPGSQL