Convo update

This commit is contained in:
Siddharth Ganesan
2025-07-08 17:20:05 -07:00
parent d75751bbe6
commit 3460a7b39e
8 changed files with 247 additions and 448 deletions

View File

@@ -1,12 +1,12 @@
import { eq, and } from 'drizzle-orm'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console-logger'
import { getRotatingApiKey } from '@/lib/utils'
import { db } from '@/db'
import { copilotChats } from '@/db/schema'
import { executeProviderRequest } from '@/providers'
import { getRotatingApiKey } from '@/lib/utils'
const logger = createLogger('CopilotChatAPI')
@@ -43,12 +43,7 @@ export async function GET(req: NextRequest, { params }: { params: { id: string }
const [chat] = await db
.select()
.from(copilotChats)
.where(
and(
eq(copilotChats.id, chatId),
eq(copilotChats.userId, session.user.id)
)
)
.where(and(eq(copilotChats.id, chatId), eq(copilotChats.userId, session.user.id)))
.limit(1)
if (!chat) {
@@ -94,12 +89,7 @@ export async function PUT(req: NextRequest, { params }: { params: { id: string }
const [existingChat] = await db
.select()
.from(copilotChats)
.where(
and(
eq(copilotChats.id, chatId),
eq(copilotChats.userId, session.user.id)
)
)
.where(and(eq(copilotChats.id, chatId), eq(copilotChats.userId, session.user.id)))
.limit(1)
if (!existingChat) {
@@ -172,12 +162,7 @@ export async function DELETE(req: NextRequest, { params }: { params: { id: strin
const [existingChat] = await db
.select({ id: copilotChats.id })
.from(copilotChats)
.where(
and(
eq(copilotChats.id, chatId),
eq(copilotChats.userId, session.user.id)
)
)
.where(and(eq(copilotChats.id, chatId), eq(copilotChats.userId, session.user.id)))
.limit(1)
if (!existingChat) {
@@ -185,9 +170,7 @@ export async function DELETE(req: NextRequest, { params }: { params: { id: strin
}
// Delete the chat
await db
.delete(copilotChats)
.where(eq(copilotChats.id, chatId))
await db.delete(copilotChats).where(eq(copilotChats.id, chatId))
logger.info(`Deleted chat ${chatId} for user ${session.user.id}`)
@@ -207,10 +190,11 @@ export async function DELETE(req: NextRequest, { params }: { params: { id: strin
export async function generateChatTitle(userMessage: string): Promise<string> {
try {
const apiKey = getRotatingApiKey('anthropic')
const response = await executeProviderRequest('anthropic', {
model: 'claude-3-haiku-20240307', // Use faster, cheaper model for title generation
systemPrompt: 'You are a helpful assistant that generates concise, descriptive titles for chat conversations. Create a title that captures the main topic or question being discussed. Keep it under 50 characters and make it specific and clear.',
systemPrompt:
'You are a helpful assistant that generates concise, descriptive titles for chat conversations. Create a title that captures the main topic or question being discussed. Keep it under 50 characters and make it specific and clear.',
context: `Generate a concise title for a conversation that starts with this user message: "${userMessage}"
Return only the title text, nothing else.`,
@@ -230,4 +214,4 @@ Return only the title text, nothing else.`,
logger.error('Failed to generate chat title:', error)
return 'New Chat' // Fallback title
}
}
}

View File

@@ -1,4 +1,4 @@
import { desc, eq, and } from 'drizzle-orm'
import { and, desc, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
@@ -34,11 +34,14 @@ export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url)
const workflowId = searchParams.get('workflowId')
const limit = parseInt(searchParams.get('limit') || '50')
const offset = parseInt(searchParams.get('offset') || '0')
const limit = Number.parseInt(searchParams.get('limit') || '50')
const offset = Number.parseInt(searchParams.get('offset') || '0')
const { workflowId: validatedWorkflowId, limit: validatedLimit, offset: validatedOffset } =
ListChatsSchema.parse({ workflowId, limit, offset })
const {
workflowId: validatedWorkflowId,
limit: validatedLimit,
offset: validatedOffset,
} = ListChatsSchema.parse({ workflowId, limit, offset })
logger.info(`Listing chats for user ${session.user.id}, workflow ${validatedWorkflowId}`)
@@ -63,7 +66,7 @@ export async function GET(req: NextRequest) {
.offset(validatedOffset)
// Process the results to add message counts and clean up data
const processedChats = chats.map(chat => ({
const processedChats = chats.map((chat) => ({
id: chat.id,
title: chat.title,
model: chat.model,
@@ -170,4 +173,4 @@ export async function POST(req: NextRequest) {
logger.error('Failed to create copilot chat:', error)
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
}
}
}

View File

@@ -1,13 +1,13 @@
import { sql, eq, and } from 'drizzle-orm'
import { and, eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console-logger'
import { getRotatingApiKey } from '@/lib/utils'
import { generateEmbeddings } from '@/app/api/knowledge/utils'
import { getSession } from '@/lib/auth'
import { db } from '@/db'
import { docsEmbeddings, copilotChats } from '@/db/schema'
import { copilotChats, docsEmbeddings } from '@/db/schema'
import { executeProviderRequest } from '@/providers'
import { getProviderDefaultModel } from '@/providers/models'
@@ -43,10 +43,11 @@ const DocsQuerySchema = z.object({
async function generateChatTitle(userMessage: string): Promise<string> {
try {
const apiKey = getRotatingApiKey('anthropic')
const response = await executeProviderRequest('anthropic', {
model: 'claude-3-haiku-20240307', // Use faster, cheaper model for title generation
systemPrompt: 'You are a helpful assistant that generates concise, descriptive titles for chat conversations. Create a title that captures the main topic or question being discussed. Keep it under 50 characters and make it specific and clear.',
systemPrompt:
'You are a helpful assistant that generates concise, descriptive titles for chat conversations. Create a title that captures the main topic or question being discussed. Keep it under 50 characters and make it specific and clear.',
context: `Generate a concise title for a conversation that starts with this user message: "${userMessage}"
Return only the title text, nothing else.`,
@@ -171,7 +172,8 @@ Content: ${chunkText}`
let conversationContext = ''
if (conversationHistory.length > 0) {
conversationContext = '\n\nConversation History:\n'
conversationHistory.slice(-6).forEach((msg: any) => { // Include last 6 messages for context
conversationHistory.slice(-6).forEach((msg: any) => {
// Include last 6 messages for context
const role = msg.role === 'user' ? 'Human' : 'Assistant'
conversationContext += `${role}: ${msg.content}\n`
})
@@ -270,11 +272,12 @@ export async function POST(req: NextRequest) {
try {
const body = await req.json()
const { query, topK, provider, model, stream, chatId, workflowId, createNewChat } = DocsQuerySchema.parse(body)
const { query, topK, provider, model, stream, chatId, workflowId, createNewChat } =
DocsQuerySchema.parse(body)
// Get session for chat functionality
const session = await getSession()
logger.info(`[${requestId}] Docs RAG query: "${query}"`, {
provider: provider || DOCS_RAG_CONFIG.defaultProvider,
model:
@@ -296,12 +299,7 @@ export async function POST(req: NextRequest) {
const [existingChat] = await db
.select()
.from(copilotChats)
.where(
and(
eq(copilotChats.id, chatId),
eq(copilotChats.userId, session.user.id)
)
)
.where(and(eq(copilotChats.id, chatId), eq(copilotChats.userId, session.user.id)))
.limit(1)
if (existingChat) {
@@ -360,7 +358,14 @@ export async function POST(req: NextRequest) {
// Step 3: Generate response using LLM
logger.info(`[${requestId}] Generating LLM response with ${chunks.length} chunks...`)
const response = await generateResponse(query, chunks, provider, model, stream, conversationHistory)
const response = await generateResponse(
query,
chunks,
provider,
model,
stream,
conversationHistory
)
// Step 4: Format sources for response
const sources = chunks.map((chunk) => ({
@@ -403,7 +408,7 @@ export async function POST(req: NextRequest) {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(metadata)}\n\n`))
let accumulatedResponse = ''
try {
while (true) {
const { done, value } = await reader.read()
@@ -413,10 +418,10 @@ export async function POST(req: NextRequest) {
const chunkText = decoder.decode(value)
// Clean up any object serialization artifacts in streaming content
const cleanedChunk = chunkText.replace(/\[object Object\],?/g, '')
// Accumulate the response content for database saving
accumulatedResponse += cleanedChunk
const contentChunk = {
type: 'content',
content: cleanedChunk,

View File

@@ -1,28 +1,36 @@
'use client'
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
import { Bot, ChevronDown, Loader2, MessageSquarePlus, MoreHorizontal, Send, Trash2, User } from 'lucide-react'
import {
Bot,
ChevronDown,
Loader2,
MessageSquarePlus,
MoreHorizontal,
Send,
Trash2,
User,
} from 'lucide-react'
import { Button } from '@/components/ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Input } from '@/components/ui/input'
import { ScrollArea } from '@/components/ui/scroll-area'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import {
type CopilotChat,
type CopilotMessage,
deleteChat,
getChat,
listChats,
sendStreamingMessage,
} from '@/lib/copilot-api'
import { createLogger } from '@/lib/logs/console-logger'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { CopilotModal } from './components/copilot-modal/copilot-modal'
import {
listChats,
getChat,
deleteChat,
sendStreamingMessage,
type CopilotChat,
type CopilotMessage
} from '@/lib/copilot-api'
const logger = createLogger('Copilot')
@@ -58,7 +66,7 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
const [loadingChats, setLoadingChats] = useState(false)
const scrollAreaRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null)
const { activeWorkflowId } = useWorkflowRegistry()
// Load chats when workflow changes
@@ -127,22 +135,25 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
}, [])
// Delete a chat
const handleDeleteChat = useCallback(async (chatId: string) => {
try {
const result = await deleteChat(chatId)
if (result.success) {
setChats(prev => prev.filter(chat => chat.id !== chatId))
if (currentChat?.id === chatId) {
startNewChat()
const handleDeleteChat = useCallback(
async (chatId: string) => {
try {
const result = await deleteChat(chatId)
if (result.success) {
setChats((prev) => prev.filter((chat) => chat.id !== chatId))
if (currentChat?.id === chatId) {
startNewChat()
}
logger.info('Chat deleted successfully')
} else {
logger.error('Failed to delete chat:', result.error)
}
logger.info('Chat deleted successfully')
} else {
logger.error('Failed to delete chat:', result.error)
} catch (error) {
logger.error('Error deleting chat:', error)
}
} catch (error) {
logger.error('Error deleting chat:', error)
}
}, [currentChat, startNewChat])
},
[currentChat, startNewChat]
)
// Expose functions to parent
useImperativeHandle(
@@ -225,14 +236,14 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
setMessages((prev) =>
prev.map((msg) =>
msg.id === streamingMessage.id
? {
...msg,
? {
...msg,
content: accumulatedContent,
citations: sources.map((source: any, index: number) => ({
id: index + 1,
title: source.title,
url: source.link,
}))
})),
}
: msg
)
@@ -242,13 +253,13 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
setMessages((prev) =>
prev.map((msg) =>
msg.id === streamingMessage.id
? {
? {
...msg,
citations: sources.map((source: any, index: number) => ({
id: index + 1,
title: source.title,
url: source.link,
}))
})),
}
: msg
)
@@ -291,7 +302,8 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
const errorMessage: CopilotMessage = {
id: streamingMessage.id,
role: 'assistant',
content: 'Sorry, I encountered an error while searching the documentation. Please try again.',
content:
'Sorry, I encountered an error while searching the documentation. Please try again.',
timestamp: new Date().toISOString(),
}
@@ -309,7 +321,10 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
}
// Function to render content with inline hyperlinked citations and basic markdown
const renderContentWithCitations = (content: string, citations: CopilotMessage['citations'] = []) => {
const renderContentWithCitations = (
content: string,
citations: CopilotMessage['citations'] = []
) => {
if (!content) return content
let processedContent = content
@@ -423,12 +438,15 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
}))
// Handle modal message sending
const handleModalSendMessage = useCallback(async (message: string) => {
// Create form event and call the main handler
const mockEvent = { preventDefault: () => {} } as React.FormEvent
setInput(message)
await handleSubmit(mockEvent)
}, [handleSubmit])
const handleModalSendMessage = useCallback(
async (message: string) => {
// Create form event and call the main handler
const mockEvent = { preventDefault: () => {} } as React.FormEvent
setInput(message)
await handleSubmit(mockEvent)
},
[handleSubmit]
)
return (
<>
@@ -443,25 +461,20 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
<p className='text-muted-foreground text-xs'>Ask questions about Sim Studio</p>
</div>
</div>
{/* Chat Management */}
<div className='flex items-center gap-2'>
<Button
variant='outline'
size='sm'
onClick={startNewChat}
className='h-8'
>
<MessageSquarePlus className='h-4 w-4 mr-2' />
<Button variant='outline' size='sm' onClick={startNewChat} className='h-8'>
<MessageSquarePlus className='mr-2 h-4 w-4' />
New Chat
</Button>
{chats.length > 0 && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='outline' size='sm' className='h-8'>
{currentChat?.title || 'Select Chat'}
<ChevronDown className='h-4 w-4 ml-2' />
<ChevronDown className='ml-2 h-4 w-4' />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align='end' className='w-64'>
@@ -471,31 +484,28 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(
onClick={() => selectChat(chat)}
className='flex-1 cursor-pointer'
>
<div className='flex-1 min-w-0'>
<div className='font-medium text-sm truncate'>
<div className='min-w-0 flex-1'>
<div className='truncate font-medium text-sm'>
{chat.title || 'Untitled Chat'}
</div>
<div className='text-muted-foreground text-xs'>
{chat.messageCount} messages {new Date(chat.updatedAt).toLocaleDateString()}
{chat.messageCount} messages {' '}
{new Date(chat.updatedAt).toLocaleDateString()}
</div>
</div>
</DropdownMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant='ghost'
size='sm'
className='h-8 w-8 p-0 shrink-0'
>
<Button variant='ghost' size='sm' className='h-8 w-8 shrink-0 p-0'>
<MoreHorizontal className='h-4 w-4' />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align='end'>
<DropdownMenuItem
onClick={() => handleDeleteChat(chat.id)}
className='text-destructive cursor-pointer'
className='cursor-pointer text-destructive'
>
<Trash2 className='h-4 w-4 mr-2' />
<Trash2 className='mr-2 h-4 w-4' />
Delete
</DropdownMenuItem>
</DropdownMenuContent>

View File

@@ -93,12 +93,8 @@
"name": "account_user_id_user_id_fk",
"tableFrom": "account",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -170,12 +166,8 @@
"name": "api_key_user_id_user_id_fk",
"tableFrom": "api_key",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -185,9 +177,7 @@
"api_key_key_unique": {
"name": "api_key_key_unique",
"nullsNotDistinct": false,
"columns": [
"key"
]
"columns": ["key"]
}
},
"policies": {},
@@ -312,12 +302,8 @@
"name": "chat_workflow_id_workflow_id_fk",
"tableFrom": "chat",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -325,12 +311,8 @@
"name": "chat_user_id_user_id_fk",
"tableFrom": "chat",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -487,12 +469,8 @@
"name": "copilot_chats_user_id_user_id_fk",
"tableFrom": "copilot_chats",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -500,12 +478,8 @@
"name": "copilot_chats_workflow_id_workflow_id_fk",
"tableFrom": "copilot_chats",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -571,12 +545,8 @@
"name": "custom_tools_user_id_user_id_fk",
"tableFrom": "custom_tools",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -1013,12 +983,8 @@
"name": "document_knowledge_base_id_knowledge_base_id_fk",
"tableFrom": "document",
"tableTo": "knowledge_base",
"columnsFrom": [
"knowledge_base_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["knowledge_base_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -1315,12 +1281,8 @@
"name": "embedding_knowledge_base_id_knowledge_base_id_fk",
"tableFrom": "embedding",
"tableTo": "knowledge_base",
"columnsFrom": [
"knowledge_base_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["knowledge_base_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -1328,12 +1290,8 @@
"name": "embedding_document_id_document_id_fk",
"tableFrom": "embedding",
"tableTo": "document",
"columnsFrom": [
"document_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["document_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -1385,12 +1343,8 @@
"name": "environment_user_id_user_id_fk",
"tableFrom": "environment",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -1400,9 +1354,7 @@
"environment_user_id_unique": {
"name": "environment_user_id_unique",
"nullsNotDistinct": false,
"columns": [
"user_id"
]
"columns": ["user_id"]
}
},
"policies": {},
@@ -1469,12 +1421,8 @@
"name": "invitation_inviter_id_user_id_fk",
"tableFrom": "invitation",
"tableTo": "user",
"columnsFrom": [
"inviter_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["inviter_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -1482,12 +1430,8 @@
"name": "invitation_organization_id_organization_id_fk",
"tableFrom": "invitation",
"tableTo": "organization",
"columnsFrom": [
"organization_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["organization_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -1654,12 +1598,8 @@
"name": "knowledge_base_user_id_user_id_fk",
"tableFrom": "knowledge_base",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -1667,12 +1607,8 @@
"name": "knowledge_base_workspace_id_workspace_id_fk",
"tableFrom": "knowledge_base",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workspace_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -1763,12 +1699,8 @@
"name": "marketplace_workflow_id_workflow_id_fk",
"tableFrom": "marketplace",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -1776,12 +1708,8 @@
"name": "marketplace_author_id_user_id_fk",
"tableFrom": "marketplace",
"tableTo": "user",
"columnsFrom": [
"author_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["author_id"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
@@ -1834,12 +1762,8 @@
"name": "member_user_id_user_id_fk",
"tableFrom": "member",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -1847,12 +1771,8 @@
"name": "member_organization_id_organization_id_fk",
"tableFrom": "member",
"tableTo": "organization",
"columnsFrom": [
"organization_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["organization_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -1976,12 +1896,8 @@
"name": "memory_workflow_id_workflow_id_fk",
"tableFrom": "memory",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -2244,12 +2160,8 @@
"name": "permissions_user_id_user_id_fk",
"tableFrom": "permissions",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -2325,12 +2237,8 @@
"name": "session_user_id_user_id_fk",
"tableFrom": "session",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -2338,12 +2246,8 @@
"name": "session_active_organization_id_organization_id_fk",
"tableFrom": "session",
"tableTo": "organization",
"columnsFrom": [
"active_organization_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["active_organization_id"],
"columnsTo": ["id"],
"onDelete": "set null",
"onUpdate": "no action"
}
@@ -2353,9 +2257,7 @@
"session_token_unique": {
"name": "session_token_unique",
"nullsNotDistinct": false,
"columns": [
"token"
]
"columns": ["token"]
}
},
"policies": {},
@@ -2455,12 +2357,8 @@
"name": "settings_user_id_user_id_fk",
"tableFrom": "settings",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -2470,9 +2368,7 @@
"settings_user_id_unique": {
"name": "settings_user_id_unique",
"nullsNotDistinct": false,
"columns": [
"user_id"
]
"columns": ["user_id"]
}
},
"policies": {},
@@ -2657,9 +2553,7 @@
"user_email_unique": {
"name": "user_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
"columns": ["email"]
}
},
"policies": {},
@@ -2792,12 +2686,8 @@
"name": "user_stats_user_id_user_id_fk",
"tableFrom": "user_stats",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -2807,9 +2697,7 @@
"user_stats_user_id_unique": {
"name": "user_stats_user_id_unique",
"nullsNotDistinct": false,
"columns": [
"user_id"
]
"columns": ["user_id"]
}
},
"policies": {},
@@ -2910,9 +2798,7 @@
"waitlist_email_unique": {
"name": "waitlist_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
"columns": ["email"]
}
},
"policies": {},
@@ -2997,12 +2883,8 @@
"name": "webhook_workflow_id_workflow_id_fk",
"tableFrom": "webhook",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -3150,12 +3032,8 @@
"name": "workflow_user_id_user_id_fk",
"tableFrom": "workflow",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -3163,12 +3041,8 @@
"name": "workflow_workspace_id_workspace_id_fk",
"tableFrom": "workflow",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workspace_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -3176,12 +3050,8 @@
"name": "workflow_folder_id_workflow_folder_id_fk",
"tableFrom": "workflow",
"tableTo": "workflow_folder",
"columnsFrom": [
"folder_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["folder_id"],
"columnsTo": ["id"],
"onDelete": "set null",
"onUpdate": "no action"
}
@@ -3394,12 +3264,8 @@
"name": "workflow_blocks_workflow_id_workflow_id_fk",
"tableFrom": "workflow_blocks",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -3552,12 +3418,8 @@
"name": "workflow_edges_workflow_id_workflow_id_fk",
"tableFrom": "workflow_edges",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -3565,12 +3427,8 @@
"name": "workflow_edges_source_block_id_workflow_blocks_id_fk",
"tableFrom": "workflow_edges",
"tableTo": "workflow_blocks",
"columnsFrom": [
"source_block_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["source_block_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -3578,12 +3436,8 @@
"name": "workflow_edges_target_block_id_workflow_blocks_id_fk",
"tableFrom": "workflow_edges",
"tableTo": "workflow_blocks",
"columnsFrom": [
"target_block_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["target_block_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -3892,12 +3746,8 @@
"name": "workflow_execution_blocks_workflow_id_workflow_id_fk",
"tableFrom": "workflow_execution_blocks",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4166,12 +4016,8 @@
"name": "workflow_execution_logs_workflow_id_workflow_id_fk",
"tableFrom": "workflow_execution_logs",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -4179,12 +4025,8 @@
"name": "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk",
"tableFrom": "workflow_execution_logs",
"tableTo": "workflow_execution_snapshots",
"columnsFrom": [
"state_snapshot_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["state_snapshot_id"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
@@ -4304,12 +4146,8 @@
"name": "workflow_execution_snapshots_workflow_id_workflow_id_fk",
"tableFrom": "workflow_execution_snapshots",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4454,12 +4292,8 @@
"name": "workflow_folder_user_id_user_id_fk",
"tableFrom": "workflow_folder",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -4467,12 +4301,8 @@
"name": "workflow_folder_workspace_id_workspace_id_fk",
"tableFrom": "workflow_folder",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workspace_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4549,12 +4379,8 @@
"name": "workflow_logs_workflow_id_workflow_id_fk",
"tableFrom": "workflow_logs",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4653,12 +4479,8 @@
"name": "workflow_schedule_workflow_id_workflow_id_fk",
"tableFrom": "workflow_schedule",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4668,9 +4490,7 @@
"workflow_schedule_workflow_id_unique": {
"name": "workflow_schedule_workflow_id_unique",
"nullsNotDistinct": false,
"columns": [
"workflow_id"
]
"columns": ["workflow_id"]
}
},
"policies": {},
@@ -4764,12 +4584,8 @@
"name": "workflow_subflows_workflow_id_workflow_id_fk",
"tableFrom": "workflow_subflows",
"tableTo": "workflow",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workflow_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4823,12 +4639,8 @@
"name": "workspace_owner_id_user_id_fk",
"tableFrom": "workspace",
"tableTo": "user",
"columnsFrom": [
"owner_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["owner_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4922,12 +4734,8 @@
"name": "workspace_invitation_workspace_id_workspace_id_fk",
"tableFrom": "workspace_invitation",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workspace_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -4935,12 +4743,8 @@
"name": "workspace_invitation_inviter_id_user_id_fk",
"tableFrom": "workspace_invitation",
"tableTo": "user",
"columnsFrom": [
"inviter_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["inviter_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -4950,9 +4754,7 @@
"workspace_invitation_token_unique": {
"name": "workspace_invitation_token_unique",
"nullsNotDistinct": false,
"columns": [
"token"
]
"columns": ["token"]
}
},
"policies": {},
@@ -5031,12 +4833,8 @@
"name": "workspace_member_workspace_id_workspace_id_fk",
"tableFrom": "workspace_member",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["workspace_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
@@ -5044,12 +4842,8 @@
"name": "workspace_member_user_id_user_id_fk",
"tableFrom": "workspace_member",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
@@ -5065,11 +4859,7 @@
"public.permission_type": {
"name": "permission_type",
"schema": "public",
"values": [
"admin",
"write",
"read"
]
"values": ["admin", "write", "read"]
}
},
"schemas": {},
@@ -5082,4 +4872,4 @@
"schemas": {},
"tables": {}
}
}
}

View File

@@ -367,4 +367,4 @@
"breakpoints": true
}
]
}
}

View File

@@ -1001,7 +1001,7 @@ export const copilotChats = pgTable(
userIdIdx: index('copilot_chats_user_id_idx').on(table.userId),
workflowIdIdx: index('copilot_chats_workflow_id_idx').on(table.workflowId),
userWorkflowIdx: index('copilot_chats_user_workflow_idx').on(table.userId, table.workflowId),
// Ordering indexes
createdAtIdx: index('copilot_chats_created_at_idx').on(table.createdAt),
updatedAtIdx: index('copilot_chats_updated_at_idx').on(table.updatedAt),

View File

@@ -51,10 +51,14 @@ export interface DocsQueryRequest {
/**
* List chats for a specific workflow
*/
export async function listChats(workflowId: string, limit = 50, offset = 0): Promise<{
success: boolean
export async function listChats(
workflowId: string,
limit = 50,
offset = 0
): Promise<{
success: boolean
chats: CopilotChat[]
error?: string
error?: string
}> {
try {
const params = new URLSearchParams({
@@ -87,10 +91,10 @@ export async function listChats(workflowId: string, limit = 50, offset = 0): Pro
/**
* Create a new chat
*/
export async function createChat(request: CreateChatRequest): Promise<{
success: boolean
export async function createChat(request: CreateChatRequest): Promise<{
success: boolean
chat?: CopilotChat
error?: string
error?: string
}> {
try {
const response = await fetch('/api/copilot/chats', {
@@ -121,10 +125,10 @@ export async function createChat(request: CreateChatRequest): Promise<{
/**
* Get a specific chat with full message history
*/
export async function getChat(chatId: string): Promise<{
success: boolean
export async function getChat(chatId: string): Promise<{
success: boolean
chat?: CopilotChat
error?: string
error?: string
}> {
try {
const response = await fetch(`/api/copilot/chats/${chatId}`)
@@ -150,10 +154,13 @@ export async function getChat(chatId: string): Promise<{
/**
* Update a chat
*/
export async function updateChat(chatId: string, request: UpdateChatRequest): Promise<{
success: boolean
export async function updateChat(
chatId: string,
request: UpdateChatRequest
): Promise<{
success: boolean
chat?: CopilotChat
error?: string
error?: string
}> {
try {
const response = await fetch(`/api/copilot/chats/${chatId}`, {
@@ -184,9 +191,9 @@ export async function updateChat(chatId: string, request: UpdateChatRequest): Pr
/**
* Delete a chat
*/
export async function deleteChat(chatId: string): Promise<{
success: boolean
error?: string
export async function deleteChat(chatId: string): Promise<{
success: boolean
error?: string
}> {
try {
const response = await fetch(`/api/copilot/chats/${chatId}`, {
@@ -290,4 +297,4 @@ export async function sendStreamingMessage(request: DocsQueryRequest): Promise<{
error: error instanceof Error ? error.message : 'Unknown error',
}
}
}
}