From b4fa734c857b4abdc5bd1ef9f635edad09e0b0b9 Mon Sep 17 00:00:00 2001
From: Terry Sutton
Date: Mon, 19 Feb 2024 15:52:58 -0330
Subject: [PATCH] Chore/use completions api (#20246)
* Start
* Refactor code and remove unused imports
* Refactor SchemaFlowHandler and UserChat components
* Refactor code and remove unused files
* Refactor Thread component and remove CurrentThreadName import
* Remove oldest_messages view from supabase.ts
* Refactor supabase.ts file
* Hook up loading state ChatInput component and remove old route handlers
* Add updated prompt
* Refactor chat form component and remove unused code
* Make the suspense work when fetching messages.
* Small refactor in the chat assistant form component.
* Experimenting with streaming responses. WIP.
* Move all components to thread_id/message_id folder.
* Massive refactor but uses Nextjs app router properly.
* Add a conditional submit which is used if the user haven't been logged in.
* Add a typecheck command to db-new app.
* Minor fixes.
* Bunch of minor fixes.
* Clean up more code.
* Refactor the AssistantChatForm to use the new React forms features.
* Run fitView after 50 milliseconds because it didn't run in some cases.
* Style and flow nudges
* Prettier
* Delete old file
---------
Co-authored-by: Ivan Vasilov
---
apps/database-new/app/NewThreadInput.tsx | 81 +---
.../app/[threadId]/[runId]/Chat.tsx | 36 --
.../app/[threadId]/[runId]/ChatInput.tsx | 63 ----
.../app/[threadId]/[runId]/Messages.tsx | 56 ---
.../[runId]/[messageId]/CodeEditor.tsx | 50 ---
.../[runId]/[messageId]/MessageId.utils.ts | 66 ----
.../[runId]/[messageId]/SchemaFlow.tsx | 97 -----
.../[threadId]/[runId]/[messageId]/page.tsx | 29 --
.../app/[threadId]/[runId]/layout.tsx | 22 --
.../app/[threadId]/[runId]/page----delete.tsx | 74 ----
.../[message_id]}/BottomMarker.tsx | 0
.../[thread_id]/[message_id]/CodeEditor.tsx | 23 ++
.../[message_id]}/CodeEditorContainer.tsx | 0
.../[message_id]/MessageItem.tsx} | 64 +---
.../app/[thread_id]/[message_id]/Messages.tsx | 62 +++
.../[thread_id]/[message_id]/SchemaFlow.tsx | 8 +
.../[message_id]/getAssistantMessage.ts | 135 +++++++
.../[thread_id]/[message_id]/getMessages.ts | 10 +
.../app/[thread_id]/[message_id]/page.tsx | 56 +++
apps/database-new/app/actions.ts | 356 ++++++++----------
.../ai/sql/threads/[threadId]/delete/route.ts | 13 -
.../threads/[threadId]/read/[runId]/route.ts | 50 ---
.../ai/sql/threads/[threadId]/update/route.ts | 22 --
.../app/api/ai/sql/threads/create/route.ts | 58 ---
.../app/profile/ConfirmDeleteThreadModal.tsx | 10 +-
.../app/profile/EditThreadModal.tsx | 6 +-
apps/database-new/app/profile/Thread.tsx | 13 +-
apps/database-new/app/profile/Threads.tsx | 13 +-
apps/database-new/app/profile/page.tsx | 2 +-
.../components/AssistantChatForm.tsx | 117 ------
.../ChatLoadingAnimation.tsx | 37 ++
.../AssistantChatForm/ChatSuggestions.tsx | 31 ++
.../components/AssistantChatForm/action.ts | 10 +
.../components/AssistantChatForm/index.tsx | 148 ++++++++
.../components/Auth/LoginForm.tsx | 7 +-
.../components/Header/CurrentThreadName.tsx | 28 --
.../database-new/components/Header/Header.tsx | 17 +-
.../components/Header/HeaderActions.tsx | 36 +-
.../components/Header/SaveSchemaDropdown.tsx | 4 +-
.../components/Loaders/GraphLoader.tsx | 4 +-
.../components/Loaders/SchemaLoader.tsx | 2 +-
.../MonacoEditor}/CodeEditor.utils.ts | 0
.../MonacoEditor/index.tsx} | 0
.../SchemaFlow.constants.ts | 0
.../SchemaFlowHandler}/SchemaFlow.utils.ts | 2 +-
.../SchemaFlowHandler}/TableNode.tsx | 0
.../SchemaFlowHandler/index.tsx} | 30 +-
apps/database-new/components/Shimmer.tsx | 2 +-
apps/database-new/data/conversations-query.ts | 52 ---
apps/database-new/data/messages-query.ts | 28 --
apps/database-new/package.json | 4 +-
.../migrations/20231122191507_get_started.sql | 38 +-
.../20231204135129_remote_schema.sql | 85 -----
.../20231204164321_remote_schema.sql | 3 -
apps/database-new/types/supabase.ts | 209 ++++++----
.../Policies/AIPolicyEditorPanel/Message.tsx | 2 +-
package-lock.json | 7 +-
studio/components/grid/SupabaseGrid.tsx | 210 -----------
58 files changed, 899 insertions(+), 1689 deletions(-)
delete mode 100644 apps/database-new/app/[threadId]/[runId]/Chat.tsx
delete mode 100644 apps/database-new/app/[threadId]/[runId]/ChatInput.tsx
delete mode 100644 apps/database-new/app/[threadId]/[runId]/Messages.tsx
delete mode 100644 apps/database-new/app/[threadId]/[runId]/[messageId]/CodeEditor.tsx
delete mode 100644 apps/database-new/app/[threadId]/[runId]/[messageId]/MessageId.utils.ts
delete mode 100644 apps/database-new/app/[threadId]/[runId]/[messageId]/SchemaFlow.tsx
delete mode 100644 apps/database-new/app/[threadId]/[runId]/[messageId]/page.tsx
delete mode 100644 apps/database-new/app/[threadId]/[runId]/layout.tsx
delete mode 100644 apps/database-new/app/[threadId]/[runId]/page----delete.tsx
rename apps/database-new/app/{[threadId]/[runId] => [thread_id]/[message_id]}/BottomMarker.tsx (100%)
create mode 100644 apps/database-new/app/[thread_id]/[message_id]/CodeEditor.tsx
rename apps/database-new/app/{[threadId]/[runId]/[messageId] => [thread_id]/[message_id]}/CodeEditorContainer.tsx (100%)
rename apps/database-new/app/{[threadId]/[runId]/UserChat.tsx => [thread_id]/[message_id]/MessageItem.tsx} (62%)
create mode 100644 apps/database-new/app/[thread_id]/[message_id]/Messages.tsx
create mode 100644 apps/database-new/app/[thread_id]/[message_id]/SchemaFlow.tsx
create mode 100644 apps/database-new/app/[thread_id]/[message_id]/getAssistantMessage.ts
create mode 100644 apps/database-new/app/[thread_id]/[message_id]/getMessages.ts
create mode 100644 apps/database-new/app/[thread_id]/[message_id]/page.tsx
delete mode 100644 apps/database-new/app/api/ai/sql/threads/[threadId]/delete/route.ts
delete mode 100644 apps/database-new/app/api/ai/sql/threads/[threadId]/read/[runId]/route.ts
delete mode 100644 apps/database-new/app/api/ai/sql/threads/[threadId]/update/route.ts
delete mode 100644 apps/database-new/app/api/ai/sql/threads/create/route.ts
delete mode 100644 apps/database-new/components/AssistantChatForm.tsx
create mode 100644 apps/database-new/components/AssistantChatForm/ChatLoadingAnimation.tsx
create mode 100644 apps/database-new/components/AssistantChatForm/ChatSuggestions.tsx
create mode 100644 apps/database-new/components/AssistantChatForm/action.ts
create mode 100644 apps/database-new/components/AssistantChatForm/index.tsx
delete mode 100644 apps/database-new/components/Header/CurrentThreadName.tsx
rename apps/database-new/{app/[threadId]/[runId]/[messageId] => components/MonacoEditor}/CodeEditor.utils.ts (100%)
rename apps/database-new/{app/[threadId]/[runId]/[messageId]/MonacoEditor.tsx => components/MonacoEditor/index.tsx} (100%)
rename apps/database-new/{app/[threadId]/[runId]/[messageId] => components/SchemaFlowHandler}/SchemaFlow.constants.ts (100%)
rename apps/database-new/{app/[threadId]/[runId]/[messageId] => components/SchemaFlowHandler}/SchemaFlow.utils.ts (99%)
rename apps/database-new/{app/[threadId]/[runId]/[messageId] => components/SchemaFlowHandler}/TableNode.tsx (100%)
rename apps/database-new/{app/[threadId]/[runId]/[messageId]/SchemaFlowHandler.tsx => components/SchemaFlowHandler/index.tsx} (74%)
delete mode 100644 apps/database-new/data/conversations-query.ts
delete mode 100644 apps/database-new/data/messages-query.ts
delete mode 100644 apps/database-new/supabase/migrations/20231204135129_remote_schema.sql
delete mode 100644 apps/database-new/supabase/migrations/20231204164321_remote_schema.sql
delete mode 100644 studio/components/grid/SupabaseGrid.tsx
diff --git a/apps/database-new/app/NewThreadInput.tsx b/apps/database-new/app/NewThreadInput.tsx
index 13881583875..e0183639aa0 100644
--- a/apps/database-new/app/NewThreadInput.tsx
+++ b/apps/database-new/app/NewThreadInput.tsx
@@ -1,53 +1,10 @@
'use client'
-import { CHAT_EXAMPLES } from '@/data/chat-examples'
+import { AssistantChatForm } from '@/components/AssistantChatForm'
import { useAppStateSnapshot } from '@/lib/state'
import { createClient } from '@/lib/supabase/client'
-import { ExternalLink } from 'lucide-react'
-import { useState } from 'react'
-import { useFormState } from 'react-dom'
-import { createThread } from './actions'
-import { AssistantChatForm } from '@/components/AssistantChatForm'
-import { cn } from 'ui'
-
-const suggestions = CHAT_EXAMPLES
const NewThreadInput = () => {
- const [value, setValue] = useState(() => {
- if (typeof window !== 'undefined') {
- const localPrompt = localStorage.getItem('prompt')
- if (localPrompt) {
- localStorage.removeItem('prompt')
- return localPrompt
- }
- }
- return ''
- })
-
- const initialState = {
- message: undefined,
- success: undefined,
- data: {
- value,
- runId: undefined,
- threadId: undefined,
- messageId: undefined,
- },
- }
-
- const [state, formAction] = useFormState(createThread, initialState)
-
- // useEffect(() => {
- // if (state.success && state.data.threadId && state.data.runId && state.data.messageId) {
- // console.log(state.data)
- // const { threadId, runId, messageId } = state.data
- // console.log('threadId', threadId)
- // console.log('runId', runId)
- // console.log('messageId', messageId)
- // // router.push(`/${state.data.threadId}/${state.data.runId}/${state.data.messageId}`)
- // }
- // }, [state.success])
-
const supabase = createClient()
const snap = useAppStateSnapshot()
@@ -55,48 +12,20 @@ const NewThreadInput = () => {
<>
{
+ chatContext={'new'}
+ canSubmit={async () => {
const {
data: { user },
} = await supabase.auth.getUser()
if (!user) {
- event.preventDefault()
- localStorage.setItem('prompt', value)
snap.setLoginDialogOpen(true)
- return
+ return false
}
+ return true
}}
- value={value}
placeholder="e.g Create a Telegram-like chat application"
- onValueChange={(e) => setValue(e.target.value)}
- message={state?.message}
/>
- {state?.message && {state?.message}
}
-
-
- {suggestions.map((suggestion, idx) => (
- {
- setValue(suggestion.prompt)
- event.preventDefault()
- }}
- >
- {suggestion.label}
-
-
- ))}
>
)
diff --git a/apps/database-new/app/[threadId]/[runId]/Chat.tsx b/apps/database-new/app/[threadId]/[runId]/Chat.tsx
deleted file mode 100644
index f4aafefba87..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/Chat.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { Suspense } from 'react'
-import { ScrollArea } from 'ui'
-
-import { cn } from '@ui/lib/utils/cn'
-
-import { Messages } from './Messages'
-import { ChatInput } from './ChatInput'
-import { BottomMarker } from './BottomMarker'
-
-async function Chat({ params }: { params: { threadId: string; runId: string } }) {
- return (
-
-
-
-
- loading}>
-
-
-
-
-
-
-
-
- )
-}
-
-export { Chat }
diff --git a/apps/database-new/app/[threadId]/[runId]/ChatInput.tsx b/apps/database-new/app/[threadId]/[runId]/ChatInput.tsx
deleted file mode 100644
index 64bf1af14dd..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/ChatInput.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-'use client'
-
-import { updateThread } from '@/app/actions'
-import { AssistantChatForm } from '@/components/AssistantChatForm'
-import { useState } from 'react'
-import { useFormState, useFormStatus } from 'react-dom'
-import { Input } from 'ui'
-
-function ChatInput({ params }: { params: { threadId: string; runId: string } }) {
- const [value, setValue] = useState('')
-
- const { threadId, runId } = params
-
- const initialState = {
- message: undefined,
- success: undefined,
- data: {
- value,
- },
- }
-
- const [state, formAction] = useFormState(updateThread, initialState)
-
- const Message = () => {
- const { pending } = useFormStatus()
-
- console.log('state.message', state.message)
-
- return !state.success && state.success !== undefined && !pending ? (
- {state.message}
- ) : null
- }
-
- return (
-
-
setValue(v.target.value)}
- message={state.message}
- >
-
-
-
-
-
- )
-}
-
-export { ChatInput }
diff --git a/apps/database-new/app/[threadId]/[runId]/Messages.tsx b/apps/database-new/app/[threadId]/[runId]/Messages.tsx
deleted file mode 100644
index c4fb282dd94..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/Messages.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import dayjs from 'dayjs'
-import relativeTime from 'dayjs/plugin/relativeTime'
-import { sortBy } from 'lodash'
-import OpenAI from 'openai'
-import UserChat from './UserChat'
-
-dayjs.extend(relativeTime)
-
-const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
-
-async function Messages({ params }: { params: { threadId: string; runId: string } }) {
- const [run, { data: messages }] = await Promise.all([
- openai.beta.threads.runs.retrieve(params.threadId, params.runId),
- openai.beta.threads.messages.list(params.threadId),
- ])
-
- const messagesSorted = sortBy(messages, (m) => m.created_at)
-
- const userMessages = messagesSorted.filter((message) => message.role === 'user')
-
- return (
-
- {userMessages.map((message, idx) => {
- const index = messages.indexOf(message)
-
- const reply = messages[index + 1]
- const isLatest = idx === userMessages.length - 1
-
- const hoursFromNow = dayjs().diff(dayjs(message.created_at * 1000), 'hours')
- const formattedTimeFromNow = dayjs(message.created_at * 1000).fromNow()
- const formattedCreatedAt = dayjs(message.created_at * 1000).format('DD MMM YYYY, HH:mm')
- const replyDuration =
- reply !== undefined ? reply.created_at - message.created_at : undefined
-
- const times = {
- hoursFromNow,
- formattedTimeFromNow,
- formattedCreatedAt,
- replyDuration,
- }
-
- return (
-
- )
- })}
-
- )
-}
-
-export { Messages }
diff --git a/apps/database-new/app/[threadId]/[runId]/[messageId]/CodeEditor.tsx b/apps/database-new/app/[threadId]/[runId]/[messageId]/CodeEditor.tsx
deleted file mode 100644
index 272ba15558f..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/[messageId]/CodeEditor.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { format } from 'sql-formatter'
-
-import { getThread } from './MessageId.utils'
-import { MonacoEditor } from './MonacoEditor'
-import { CodeEditorContainer } from './CodeEditorContainer'
-
-import OpenAI from 'openai'
-
-const openai = new OpenAI()
-
-async function waitForRunCompletion(params: { threadId: string; runId: string }) {
- console.log('waiting for run completion')
- let realRun = await openai.beta.threads.runs.retrieve(params.threadId, params.runId)
-
- while (realRun.status === 'in_progress') {
- //console.log('while..')
- await new Promise((resolve) => setTimeout(resolve, 2000))
- realRun = await openai.beta.threads.runs.retrieve(params.threadId, params.runId)
- //console.log('realRun', realRun.status)
- }
-
- //console.log('Run completed:', realRun.status)
-}
-
-export async function CodeEditor({ params }: { params: any }) {
- const { threadId, runId, messageId } = params
-
- await waitForRunCompletion(params)
-
- const content = await getThread({ threadId, runId, messageId })
-
- const code = format(content, { language: 'postgresql' })
-
- // keep polling until the run is completed
-
- // useEffect(() => {
- // snap.setSelectedCode(code)
- // }, [code])
-
- /**
- * - CodeEditorContainer is a client component, which uses valtio state
- * - MonacoEditor is a server component injected into it
- */
-
- return (
-
-
-
- )
-}
diff --git a/apps/database-new/app/[threadId]/[runId]/[messageId]/MessageId.utils.ts b/apps/database-new/app/[threadId]/[runId]/[messageId]/MessageId.utils.ts
deleted file mode 100644
index 73e2ef4ccb1..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/[messageId]/MessageId.utils.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-'use server'
-
-import OpenAI from 'openai'
-
-import { AssistantMessage } from '@/lib/types'
-import { compact, sortBy } from 'lodash'
-
-const openai = new OpenAI()
-
-async function getThread({
- threadId,
- runId,
- messageId,
-}: {
- threadId: string
- runId: string
- messageId: string
-}) {
- const [run, { data: _messages }] = await Promise.all([
- openai.beta.threads.runs.retrieve(threadId, runId),
- openai.beta.threads.messages.list(threadId),
- ])
-
- let messages = compact(
- await Promise.all(
- _messages.map(async (m) => {
- if (m.role === 'user' && m.content[0].type === 'text') {
- return {
- id: m.id,
- role: 'user' as const,
- created_at: m.created_at,
- text: m.content[0].text.value,
- }
- }
-
- if (m.content.length >= 1 && m.content[0].type === 'text') {
- let sql = ''
- if (m.content[0].type === 'text') {
- sql = m.content[0].text.value.replaceAll('\n', '')
- }
- return {
- id: m.id,
- role: 'assistant' as const,
- created_at: m.created_at,
- sql,
- }
- }
- })
- )
- )
-
- messages = sortBy(messages, (m) => m.created_at)
-
- const userMessages = messages.filter((m) => m.role === 'user')
-
- const selectedMessageIdx = messages.findIndex((m) => m.id === messageId)
- const selectedMessageReply = (
- selectedMessageIdx !== -1 ? messages[selectedMessageIdx + 1] : undefined
- ) as AssistantMessage | undefined
-
- const content = selectedMessageReply?.sql.replaceAll('```sql', '').replaceAll('```', '') || ''
-
- return content
-}
-
-export { getThread }
diff --git a/apps/database-new/app/[threadId]/[runId]/[messageId]/SchemaFlow.tsx b/apps/database-new/app/[threadId]/[runId]/[messageId]/SchemaFlow.tsx
deleted file mode 100644
index 8f221fac4ae..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/[messageId]/SchemaFlow.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { format } from 'sql-formatter'
-
-import { getThread } from './MessageId.utils'
-
-import { parseTables } from '@/lib/utils'
-import SchemaFlowHandler from './SchemaFlowHandler'
-
-import OpenAI from 'openai'
-import { createClient } from '@/lib/supabase/server'
-
-import { cookies } from 'next/headers'
-
-const openai = new OpenAI()
-
-async function waitForRunCompletion(params: {
- threadId: string
- runId: string
- messageId: string
-}) {
- console.log('Waiting for run completion')
- let realRun = await openai.beta.threads.runs.retrieve(params.threadId, params.runId)
-
- if (realRun.status !== 'in_progress') {
- console.log('Run is not in progress. Exiting without running anything.')
-
- // Check if it exists in Supabase DB
- try {
- const cookieStore = cookies()
- const supabase = createClient(cookieStore)
-
- const { data, error } = await supabase
- .from('responses_ai')
- .select('*')
- .eq('message_id', params.messageId)
-
- if (error) throw error
-
- if (data.length > 0) {
- return { newMessage: false }
- }
- } catch (error) {
- console.error('The error is', error)
- }
- }
-
- while (realRun.status === 'in_progress') {
- await new Promise((resolve) => setTimeout(resolve, 2000))
- realRun = await openai.beta.threads.runs.retrieve(params.threadId, params.runId)
- }
-
- console.log('Run completed:', realRun.status)
- return { newMessage: true }
-}
-
-export async function SchemaFlow({ params }: { params: any }) {
- //console.log('schemaFlow refetching')
-
- const { threadId, runId, messageId } = params
-
- // keep polling until the run is completed
- const { newMessage } = await waitForRunCompletion(params)
-
- const content = await getThread({ threadId, runId, messageId })
-
- const cookieStore = cookies()
- const supabase = createClient(cookieStore)
-
- const {
- data: { user },
- error: userError,
- } = await supabase.auth.getUser()
- if (!user) return
- if (!newMessage) return
-
- try {
- const { error } = await supabase.from('responses_ai').insert({
- message_id: messageId,
- thread_id: threadId,
- text: content,
- run_id: runId,
- user_id: user.id,
- })
- if (error) throw error
- } catch (error) {
- console.error(error)
- }
- console.log({ content })
- //const code = format(content, { language: 'postgresql' })
-
- //const tables = await parseTables(content)
-
- // useEffect(() => {
- // snap.setSelectedCode(code)
- // }, [code])
-
- return
-}
diff --git a/apps/database-new/app/[threadId]/[runId]/[messageId]/page.tsx b/apps/database-new/app/[threadId]/[runId]/[messageId]/page.tsx
deleted file mode 100644
index eca31d6840f..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/[messageId]/page.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { CodeEditor } from '@/app/[threadId]/[runId]/[messageId]/CodeEditor'
-import { SchemaFlow } from '@/app/[threadId]/[runId]/[messageId]/SchemaFlow'
-import GraphLoader from '@/components/Loaders/GraphLoader'
-import SchemaLoader from '@/components/Loaders/SchemaLoader'
-import { Loader2 } from 'lucide-react'
-import Image from 'next/image'
-import { Suspense } from 'react'
-
-interface ThreadPageProps {
- params: {
- threadId: string
- runId: string
- messageId: string
- }
-}
-
-export default async function ThreadPage({ params }: ThreadPageProps) {
- return (
-
- }>
-
-
-
- }>
-
-
-
- )
-}
diff --git a/apps/database-new/app/[threadId]/[runId]/layout.tsx b/apps/database-new/app/[threadId]/[runId]/layout.tsx
deleted file mode 100644
index c8bf55dbef4..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/layout.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Chat } from '@/app/[threadId]/[runId]/Chat'
-import SaveSchemaDropdown from '@/components/Header/SaveSchemaDropdown'
-import ToggleCodeEditorButton from '@/components/Header/ToggleCodeEditorButton'
-
-export default function Layout({
- children,
- params,
-}: {
- children: React.ReactNode
- params: { threadId: string; runId: string }
-}) {
- return (
-
- )
-}
diff --git a/apps/database-new/app/[threadId]/[runId]/page----delete.tsx b/apps/database-new/app/[threadId]/[runId]/page----delete.tsx
deleted file mode 100644
index 87b9c8530a3..00000000000
--- a/apps/database-new/app/[threadId]/[runId]/page----delete.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import * as React from 'react'
-
-const RunIdPage = async () => {
- return <>>
-}
-
-import OpenAI from 'openai'
-
-import { AssistantMessage } from '@/lib/types'
-import { compact, sortBy } from 'lodash'
-
-const openai = new OpenAI()
-
-async function getThread({
- threadId,
- runId,
- messageId,
-}: {
- threadId: string
- runId: string
- messageId: string
-}) {
- console.log('getting thread..', threadId, runId, messageId)
-
- const [run, { data: _messages }] = await Promise.all([
- openai.beta.threads.runs.retrieve(threadId, runId),
- openai.beta.threads.messages.list(threadId),
- ])
-
- let messages = compact(
- await Promise.all(
- _messages.map(async (m) => {
- if (m.role === 'user' && m.content[0].type === 'text') {
- return {
- id: m.id,
- role: 'user' as const,
- created_at: m.created_at,
- text: m.content[0].text.value,
- }
- }
-
- if (m.content.length >= 1 && m.content[0].type === 'text') {
- let sql = ''
- if (m.content[0].type === 'text') {
- sql = m.content[0].text.value.replaceAll('\n', '')
- }
- return {
- id: m.id,
- role: 'assistant' as const,
- created_at: m.created_at,
- sql,
- }
- }
- })
- )
- )
-
- messages = sortBy(messages, (m) => m.created_at)
-
- // const userMessages = messages.filter((m) => m.role === 'user')
-
- const selectedMessageIdx = messages.findIndex((m) => m.id === messageId)
- const selectedMessageReply = (
- selectedMessageIdx !== -1 ? messages[selectedMessageIdx + 1] : undefined
- ) as AssistantMessage | undefined
-
- const content = selectedMessageReply?.sql.replaceAll('```sql', '').replaceAll('```', '') || ''
-
- return content
-}
-
-export { getThread }
-
-export default RunIdPage
diff --git a/apps/database-new/app/[threadId]/[runId]/BottomMarker.tsx b/apps/database-new/app/[thread_id]/[message_id]/BottomMarker.tsx
similarity index 100%
rename from apps/database-new/app/[threadId]/[runId]/BottomMarker.tsx
rename to apps/database-new/app/[thread_id]/[message_id]/BottomMarker.tsx
diff --git a/apps/database-new/app/[thread_id]/[message_id]/CodeEditor.tsx b/apps/database-new/app/[thread_id]/[message_id]/CodeEditor.tsx
new file mode 100644
index 00000000000..8197c3ab6ac
--- /dev/null
+++ b/apps/database-new/app/[thread_id]/[message_id]/CodeEditor.tsx
@@ -0,0 +1,23 @@
+import { format } from 'sql-formatter'
+
+import { MonacoEditor } from '@/components/MonacoEditor'
+import { CodeEditorContainer } from './CodeEditorContainer'
+
+export async function CodeEditor({ promisedMessage }: { promisedMessage: Promise }) {
+ const code = await promisedMessage
+
+ // strip the ```sql ``` from the code
+ // not sure why we need this & can't get openai to stop returning it
+ const strippedCode = code.replace('```sql\n', '').replace('\n```', '')
+ const formattedCode = format(strippedCode, { language: 'postgresql' })
+
+ /**
+ * - CodeEditorContainer is a client component, which uses valtio state
+ * - MonacoEditor is a server component injected into it
+ */
+ return (
+
+
+
+ )
+}
diff --git a/apps/database-new/app/[threadId]/[runId]/[messageId]/CodeEditorContainer.tsx b/apps/database-new/app/[thread_id]/[message_id]/CodeEditorContainer.tsx
similarity index 100%
rename from apps/database-new/app/[threadId]/[runId]/[messageId]/CodeEditorContainer.tsx
rename to apps/database-new/app/[thread_id]/[message_id]/CodeEditorContainer.tsx
diff --git a/apps/database-new/app/[threadId]/[runId]/UserChat.tsx b/apps/database-new/app/[thread_id]/[message_id]/MessageItem.tsx
similarity index 62%
rename from apps/database-new/app/[threadId]/[runId]/UserChat.tsx
rename to apps/database-new/app/[thread_id]/[message_id]/MessageItem.tsx
index f1457b55ff6..98aa11796d8 100644
--- a/apps/database-new/app/[threadId]/[runId]/UserChat.tsx
+++ b/apps/database-new/app/[thread_id]/[message_id]/MessageItem.tsx
@@ -1,61 +1,30 @@
'use client'
-import { useAppStateSnapshot } from '@/lib/state'
-import { pull } from 'lodash'
+import { Message as MessageItem } from 'ai/react'
import Link from 'next/link'
-import { useParams, usePathname, useRouter } from 'next/navigation'
-import OpenAI from 'openai'
-import { useEffect } from 'react'
+import { useParams, usePathname } from 'next/navigation'
import { cn } from 'ui'
-interface UserChatProps {
- message: OpenAI.Beta.Threads.Messages.ThreadMessage
+interface MessageItemProps {
+ message: MessageItem
isLatest: boolean
times: {
hoursFromNow: number
formattedTimeFromNow: string
formattedCreatedAt: string
- replyDuration: number | undefined
}
- run: OpenAI.Beta.Threads.Run
}
-const UserChat = ({ message, isLatest, times, run }: UserChatProps) => {
- const router = useRouter()
- const snap = useAppStateSnapshot()
- const { threadId, runId } = useParams()
-
- // console.log(run)
-
- const LOADING_STATUSES = ['in_progress', 'queued']
-
- const runIsInProgressRemotely = LOADING_STATUSES.includes(run.status)
-
- useEffect(() => {
- if (runIsInProgressRemotely) {
- // set a local state for run loading
- // this state will be updated via other client components when completing a run
- // remove current message id from array if it exists
- let currentRunsLoading = [...snap.runsLoading]
- pull(currentRunsLoading, run.id)
- const payload = [...currentRunsLoading, run.id]
- snap.setRunsLoading([...payload])
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [runIsInProgressRemotely, run.id]) // Intentionally left snap out of the dependency array
-
+const MessageItem = ({ message, isLatest, times }: MessageItemProps) => {
+ const { thread_id } = useParams()
// using the local state for run loading
- const isLoading = snap.runsLoading.includes(run.id) && isLatest
+ const isLoading = false
- const { hoursFromNow, formattedTimeFromNow, formattedCreatedAt, replyDuration } = times
+ const { hoursFromNow, formattedTimeFromNow, formattedCreatedAt } = times
// chat shown as selected when url matches
const isSelected = usePathname().includes(message.id)
- // extract the text from the assistant message
- const message_content = message.content[0]
- const text = message_content.type === 'text' ? message_content.text.value : ''
-
return (
{
isSelected && 'bg-surface-200',
isSelected ? 'border-r-foreground' : 'border-r border-r-transparent'
)}
- href={`/${threadId}/${runId}/${message.id}`}
+ href={`/${thread_id}/${message.id}`}
>
{
: 'bg-transparent border-foreground-muted group-hover:border-foreground'
)}
/>
+
{isLoading && (
{
)}
+
{/* Node line*/}
{!isLatest &&
}
@@ -106,7 +77,7 @@ const UserChat = ({ message, isLatest, times, run }: UserChatProps) => {
{
isSelected ? 'text-foreground' : 'text-light group-hover:text-foreground'
)}
>
- {text}
+ {message.content}
- {/* {isLoading &&
} */}
+ {isLoading &&
}
{isSelected && (
@@ -135,11 +106,6 @@ const UserChat = ({ message, isLatest, times, run }: UserChatProps) => {
)}
>
Sent {hoursFromNow > 6 ? `on ${formattedCreatedAt}` : formattedTimeFromNow}
- {replyDuration !== undefined
- ? ` with ${replyDuration}s response`
- : isLoading
- ? ', generating response...'
- : ''}
)}
@@ -147,4 +113,4 @@ const UserChat = ({ message, isLatest, times, run }: UserChatProps) => {
)
}
-export default UserChat
+export default MessageItem
diff --git a/apps/database-new/app/[thread_id]/[message_id]/Messages.tsx b/apps/database-new/app/[thread_id]/[message_id]/Messages.tsx
new file mode 100644
index 00000000000..6954b2b1957
--- /dev/null
+++ b/apps/database-new/app/[thread_id]/[message_id]/Messages.tsx
@@ -0,0 +1,62 @@
+import { ScrollArea } from 'ui'
+
+import dayjs from 'dayjs'
+import relativeTime from 'dayjs/plugin/relativeTime'
+import { BottomMarker } from './BottomMarker'
+import { getMessages } from './getMessages'
+
+import MessageItem from './MessageItem'
+
+dayjs.extend(relativeTime)
+
+export const Messages = async ({ threadId }: { threadId: string }) => {
+ const { data: messages, error } = await getMessages(threadId)
+
+ if (error) {
+ return <>Error happened>
+ }
+
+ const initialMessages = messages.flatMap((m) => [
+ {
+ id: m.message_id,
+ content: m.message_input,
+ role: 'user' as const,
+ createdAt: new Date(m.created_at),
+ },
+ {
+ id: m.message_id,
+ content: m.message_content,
+ role: 'assistant' as const,
+ createdAt: new Date(m.created_at),
+ },
+ ])
+
+ const userMessages = initialMessages.filter((m) => m.role === 'user')
+
+ return (
+
+
+ {userMessages.map((message, idx) => {
+ const createdAtTimestamp = dayjs(message.createdAt)
+ const isLatest = Array.isArray(messages) && idx === messages.length - 1
+
+ const hoursFromNow = dayjs().diff(createdAtTimestamp, 'hour')
+ const formattedTimeFromNow = dayjs(createdAtTimestamp).fromNow()
+
+ const formattedCreatedAt = dayjs(createdAtTimestamp).format('DD MMM YYYY, HH:mm')
+
+ const times = {
+ hoursFromNow,
+ formattedTimeFromNow,
+ formattedCreatedAt,
+ }
+
+ return (
+
+ )
+ })}
+
+
+
+ )
+}
diff --git a/apps/database-new/app/[thread_id]/[message_id]/SchemaFlow.tsx b/apps/database-new/app/[thread_id]/[message_id]/SchemaFlow.tsx
new file mode 100644
index 00000000000..e53a476920a
--- /dev/null
+++ b/apps/database-new/app/[thread_id]/[message_id]/SchemaFlow.tsx
@@ -0,0 +1,8 @@
+import SchemaFlowHandler from '@/components/SchemaFlowHandler'
+
+export async function SchemaFlow({ promisedMessage }: { promisedMessage: Promise }) {
+ const code = await promisedMessage
+ const strippedCode = code.replace('```sql\n', '').replace('\n```', '')
+
+ return
+}
diff --git a/apps/database-new/app/[thread_id]/[message_id]/getAssistantMessage.ts b/apps/database-new/app/[thread_id]/[message_id]/getAssistantMessage.ts
new file mode 100644
index 00000000000..00793d04506
--- /dev/null
+++ b/apps/database-new/app/[thread_id]/[message_id]/getAssistantMessage.ts
@@ -0,0 +1,135 @@
+import { stripIndent } from 'common-tags'
+import { cookies } from 'next/headers'
+import OpenAI from 'openai'
+
+// import is weird, what's up with this?
+import { createClient } from '@/lib/supabase/server'
+import { ContextLengthError } from '../../../../../packages/ai-commands/src/errors'
+import { getMessages } from './getMessages'
+
+// Create an OpenAI API client (that's edge friendly!)
+const openai = new OpenAI({
+ apiKey: process.env.OPENAI_API_KEY,
+})
+
+// Set the runtime to edge for best performance
+export const runtime = 'edge'
+
+export type AiAssistantMessage = {
+ content: string
+ role: 'user' | 'assistant'
+}
+
+/**
+ * Get the latest AI generated message. In the DB, there's a pair of user prompt and AI response, so if the
+ * user sent a message but that message lacks the corresponding AI response, a call will be made to OpenAI to generate
+ * it.
+ */
+export const getAssistantResponse = async (threadId: string, messageId: string) => {
+ const cookieStore = cookies()
+ const supabase = createClient(cookieStore)
+
+ const { data: message, error } = await supabase
+ .from('messages')
+ .select()
+ .eq('message_id', messageId)
+ .single()
+
+ if (!message || error) {
+ throw new Error('A message with that id does not exist.')
+ }
+
+ // if the message lacks the AI response, fetch it from OpenAI
+ if (!message.message_content) {
+ const { data: messages, error } = await getMessages(threadId)
+
+ if (error) {
+ throw new Error('Error while trying to fetch the existing messages.')
+ }
+
+ // transform the DB messages into a format that OpenAI understands
+ const existingMessages = messages.flatMap((m) => [
+ {
+ content: m.message_input,
+ role: 'user' as const,
+ },
+ {
+ content: m.message_content,
+ role: 'assistant' as const,
+ },
+ ])
+
+ const initMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
+ {
+ role: 'system',
+ content: stripIndent`
+ Your purpose is to generate a SQL schema where the user will give commands to you via a chat.
+ The output should use the following instructions:
+ - The generated SQL must be valid SQL.
+ - For primary keys, always use "id bigint primary key generated always as identity" (not serial)
+ - Always create foreign key references in the create statement
+ - Prefer 'text' over 'varchar'
+ - Prefer 'timestamp with time zone' over 'date'
+ - Use vector(384) data type for any embedding/vector related query
+ - Always use double apostrophe in SQL strings (eg. 'Night''s watch')
+ - Always omit \`\`\`sql from your reply
+ - You can use only CREATE TABLE queries, no other queries are allowed under no circumstances (ALTER TABLE etc).
+ - On each subsequent message from the user, rewrite the original response to include the new requirement.
+ - Don't add any SQL comments in the code
+ - Never put a comma before a round bracket
+
+ The output should look like this: "CREATE TABLE users (id bigint primary key generated always as identity)"
+
+ DO NOT RESPOND WITH ANYTHING ELSE.
+ YOU MUST NOT ANSWER WITH ANY PLAIN TEXT
+ ONLY RESPOND WITH 1 CODE BLOCK
+ YOU MUST NOT FOLLOW UP ANY CODE BLOCKS WITH ANY EXPLANATION
+ `,
+ },
+ ]
+
+ initMessages.push(...existingMessages)
+
+ try {
+ const response = await openai.chat.completions.create({
+ model: 'gpt-3.5-turbo-16k-0613',
+ messages: initMessages,
+ max_tokens: 1024,
+ temperature: 0,
+ stream: false,
+ })
+
+ const newMessage = response.choices[0].message.content || ''
+
+ const {
+ data: { user },
+ error: userError,
+ } = await supabase.auth.getUser()
+
+ if (userError || !user) {
+ throw new Error('Failed to get user')
+ }
+
+ // Insert the message from the completion
+ const { data, error } = await supabase
+ .from('messages')
+ .update({ message_content: newMessage })
+ .eq('id', message.id)
+ .select()
+ .single()
+
+ if (error || !data) {
+ throw new Error('Error while updating the existing message with the completion data.')
+ }
+
+ return data.message_content
+ } catch (error) {
+ if (error instanceof Error && 'code' in error && error.code === 'context_length_exceeded') {
+ throw new ContextLengthError()
+ }
+ throw error
+ }
+ }
+
+ return message.message_content
+}
diff --git a/apps/database-new/app/[thread_id]/[message_id]/getMessages.ts b/apps/database-new/app/[thread_id]/[message_id]/getMessages.ts
new file mode 100644
index 00000000000..f4ea971894b
--- /dev/null
+++ b/apps/database-new/app/[thread_id]/[message_id]/getMessages.ts
@@ -0,0 +1,10 @@
+import { createClient } from '@/lib/supabase/server'
+import { cookies } from 'next/headers'
+import { cache } from 'react'
+
+export const getMessages = cache(async (thread_id: string) => {
+ const cookieStore = cookies()
+ const supabase = createClient(cookieStore)
+
+ return await supabase.from('messages').select().eq('thread_id', thread_id).order('created_at')
+})
diff --git a/apps/database-new/app/[thread_id]/[message_id]/page.tsx b/apps/database-new/app/[thread_id]/[message_id]/page.tsx
new file mode 100644
index 00000000000..4ff81c18eb7
--- /dev/null
+++ b/apps/database-new/app/[thread_id]/[message_id]/page.tsx
@@ -0,0 +1,56 @@
+import { AssistantChatForm } from '@/components/AssistantChatForm'
+import SaveSchemaDropdown from '@/components/Header/SaveSchemaDropdown'
+import ToggleCodeEditorButton from '@/components/Header/ToggleCodeEditorButton'
+import GraphLoader from '@/components/Loaders/GraphLoader'
+import SchemaLoader from '@/components/Loaders/SchemaLoader'
+import { cn } from '@ui/lib/utils/cn'
+import { Suspense } from 'react'
+import { CodeEditor } from './CodeEditor'
+import { Messages } from './Messages'
+import { SchemaFlow } from './SchemaFlow'
+import { getAssistantResponse } from './getAssistantMessage'
+
+export interface ThreadPageProps {
+ params: {
+ thread_id: string
+ message_id: string
+ }
+}
+
+export default async function ThreadPage({ params }: ThreadPageProps) {
+ const promise = getAssistantResponse(params.thread_id, params.message_id)
+
+ return (
+
+
+
+
+
+
+
+
+ }>
+
+
+ }>
+
+
+
+
+ )
+}
diff --git a/apps/database-new/app/actions.ts b/apps/database-new/app/actions.ts
index 6b5ebebdd93..3d17b972031 100644
--- a/apps/database-new/app/actions.ts
+++ b/apps/database-new/app/actions.ts
@@ -1,21 +1,12 @@
'use server'
-import { last, sortBy } from 'lodash'
-
-import { AssistantMessage } from '@/lib/types'
-import { parseTables } from '@/lib/utils'
-
import { createClient } from '@/lib/supabase/server'
+import { Message } from 'ai'
import { revalidatePath } from 'next/cache'
import { cookies } from 'next/headers'
-import { RedirectType, redirect } from 'next/navigation'
+import { redirect } from 'next/navigation'
-import OpenAI from 'openai'
import { z } from 'zod'
-import { threadId } from 'worker_threads'
-import { MessageContentText } from 'openai/resources/beta/threads/index.mjs'
-
-const openai = new OpenAI()
export async function logout() {
const cookieStore = cookies()
@@ -32,6 +23,151 @@ export async function logout() {
redirect('/')
}
+//export async function createThread(prevState: any, formData: FormData) {
+export async function createThread(input: string, message: Message, currentThread?: string) {
+ const cookieStore = cookies()
+ const supabase = createClient(cookieStore)
+
+ let thread_id = currentThread ?? ''
+ let message_id = ''
+
+ try {
+ const {
+ data: { user },
+ error: userError,
+ } = await supabase.auth.getUser()
+
+ if (userError || !user) {
+ return {
+ success: false,
+ message: 'Failed to get user',
+ data: undefined,
+ }
+ }
+
+ // create a new thread
+ if (!thread_id) {
+ try {
+ const { data, error } = await supabase
+ .from('threads')
+ .insert({ user_id: user.id, thread_title: input })
+ .select()
+
+ if (error) throw error
+
+ if (data) {
+ console.log({ data })
+ thread_id = data[0].id
+ // message_id = data[0].message_id
+ }
+ } catch (error) {
+ console.error(error)
+ }
+ }
+
+ //insert the message from the completion
+ try {
+ const { data, error } = await supabase
+ .from('messages')
+ .insert({
+ thread_id,
+ message_content: message.content,
+ message_input: input,
+ message_role: 'assistant',
+ user_id: user.id,
+ })
+ .select()
+
+ if (error) throw error
+ if (data) {
+ message_id = data[0].message_id
+ }
+ } catch (error) {
+ console.error(error)
+ }
+ } catch (error: any) {
+ console.error(error)
+ return {
+ success: false,
+ message: 'Failed to update title to update title',
+ data: undefined,
+ }
+ }
+ redirect(`/${thread_id}/${message_id}`)
+}
+
+export async function upsertThreadMessage(input: string, currentThread?: string) {
+ const cookieStore = cookies()
+ const supabase = createClient(cookieStore)
+
+ let thread_id = currentThread ?? ''
+ let message_id = ''
+
+ try {
+ const {
+ data: { user },
+ error: userError,
+ } = await supabase.auth.getUser()
+
+ if (userError || !user) {
+ return {
+ success: false,
+ message: 'Failed to get user',
+ data: undefined,
+ }
+ }
+
+ // create a new thread
+ if (!thread_id) {
+ try {
+ const { data, error } = await supabase
+ .from('threads')
+ .insert({ user_id: user.id, thread_title: input })
+ .select()
+
+ if (error) throw error
+
+ if (data) {
+ console.log({ data })
+ thread_id = data[0].id
+ // message_id = data[0].message_id
+ }
+ } catch (error) {
+ console.error(error)
+ }
+ }
+
+ //insert the message from the completion
+ try {
+ const { data, error } = await supabase
+ .from('messages')
+ .insert({
+ thread_id,
+ message_content: '',
+ message_input: input,
+ message_role: 'assistant',
+ user_id: user.id,
+ })
+ .select()
+
+ if (error) throw error
+ if (data) {
+ message_id = data[0].message_id
+ }
+ } catch (error) {
+ console.error(error)
+ }
+ } catch (error: any) {
+ console.error(error)
+ return {
+ success: false,
+ message: 'Failed to update title to update title',
+ data: undefined,
+ }
+ }
+ redirect(`/${thread_id}/${message_id}`)
+}
+
export async function deleteThread(prevState: any, formData: FormData) {
const cookieStore = cookies()
const supabase = createClient(cookieStore)
@@ -45,8 +181,7 @@ export async function deleteThread(prevState: any, formData: FormData) {
thread_id: formData.get('thread_id'),
})
- await supabase.from('threads').delete().eq('thread_id', data.thread_id)
- await openai.beta.threads.del(data.thread_id)
+ await supabase.from('threads').delete().eq('id', data.thread_id)
revalidatePath('/profile')
@@ -71,18 +206,18 @@ export async function updateThreadName(prevState: any, formData: FormData) {
try {
const schema = z.object({
thread_title: z.string(),
- row_id: z.string(),
+ thread_id: z.string(),
})
const data = schema.parse({
thread_title: formData.get('thread_title'),
- row_id: formData.get('row_id'),
+ thread_id: formData.get('thread_id'),
})
const { error } = await supabase
.from('threads')
.update({ thread_title: data.thread_title })
- .eq('id', data.row_id)
+ .eq('id', data.thread_id)
if (error) {
throw error
}
@@ -102,192 +237,3 @@ export async function updateThreadName(prevState: any, formData: FormData) {
}
}
}
-
-export async function createThread(prevState: any, formData: FormData) {
- const cookieStore = cookies()
- const supabase = createClient(cookieStore)
-
- let redirectUrl = ''
-
- try {
- const schema = z.object({
- value: z.string(),
- })
-
- const data = schema.parse({
- value: formData.get('value'),
- })
-
- const {
- data: { user },
- error: userError,
- } = await supabase.auth.getUser()
-
- if (userError || !user) {
- return {
- success: false,
- message: 'Failed to get user',
- data: undefined,
- }
- }
-
- const thread = await openai.beta.threads.create()
-
- const message = await openai.beta.threads.messages.create(thread.id, {
- role: 'user',
- content: data.value,
- })
-
- const createRun = await openai.beta.threads.runs.create(thread.id, {
- assistant_id: 'asst_oLWrK8lScZVNEpfjwUIvBAnq',
- })
-
- const [run, { data: messages }] = await Promise.all([
- openai.beta.threads.runs.retrieve(thread.id, createRun.id),
- openai.beta.threads.messages.list(thread.id),
- ])
-
- const threadTitle = messages
- .filter((m) => m.role === 'user' && m.content[0]?.type === 'text')
- .map((m) => {
- if (m.content[0]?.type === 'text') {
- return m.content[0]?.text?.value
- }
- return undefined
- })
- .find((text) => text !== undefined)
-
- try {
- const { error } = await supabase.from('threads').insert({
- thread_id: thread.id,
- run_id: run.id,
- user_id: user.id,
- thread_title: threadTitle as string,
- latest_message_id: message.id,
- })
- revalidatePath('/profile')
- if (error) throw error
- } catch (error) {
- console.error(error)
- }
-
- // insert into supabase
- try {
- const { error } = await supabase.from('messages_user').insert({
- message_id: message.id,
- thread_id: thread.id,
- text: (message.content[0] as MessageContentText).text.value,
- run_id: run.id,
- user_id: user.id,
- })
- if (error) throw error
- } catch (error) {
- console.error(error)
- }
-
- redirectUrl = `/${thread.id}/${run.id}/${message.id}`
- // return {
- // success: true,
- // message: 'Managed to run action',
- // data: {
- // messageId: message.id,
- // runId: run.id,
- // threadId: thread.id,
- // },
- // }
- } catch (error: any) {
- console.error(error)
- return {
- success: false,
- message: 'Failed to update title to update title',
- data: undefined,
- }
- }
-
- redirect(redirectUrl)
-}
-
-export async function updateThread(prevState: any, formData: FormData) {
- const cookieStore = cookies()
- const supabase = createClient(cookieStore)
- let redirectUrl = ''
-
- try {
- const schema = z.object({
- value: z.string(),
- threadId: z.string(),
- runId: z.string(),
- })
-
- const data = schema.parse({
- value: formData.get('value'),
- threadId: formData.get('threadId'),
- runId: formData.get('runId'),
- })
-
- const message = await openai.beta.threads.messages.create(data.threadId, {
- content: data.value,
- role: 'user',
- })
-
- revalidatePath(`/${data.threadId}/${data.runId}`, 'layout')
-
- // insert into supabase
-
- const {
- data: { user },
- error: userError,
- } = await supabase.auth.getUser()
-
- if (userError || !user) {
- return {
- success: false,
- message: 'Failed to get user',
- data: undefined,
- }
- }
- console.log('message.content[0]', message.content[0])
- try {
- const { error } = await supabase.from('messages_user').insert({
- message_id: message.id,
- thread_id: data.threadId,
- text: (message.content[0] as MessageContentText).text.value,
- run_id: data.runId,
- user_id: user.id,
- })
- if (error) throw error
- } catch (error) {
- console.error(error)
- }
- console.log('message.id', message.id)
- // update existing thread with latest messageID
- try {
- const { error } = await supabase
- .from('threads')
- .update({
- latest_message_id: message.id,
- })
- .eq('thread_id', data.threadId)
- if (error) throw error
- } catch (error) {
- console.error('Update error:', error)
- }
-
- const run = await openai.beta.threads.runs.create(message.thread_id, {
- assistant_id: 'asst_oLWrK8lScZVNEpfjwUIvBAnq',
- })
-
- revalidatePath(`/${data.threadId}/${data.runId}/${message.id}`, 'layout')
-
- redirectUrl = `/${message.thread_id}/${run.id}/${message.id}`
- } catch (error: any) {
- return {
- success: false,
- message: 'Failed to update schema with new prompt',
- data: undefined,
- }
- }
-
- revalidatePath(redirectUrl)
- redirect(redirectUrl)
-}
diff --git a/apps/database-new/app/api/ai/sql/threads/[threadId]/delete/route.ts b/apps/database-new/app/api/ai/sql/threads/[threadId]/delete/route.ts
deleted file mode 100644
index ac5b4e0554f..00000000000
--- a/apps/database-new/app/api/ai/sql/threads/[threadId]/delete/route.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import OpenAI from 'openai'
-
-const openai = new OpenAI()
-
-export async function POST(req: Request, { params }: { params: { threadId: string } }) {
- if (!req.body) {
- return Response.error()
- }
-
- const kill = await openai.beta.threads.del(params.threadId)
- console.log({ kill })
- return Response.json({})
-}
diff --git a/apps/database-new/app/api/ai/sql/threads/[threadId]/read/[runId]/route.ts b/apps/database-new/app/api/ai/sql/threads/[threadId]/read/[runId]/route.ts
deleted file mode 100644
index d15c0087d08..00000000000
--- a/apps/database-new/app/api/ai/sql/threads/[threadId]/read/[runId]/route.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-// import { parseTables } from '@/lib/utils'
-import { compact } from 'lodash'
-import OpenAI from 'openai'
-
-const openai = new OpenAI()
-
-export async function GET(
- req: Request,
- { params }: { params: { threadId: string; runId: string } }
-) {
- const [run, { data: messages }] = await Promise.all([
- openai.beta.threads.runs.retrieve(params.threadId, params.runId),
- openai.beta.threads.messages.list(params.threadId),
- ])
-
- const mappedMessages = compact(
- await Promise.all(
- messages.map(async (m) => {
- if (m.role === 'user' && m.content[0].type === 'text') {
- return {
- id: m.id,
- role: 'user' as const,
- created_at: m.created_at,
- text: m.content[0].text.value,
- }
- }
-
- if (m.content.length >= 1 && m.content[0].type === 'text') {
- let sql = ''
- if (m.content[0].type === 'text') {
- sql = m.content[0].text.value.replaceAll('\n', '')
- }
- return {
- id: m.id,
- role: 'assistant' as const,
- created_at: m.created_at,
- sql,
- }
- }
- })
- )
- )
- const result = {
- id: params.threadId,
- status: run.status === 'completed' ? 'completed' : 'loading',
- messages: mappedMessages,
- }
-
- return Response.json(result)
-}
diff --git a/apps/database-new/app/api/ai/sql/threads/[threadId]/update/route.ts b/apps/database-new/app/api/ai/sql/threads/[threadId]/update/route.ts
deleted file mode 100644
index 010e0fd6b07..00000000000
--- a/apps/database-new/app/api/ai/sql/threads/[threadId]/update/route.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import OpenAI from 'openai'
-
-const openai = new OpenAI()
-
-export async function POST(req: Request, { params }: { params: { threadId: string } }) {
- if (!req.body) {
- return Response.error()
- }
-
- const { prompt } = await req.json()
-
- const message = await openai.beta.threads.messages.create(params.threadId, {
- content: prompt,
- role: 'user',
- })
-
- const run = await openai.beta.threads.runs.create(message.thread_id, {
- assistant_id: 'asst_oLWrK8lScZVNEpfjwUIvBAnq',
- })
-
- return Response.json({ threadId: message.thread_id, runId: run.id })
-}
diff --git a/apps/database-new/app/api/ai/sql/threads/create/route.ts b/apps/database-new/app/api/ai/sql/threads/create/route.ts
deleted file mode 100644
index 8f12d5476b9..00000000000
--- a/apps/database-new/app/api/ai/sql/threads/create/route.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { cookies } from 'next/headers'
-import { createClient } from '@/lib/supabase/server'
-import OpenAI from 'openai'
-import { revalidatePath } from 'next/cache'
-
-const openai = new OpenAI()
-
-export async function POST(req: Request) {
- const cookieStore = cookies()
- const supabase = createClient(cookieStore)
-
- if (!req.body) {
- return Response.error()
- }
-
- const { prompt, userID } = await req.json()
-
- const thread = await openai.beta.threads.create()
-
- await openai.beta.threads.messages.create(thread.id, {
- role: 'user',
- content: prompt,
- })
-
- const createRun = await openai.beta.threads.runs.create(thread.id, {
- assistant_id: 'asst_oLWrK8lScZVNEpfjwUIvBAnq',
- })
-
- const [run, { data: messages }] = await Promise.all([
- openai.beta.threads.runs.retrieve(thread.id, createRun.id),
- openai.beta.threads.messages.list(thread.id),
- ])
- const threadTitle = messages
- .filter((m) => m.role === 'user' && m.content[0]?.type === 'text')
- .map((m) => {
- if (m.content[0]?.type === 'text') {
- return m.content[0]?.text?.value
- }
- return undefined
- })
- .find((text) => text !== undefined)
-
- try {
- const { error } = await supabase.from('threads').insert({
- thread_id: thread.id,
- run_id: run.id,
- user_id: userID,
- thread_title: threadTitle as string,
- })
- if (error) throw error
- } catch (error) {
- console.error(error)
- }
-
- revalidatePath('/profile')
-
- return Response.json({ threadId: thread.id, runId: run.id })
-}
diff --git a/apps/database-new/app/profile/ConfirmDeleteThreadModal.tsx b/apps/database-new/app/profile/ConfirmDeleteThreadModal.tsx
index b3bc7c3833d..ddfc7c8e937 100644
--- a/apps/database-new/app/profile/ConfirmDeleteThreadModal.tsx
+++ b/apps/database-new/app/profile/ConfirmDeleteThreadModal.tsx
@@ -1,9 +1,9 @@
'use client'
-import { useEffect, createRef } from 'react'
+import { deleteThread } from '@/app/actions'
+import { createRef, useEffect } from 'react'
import { useFormState, useFormStatus } from 'react-dom'
import { Button, Input_Shadcn_, Modal } from 'ui'
-import { deleteThread } from '@/app/actions'
import { ThreadType } from './Threads'
const ConfirmDeleteThreadModal = ({
@@ -53,8 +53,6 @@ const ConfirmDeleteThreadModal = ({
)
}
- console.log('thread', thread)
-
return (
-