Skip to content

Commit 12135d2

Browse files
authored
improvement(copilot): improve context inputs and fix some bugs (#1216)
* Add logs v1 * Update * Updates * Updates * Fixes * Fix current workflow in context * Fix mentions * Error handling * Fix chat loading * Hide current workflow from context * Run workflow fix * Lint
1 parent f75c807 commit 12135d2

File tree

9 files changed

+1179
-79
lines changed

9 files changed

+1179
-79
lines changed

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

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,25 @@ const ChatMessageSchema = z.object({
5050
contexts: z
5151
.array(
5252
z.object({
53-
kind: z.enum(['past_chat', 'workflow', 'blocks', 'logs', 'knowledge', 'templates']),
53+
kind: z.enum([
54+
'past_chat',
55+
'workflow',
56+
'current_workflow',
57+
'blocks',
58+
'logs',
59+
'workflow_block',
60+
'knowledge',
61+
'templates',
62+
'docs',
63+
]),
5464
label: z.string(),
5565
chatId: z.string().optional(),
5666
workflowId: z.string().optional(),
5767
knowledgeId: z.string().optional(),
5868
blockId: z.string().optional(),
5969
templateId: z.string().optional(),
70+
executionId: z.string().optional(),
71+
// For workflow_block, provide both workflowId and blockId
6072
})
6173
)
6274
.optional(),
@@ -105,6 +117,7 @@ export async function POST(req: NextRequest) {
105117
kind: c?.kind,
106118
chatId: c?.chatId,
107119
workflowId: c?.workflowId,
120+
executionId: (c as any)?.executionId,
108121
label: c?.label,
109122
}))
110123
: undefined,
@@ -115,13 +128,18 @@ export async function POST(req: NextRequest) {
115128
if (Array.isArray(contexts) && contexts.length > 0) {
116129
try {
117130
const { processContextsServer } = await import('@/lib/copilot/process-contents')
118-
const processed = await processContextsServer(contexts as any, authenticatedUserId)
131+
const processed = await processContextsServer(contexts as any, authenticatedUserId, message)
119132
agentContexts = processed
120133
logger.info(`[${tracker.requestId}] Contexts processed for request`, {
121134
processedCount: agentContexts.length,
122135
kinds: agentContexts.map((c) => c.type),
123136
lengthPreview: agentContexts.map((c) => c.content?.length ?? 0),
124137
})
138+
if (Array.isArray(contexts) && contexts.length > 0 && agentContexts.length === 0) {
139+
logger.warn(
140+
`[${tracker.requestId}] Contexts provided but none processed. Check executionId for logs contexts.`
141+
)
142+
}
125143
} catch (e) {
126144
logger.error(`[${tracker.requestId}] Failed to process contexts`, e)
127145
}
@@ -474,16 +492,6 @@ export async function POST(req: NextRequest) {
474492
break
475493
}
476494

477-
// Check if client disconnected before processing chunk
478-
try {
479-
// Forward the chunk to client immediately
480-
controller.enqueue(value)
481-
} catch (error) {
482-
// Client disconnected - stop reading from sim agent
483-
reader.cancel() // Stop reading from sim agent
484-
break
485-
}
486-
487495
// Decode and parse SSE events for logging and capturing content
488496
const decodedChunk = decoder.decode(value, { stream: true })
489497
buffer += decodedChunk
@@ -583,6 +591,47 @@ export async function POST(req: NextRequest) {
583591

584592
default:
585593
}
594+
595+
// Emit to client: rewrite 'error' events into user-friendly assistant message
596+
if (event?.type === 'error') {
597+
try {
598+
const displayMessage: string =
599+
(event?.data && (event.data.displayMessage as string)) ||
600+
'Sorry, I encountered an error. Please try again.'
601+
const formatted = `_${displayMessage}_`
602+
// Accumulate so it persists to DB as assistant content
603+
assistantContent += formatted
604+
// Send as content chunk
605+
try {
606+
controller.enqueue(
607+
encoder.encode(
608+
`data: ${JSON.stringify({ type: 'content', data: formatted })}\n\n`
609+
)
610+
)
611+
} catch (enqueueErr) {
612+
reader.cancel()
613+
break
614+
}
615+
// Then close this response cleanly for the client
616+
try {
617+
controller.enqueue(
618+
encoder.encode(`data: ${JSON.stringify({ type: 'done' })}\n\n`)
619+
)
620+
} catch (enqueueErr) {
621+
reader.cancel()
622+
break
623+
}
624+
} catch {}
625+
// Do not forward the original error event
626+
} else {
627+
// Forward original event to client
628+
try {
629+
controller.enqueue(encoder.encode(`data: ${jsonStr}\n\n`))
630+
} catch (enqueueErr) {
631+
reader.cancel()
632+
break
633+
}
634+
}
586635
} catch (e) {
587636
// Enhanced error handling for large payloads and parsing issues
588637
const lineLength = line.length
@@ -615,10 +664,37 @@ export async function POST(req: NextRequest) {
615664
logger.debug(`[${tracker.requestId}] Processing remaining buffer: "${buffer}"`)
616665
if (buffer.startsWith('data: ')) {
617666
try {
618-
const event = JSON.parse(buffer.slice(6))
667+
const jsonStr = buffer.slice(6)
668+
const event = JSON.parse(jsonStr)
619669
if (event.type === 'content' && event.data) {
620670
assistantContent += event.data
621671
}
672+
// Forward remaining event, applying same error rewrite behavior
673+
if (event?.type === 'error') {
674+
const displayMessage: string =
675+
(event?.data && (event.data.displayMessage as string)) ||
676+
'Sorry, I encountered an error. Please try again.'
677+
const formatted = `_${displayMessage}_`
678+
assistantContent += formatted
679+
try {
680+
controller.enqueue(
681+
encoder.encode(
682+
`data: ${JSON.stringify({ type: 'content', data: formatted })}\n\n`
683+
)
684+
)
685+
controller.enqueue(
686+
encoder.encode(`data: ${JSON.stringify({ type: 'done' })}\n\n`)
687+
)
688+
} catch (enqueueErr) {
689+
reader.cancel()
690+
}
691+
} else {
692+
try {
693+
controller.enqueue(encoder.encode(`data: ${jsonStr}\n\n`))
694+
} catch (enqueueErr) {
695+
reader.cancel()
696+
}
697+
}
622698
} catch (e) {
623699
logger.warn(`[${tracker.requestId}] Failed to parse final buffer: "${buffer}"`)
624700
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/copilot-message/copilot-message.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
import { type FC, memo, useEffect, useMemo, useState } from 'react'
44
import {
55
Blocks,
6+
BookOpen,
67
Bot,
8+
Box,
79
Check,
810
Clipboard,
911
Info,
1012
LibraryBig,
1113
Loader2,
1214
RotateCcw,
1315
Shapes,
16+
SquareChevronRight,
1417
ThumbsDown,
1518
ThumbsUp,
1619
Workflow,
@@ -389,7 +392,9 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
389392
const fromBlock = Array.isArray((block as any)?.contexts)
390393
? ((block as any).contexts as any[])
391394
: []
392-
const allContexts = direct.length > 0 ? direct : fromBlock
395+
const allContexts = (direct.length > 0 ? direct : fromBlock).filter(
396+
(c: any) => c?.kind !== 'current_workflow'
397+
)
393398
const MAX_VISIBLE = 4
394399
const visible = showAllContexts
395400
? allContexts
@@ -404,14 +409,20 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
404409
>
405410
{ctx?.kind === 'past_chat' ? (
406411
<Bot className='h-3 w-3 text-muted-foreground' />
407-
) : ctx?.kind === 'workflow' ? (
412+
) : ctx?.kind === 'workflow' || ctx?.kind === 'current_workflow' ? (
408413
<Workflow className='h-3 w-3 text-muted-foreground' />
409414
) : ctx?.kind === 'blocks' ? (
410415
<Blocks className='h-3 w-3 text-muted-foreground' />
416+
) : ctx?.kind === 'workflow_block' ? (
417+
<Box className='h-3 w-3 text-muted-foreground' />
411418
) : ctx?.kind === 'knowledge' ? (
412419
<LibraryBig className='h-3 w-3 text-muted-foreground' />
413420
) : ctx?.kind === 'templates' ? (
414421
<Shapes className='h-3 w-3 text-muted-foreground' />
422+
) : ctx?.kind === 'docs' ? (
423+
<BookOpen className='h-3 w-3 text-muted-foreground' />
424+
) : ctx?.kind === 'logs' ? (
425+
<SquareChevronRight className='h-3 w-3 text-muted-foreground' />
415426
) : (
416427
<Info className='h-3 w-3 text-muted-foreground' />
417428
)}
@@ -500,7 +511,10 @@ const CopilotMessage: FC<CopilotMessageProps> = memo(
500511
const contexts: any[] = Array.isArray((message as any).contexts)
501512
? ((message as any).contexts as any[])
502513
: []
503-
const labels = contexts.map((c) => c?.label).filter(Boolean) as string[]
514+
const labels = contexts
515+
.filter((c) => c?.kind !== 'current_workflow')
516+
.map((c) => c?.label)
517+
.filter(Boolean) as string[]
504518
if (!labels.length) return <WordWrap text={text} />
505519

506520
const escapeRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')

0 commit comments

Comments
 (0)