From b5a97225c85bd57005221c42e491fab2c87ef596 Mon Sep 17 00:00:00 2001 From: Sean Oliver <882952+seanoliver@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:37:22 -0700 Subject: [PATCH] Simplify table quickstart API endpoint Remove unnecessary abstraction layers and dead code: - Combine handler and handlePost into single function - Remove shouldStream function and non-streaming path - Always stream responses (only path client uses) - Clean up imports in TableEditor --- .../TableEditor/TableEditor.tsx | 8 ++-- .../ai/table-quickstart/generate-schemas.ts | 40 ++++--------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx index df86ded3ed2..b3de329b950 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx @@ -1,9 +1,10 @@ import type { PostgresTable } from '@supabase/postgres-meta' import dayjs from 'dayjs' import { isEmpty, isUndefined, noop } from 'lodash' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { toast } from 'sonner' +import { LOCAL_STORAGE_KEYS } from 'common' import { DocsButton } from 'components/ui/DocsButton' import { useDatabasePublicationsQuery } from 'data/database-publications/database-publications-query' import { @@ -19,13 +20,14 @@ import { useEnumeratedTypesQuery } from 'data/enumerated-types/enumerated-types- import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useCustomContent } from 'hooks/custom-content/useCustomContent' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' +import { useLocalStorage } from 'hooks/misc/useLocalStorage' import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' +import { usePHFlag } from 'hooks/ui/useFlag' import { useUrlState } from 'hooks/ui/useUrlState' import { useProtectedSchemas } from 'hooks/useProtectedSchemas' import { DOCS_URL } from 'lib/constants' -import { usePHFlag } from 'hooks/ui/useFlag' import { useTableEditorStateSnapshot } from 'state/table-editor' import { Badge, Checkbox, Input, SidePanel } from 'ui' import { Admonition } from 'ui-patterns' @@ -49,8 +51,6 @@ import { } from './TableEditor.utils' import { TableTemplateSelector } from './TableQuickstart/TableTemplateSelector' import { QuickstartVariant } from './TableQuickstart/types' -import { LOCAL_STORAGE_KEYS } from 'common' -import { useLocalStorage } from 'hooks/misc/useLocalStorage' const NEW_PROJECT_THRESHOLD_DAYS = 7 diff --git a/apps/studio/pages/api/ai/table-quickstart/generate-schemas.ts b/apps/studio/pages/api/ai/table-quickstart/generate-schemas.ts index cd278b97230..7f1a6f885ac 100644 --- a/apps/studio/pages/api/ai/table-quickstart/generate-schemas.ts +++ b/apps/studio/pages/api/ai/table-quickstart/generate-schemas.ts @@ -35,23 +35,10 @@ const ResponseSchema = z.object({ type SchemaStreamResult = ReturnType> const handler = async (req: NextApiRequest, res: NextApiResponse) => { - const { method } = req - - switch (method) { - case 'POST': - return handlePost(req, res) - default: - res.setHeader('Allow', ['POST']) - res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } }) + if (req.method !== 'POST') { + return res.status(405).json({ error: { message: `Method ${req.method} Not Allowed` } }) } -} -const wrapper = (req: NextApiRequest, res: NextApiResponse) => - apiWrapper(req, res, handler, { withAuth: true }) - -export default wrapper - -const handlePost = async (req: NextApiRequest, res: NextApiResponse) => { const { model, error: modelError, @@ -70,7 +57,7 @@ const handlePost = async (req: NextApiRequest, res: NextApiResponse) => { const { prompt } = req.body if (!prompt) { - return res.status(400).json({ error: 'Please provide a description of your application.' }) + return res.status(400).json({ error: 'Please provide a description of your app.' }) } if (typeof prompt !== 'string' || prompt.length > LIMITS.MAX_PROMPT_LENGTH) { @@ -80,8 +67,6 @@ const handlePost = async (req: NextApiRequest, res: NextApiResponse) => { } try { - const wantsStream = shouldStream(req) - const abortController = new AbortController() req.on('close', () => abortController.abort()) req.on('aborted', () => abortController.abort()) @@ -116,15 +101,7 @@ const handlePost = async (req: NextApiRequest, res: NextApiResponse) => { prompt: userPrompt, }) - if (wantsStream) { - await streamSchemaResponse(res, generateStream) - return - } - - const result = generateStream() - const object = await result.object - - return res.status(200).json(object) + await streamSchemaResponse(res, generateStream) } catch (error) { if (error instanceof Error) { if (error.message.includes('context_length') || error.message.includes('too long')) { @@ -140,11 +117,10 @@ const handlePost = async (req: NextApiRequest, res: NextApiResponse) => { } } -const shouldStream = (req: NextApiRequest) => { - if (req.headers['accept']?.includes('text/event-stream')) return true - if (req.headers['x-ai-stream'] === 'true') return true - return false -} +const wrapper = (req: NextApiRequest, res: NextApiResponse) => + apiWrapper(req, res, handler, { withAuth: true }) + +export default wrapper const streamSchemaResponse = async ( res: NextApiResponse,