Skip to content

Commit ba4c796

Browse files
committed
Fix bugs
1 parent 28ad438 commit ba4c796

3 files changed

Lines changed: 287 additions & 3 deletions

File tree

apps/sim/app/api/copilot/chat/route.ts

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { db } from '@sim/db'
22
import { copilotChats } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
4-
import { and, desc, eq } from 'drizzle-orm'
4+
import { and, desc, eq, sql } from 'drizzle-orm'
55
import { type NextRequest, NextResponse } from 'next/server'
66
import { z } from 'zod'
77
import { getSession } from '@/lib/auth'
@@ -15,6 +15,7 @@ import {
1515
import { COPILOT_REQUEST_MODES } from '@/lib/copilot/models'
1616
import { orchestrateCopilotStream } from '@/lib/copilot/orchestrator'
1717
import { getStreamMeta, readStreamEvents } from '@/lib/copilot/orchestrator/stream/buffer'
18+
import type { OrchestratorResult } from '@/lib/copilot/orchestrator/types'
1819
import {
1920
authenticateCopilotRequestSessionOnly,
2021
createBadRequestResponse,
@@ -285,6 +286,35 @@ export async function POST(req: NextRequest) {
285286
})
286287
} catch {}
287288

289+
if (actualChatId) {
290+
const userMsg = {
291+
id: userMessageIdToUse,
292+
role: 'user' as const,
293+
content: message,
294+
timestamp: new Date().toISOString(),
295+
...(fileAttachments && fileAttachments.length > 0 && { fileAttachments }),
296+
...(Array.isArray(normalizedContexts) &&
297+
normalizedContexts.length > 0 && {
298+
contexts: normalizedContexts,
299+
}),
300+
}
301+
302+
const [updated] = await db
303+
.update(copilotChats)
304+
.set({
305+
messages: sql`${copilotChats.messages} || ${JSON.stringify([userMsg])}::jsonb`,
306+
conversationId: userMessageIdToUse,
307+
updatedAt: new Date(),
308+
})
309+
.where(eq(copilotChats.id, actualChatId))
310+
.returning({ messages: copilotChats.messages })
311+
312+
if (updated) {
313+
const freshMessages: any[] = Array.isArray(updated.messages) ? updated.messages : []
314+
conversationHistory = freshMessages.filter((m: any) => m.id !== userMessageIdToUse)
315+
}
316+
}
317+
288318
if (stream) {
289319
const sseStream = createSSEStream({
290320
requestPayload,
@@ -297,6 +327,7 @@ export async function POST(req: NextRequest) {
297327
titleModel: selectedModel,
298328
titleProvider: provider,
299329
requestId: tracker.requestId,
330+
workspaceId: resolvedWorkspaceId,
300331
orchestrateOptions: {
301332
userId: authenticatedUserId,
302333
workflowId,
@@ -305,6 +336,72 @@ export async function POST(req: NextRequest) {
305336
autoExecuteTools: true,
306337
interactive: true,
307338
promptForToolApproval: true,
339+
onComplete: async (result: OrchestratorResult) => {
340+
if (!actualChatId) return
341+
342+
const assistantMessage: Record<string, unknown> = {
343+
id: crypto.randomUUID(),
344+
role: 'assistant' as const,
345+
content: result.content,
346+
timestamp: new Date().toISOString(),
347+
...(result.requestId ? { requestId: result.requestId } : {}),
348+
}
349+
if (result.toolCalls.length > 0) {
350+
assistantMessage.toolCalls = result.toolCalls
351+
}
352+
if (result.contentBlocks.length > 0) {
353+
assistantMessage.contentBlocks = result.contentBlocks.map((block) => {
354+
const stored: Record<string, unknown> = { type: block.type }
355+
if (block.content) stored.content = block.content
356+
if (block.type === 'tool_call' && block.toolCall) {
357+
stored.toolCall = {
358+
id: block.toolCall.id,
359+
name: block.toolCall.name,
360+
state:
361+
block.toolCall.result?.success !== undefined
362+
? block.toolCall.result.success
363+
? 'success'
364+
: 'error'
365+
: block.toolCall.status,
366+
result: block.toolCall.result,
367+
...(block.calledBy ? { calledBy: block.calledBy } : {}),
368+
}
369+
}
370+
return stored
371+
})
372+
}
373+
374+
try {
375+
const [row] = await db
376+
.select({ messages: copilotChats.messages })
377+
.from(copilotChats)
378+
.where(eq(copilotChats.id, actualChatId))
379+
.limit(1)
380+
381+
const msgs: any[] = Array.isArray(row?.messages) ? row.messages : []
382+
const userIdx = msgs.findIndex((m: any) => m.id === userMessageIdToUse)
383+
const alreadyHasResponse =
384+
userIdx >= 0 &&
385+
userIdx + 1 < msgs.length &&
386+
(msgs[userIdx + 1] as any)?.role === 'assistant'
387+
388+
if (!alreadyHasResponse) {
389+
await db
390+
.update(copilotChats)
391+
.set({
392+
messages: sql`${copilotChats.messages} || ${JSON.stringify([assistantMessage])}::jsonb`,
393+
conversationId: sql`CASE WHEN ${copilotChats.conversationId} = ${userMessageIdToUse} THEN NULL ELSE ${copilotChats.conversationId} END`,
394+
updatedAt: new Date(),
395+
})
396+
.where(eq(copilotChats.id, actualChatId))
397+
}
398+
} catch (error) {
399+
logger.error(`[${tracker.requestId}] Failed to persist chat messages`, {
400+
chatId: actualChatId,
401+
error: error instanceof Error ? error.message : 'Unknown error',
402+
})
403+
}
404+
},
308405
},
309406
})
310407

apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ export interface UseChatOptions {
253253
stopPath?: string
254254
workflowId?: string
255255
onToolResult?: (toolName: string, success: boolean, result: unknown) => void
256+
onTitleUpdate?: () => void
257+
onStreamEnd?: (chatId: string, messages: ChatMessage[]) => void
256258
}
257259

258260
export function useChat(
@@ -279,6 +281,10 @@ export function useChat(
279281
workflowIdRef.current = options?.workflowId
280282
const onToolResultRef = useRef(options?.onToolResult)
281283
onToolResultRef.current = options?.onToolResult
284+
const onTitleUpdateRef = useRef(options?.onTitleUpdate)
285+
onTitleUpdateRef.current = options?.onTitleUpdate
286+
const onStreamEndRef = useRef(options?.onStreamEnd)
287+
onStreamEndRef.current = options?.onStreamEnd
282288
const resourcesRef = useRef(resources)
283289
resourcesRef.current = resources
284290
const activeResourceIdRef = useRef(activeResourceId)
@@ -876,6 +882,7 @@ export function useChat(
876882
queryClient.invalidateQueries({
877883
queryKey: taskKeys.list(workspaceId),
878884
})
885+
onTitleUpdateRef.current?.()
879886
break
880887
}
881888
case 'error': {
@@ -955,13 +962,23 @@ export function useChat(
955962
queryClient.invalidateQueries({ queryKey: taskKeys.list(workspaceId) })
956963
}, [workspaceId, queryClient])
957964

965+
const messagesRef = useRef(messages)
966+
messagesRef.current = messages
967+
958968
const finalize = useCallback(
959969
(options?: { error?: boolean }) => {
960970
sendingRef.current = false
961971
setIsSending(false)
962972
abortControllerRef.current = null
963973
invalidateChatQueries()
964974

975+
if (!options?.error) {
976+
const cid = chatIdRef.current
977+
if (cid && onStreamEndRef.current) {
978+
onStreamEndRef.current(cid, messagesRef.current)
979+
}
980+
}
981+
965982
if (options?.error) {
966983
setMessageQueue([])
967984
return

0 commit comments

Comments
 (0)