import { useChat } from '@ai-sdk/react' import { DefaultChatTransport } from 'ai' import { useEffect, useState } from 'react' import { Markdown } from 'components/interfaces/Markdown' import { onErrorChat } from 'components/ui/AIAssistantPanel/AIAssistant.utils' import { BASE_PATH } from 'lib/constants' import { useTrack } from 'lib/telemetry/track' import { AiIconAnimation, Button, Label_Shadcn_, Textarea } from 'ui' interface SupabaseService { name: 'Auth' | 'Storage' | 'Database' | 'Edge Function' | 'Cron' | 'Queues' | 'Vector' reason: string } interface SchemaGeneratorProps { step: 'initial' | 'second' onSqlGenerated: (sql: string) => void onReset?: () => void onServicesUpdated: (services: SupabaseService[]) => void onTitleUpdated: (title: string) => void isOneOff?: boolean } export const SchemaGenerator = ({ step, onSqlGenerated, onServicesUpdated, onTitleUpdated, onReset, isOneOff = false, }: SchemaGeneratorProps) => { const [input, setInput] = useState('') const [hasSql, setHasSql] = useState(false) const [promptIntendSent, setPromptIntendSent] = useState(false) const track = useTrack() const { messages, setMessages, sendMessage, status, addToolResult } = useChat({ id: 'schema-generator', onError: onErrorChat, onFinish: () => { setInput('') }, async onToolCall({ toolCall }) { if (toolCall.toolName === 'executeSql') { try { const sql = (toolCall.input as { sql: string }).sql setHasSql(true) onSqlGenerated(sql) addToolResult({ tool: toolCall.toolName, toolCallId: toolCall.toolCallId, output: 'Database successfully updated. Respond with next steps.', }) } catch (error) { console.error('Failed to execute SQL:', error) addToolResult({ tool: toolCall.toolName, toolCallId: toolCall.toolCallId, output: `SQL execution failed: ${error instanceof Error ? error.message : String(error)}`, }) } } if (toolCall.toolName === 'reset') { try { setHasSql(false) onSqlGenerated('') if (onReset) { onReset() } addToolResult({ tool: toolCall.toolName, toolCallId: toolCall.toolCallId, output: 'Database successfully reset', }) } catch (error) { console.error('Failed to reset the database', error) addToolResult({ tool: toolCall.toolName, toolCallId: toolCall.toolCallId, output: `Resetting the database failed: ${error instanceof Error ? error.message : String(error)}`, }) } } if (toolCall.toolName === 'setServices') { const newServices = (toolCall.input as { services: SupabaseService[] }).services onServicesUpdated(newServices) addToolResult({ tool: toolCall.toolName, toolCallId: toolCall.toolCallId, output: 'Services updated successfully', }) } if (toolCall.toolName === 'setTitle') { const newTitle = (toolCall.input as { title: string }).title onTitleUpdated(newTitle) addToolResult({ tool: toolCall.toolName, toolCallId: toolCall.toolCallId, output: 'Title updated successfully', }) } }, transport: new DefaultChatTransport({ api: `${BASE_PATH}/api/ai/onboarding/design`, }), }) const isMessagesLoading = status === 'submitted' || status === 'streaming' useEffect(() => { if (isOneOff) { setMessages([]) setInput('') } }, [isOneOff, setMessages]) const sendUserMessage = (content: string) => { const payload = { role: 'user' as const, createdAt: new Date(), parts: [{ type: 'text' as const, text: content }], id: `msg-${Date.now()}`, } sendMessage(payload) } const getLastAssistantMessage = () => { const lastAssistantMessage = messages.filter((m) => m.role === 'assistant').slice(-1)[0] if (!lastAssistantMessage?.parts) return '' const textPart = lastAssistantMessage.parts.find((part: any) => part.type === 'text') as | { text: string } | undefined return textPart?.text || '' } return (