Skip to content

Commit c8f4791

Browse files
authored
Feat/copilot client clean (#1118)
* SSE tool call v1 - not tested yet * Handle tool call generation sse * Add mark complete api * copilot new progress * Migrate get user workflow * Run workflow migrated * Migrate run workflow and remove some dead code * Migrate gdrive request access * Add server side execution logic * Get block metadata migrated * Build workflow progress * Somewhat working condition, build still broken * Stuff * Get workflow console * search online tool * Set/get env vars * oauth, gdrive list, gdrive read * Search docs * Build workflow update * Edit workflow * Migrate plan tool * Checkoff * Refactor * Improvement * checkpoint * New store basics * Generating adds to map * Update * Display v1 * Update * Stuff * Stuff * Stuff * Edit works * Interrupt tool fixes * Interrupt tool fixes * Good progress * new copilot to copilot * Fix chat laoding * Skip rendering of non registered tools * Small fix * Updates * Updates * Updates * Update * Some fixes * Revert fixes * run workflow * Move to background button shows up * User input scroll bar * Lint * Build errors * Diff controls * Restore ui * Ui fixes * Max mode ui * Thinking text collapse * Tool ui updates * Mode selector UI * Lint * Ui * Update icon * Dummy test * Lint
1 parent 6c9e0ec commit c8f4791

File tree

95 files changed

+6377
-10785
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+6377
-10785
lines changed

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

Lines changed: 70 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ async function generateChatTitleAsync(
143143
streamController?: ReadableStreamDefaultController<Uint8Array>
144144
): Promise<void> {
145145
try {
146-
logger.info(`[${requestId}] Starting async title generation for chat ${chatId}`)
146+
// logger.info(`[${requestId}] Starting async title generation for chat ${chatId}`)
147147

148148
const title = await generateChatTitle(userMessage)
149149

@@ -167,7 +167,7 @@ async function generateChatTitleAsync(
167167
logger.debug(`[${requestId}] Sent title_updated event to client: "${title}"`)
168168
}
169169

170-
logger.info(`[${requestId}] Generated title for chat ${chatId}: "${title}"`)
170+
// logger.info(`[${requestId}] Generated title for chat ${chatId}: "${title}"`)
171171
} catch (error) {
172172
logger.error(`[${requestId}] Failed to generate title for chat ${chatId}:`, error)
173173
// Don't throw - this is a background operation
@@ -229,21 +229,21 @@ export async function POST(req: NextRequest) {
229229
}
230230
}
231231

232-
logger.info(`[${tracker.requestId}] Processing copilot chat request`, {
233-
userId: authenticatedUserId,
234-
workflowId,
235-
chatId,
236-
mode,
237-
stream,
238-
createNewChat,
239-
messageLength: message.length,
240-
hasImplicitFeedback: !!implicitFeedback,
241-
provider: provider || 'openai',
242-
hasConversationId: !!conversationId,
243-
depth,
244-
prefetch,
245-
origin: requestOrigin,
246-
})
232+
// logger.info(`[${tracker.requestId}] Processing copilot chat request`, {
233+
// userId: authenticatedUserId,
234+
// workflowId,
235+
// chatId,
236+
// mode,
237+
// stream,
238+
// createNewChat,
239+
// messageLength: message.length,
240+
// hasImplicitFeedback: !!implicitFeedback,
241+
// provider: provider || 'openai',
242+
// hasConversationId: !!conversationId,
243+
// depth,
244+
// prefetch,
245+
// origin: requestOrigin,
246+
// })
247247

248248
// Handle chat context
249249
let currentChat: any = null
@@ -285,7 +285,7 @@ export async function POST(req: NextRequest) {
285285
// Process file attachments if present
286286
const processedFileContents: any[] = []
287287
if (fileAttachments && fileAttachments.length > 0) {
288-
logger.info(`[${tracker.requestId}] Processing ${fileAttachments.length} file attachments`)
288+
// logger.info(`[${tracker.requestId}] Processing ${fileAttachments.length} file attachments`)
289289

290290
for (const attachment of fileAttachments) {
291291
try {
@@ -296,7 +296,7 @@ export async function POST(req: NextRequest) {
296296
}
297297

298298
// Download file from S3
299-
logger.info(`[${tracker.requestId}] Downloading file: ${attachment.s3_key}`)
299+
// logger.info(`[${tracker.requestId}] Downloading file: ${attachment.s3_key}`)
300300
let fileBuffer: Buffer
301301
if (USE_S3_STORAGE) {
302302
fileBuffer = await downloadFromS3WithConfig(attachment.s3_key, S3_COPILOT_CONFIG)
@@ -309,9 +309,9 @@ export async function POST(req: NextRequest) {
309309
const fileContent = createAnthropicFileContent(fileBuffer, attachment.media_type)
310310
if (fileContent) {
311311
processedFileContents.push(fileContent)
312-
logger.info(
313-
`[${tracker.requestId}] Processed file: ${attachment.filename} (${attachment.media_type})`
314-
)
312+
// logger.info(
313+
// `[${tracker.requestId}] Processed file: ${attachment.filename} (${attachment.media_type})`
314+
// )
315315
}
316316
} catch (error) {
317317
logger.error(
@@ -424,27 +424,7 @@ export async function POST(req: NextRequest) {
424424
...(requestOrigin ? { origin: requestOrigin } : {}),
425425
}
426426

427-
// Log the payload being sent to the streaming endpoint
428-
try {
429-
logger.info(`[${tracker.requestId}] Sending payload to sim agent streaming endpoint`, {
430-
url: `${SIM_AGENT_API_URL}/api/chat-completion-streaming`,
431-
provider: providerToUse,
432-
mode,
433-
stream,
434-
workflowId,
435-
hasConversationId: !!effectiveConversationId,
436-
depth: typeof effectiveDepth === 'number' ? effectiveDepth : undefined,
437-
prefetch: typeof effectivePrefetch === 'boolean' ? effectivePrefetch : undefined,
438-
messagesCount: requestPayload.messages.length,
439-
...(requestOrigin ? { origin: requestOrigin } : {}),
440-
})
441-
// Full payload as JSON string
442-
logger.info(
443-
`[${tracker.requestId}] Full streaming payload: ${JSON.stringify(requestPayload)}`
444-
)
445-
} catch (e) {
446-
logger.warn(`[${tracker.requestId}] Failed to log payload preview for streaming endpoint`, e)
447-
}
427+
// Log the payload being sent to the streaming endpoint (logs currently disabled)
448428

449429
const simAgentResponse = await fetch(`${SIM_AGENT_API_URL}/api/chat-completion-streaming`, {
450430
method: 'POST',
@@ -475,7 +455,7 @@ export async function POST(req: NextRequest) {
475455

476456
// If streaming is requested, forward the stream and update chat later
477457
if (stream && simAgentResponse.body) {
478-
logger.info(`[${tracker.requestId}] Streaming response from sim agent`)
458+
// logger.info(`[${tracker.requestId}] Streaming response from sim agent`)
479459

480460
// Create user message to save
481461
const userMessage = {
@@ -493,7 +473,7 @@ export async function POST(req: NextRequest) {
493473
let assistantContent = ''
494474
const toolCalls: any[] = []
495475
let buffer = ''
496-
let isFirstDone = true
476+
const isFirstDone = true
497477
let responseIdFromStart: string | undefined
498478
let responseIdFromDone: string | undefined
499479
// Track tool call progress to identify a safe done event
@@ -515,30 +495,30 @@ export async function POST(req: NextRequest) {
515495

516496
// Start title generation in parallel if needed
517497
if (actualChatId && !currentChat?.title && conversationHistory.length === 0) {
518-
logger.info(`[${tracker.requestId}] Starting title generation with stream updates`, {
519-
chatId: actualChatId,
520-
hasTitle: !!currentChat?.title,
521-
conversationLength: conversationHistory.length,
522-
message: message.substring(0, 100) + (message.length > 100 ? '...' : ''),
523-
})
498+
// logger.info(`[${tracker.requestId}] Starting title generation with stream updates`, {
499+
// chatId: actualChatId,
500+
// hasTitle: !!currentChat?.title,
501+
// conversationLength: conversationHistory.length,
502+
// message: message.substring(0, 100) + (message.length > 100 ? '...' : ''),
503+
// })
524504
generateChatTitleAsync(actualChatId, message, tracker.requestId, controller).catch(
525505
(error) => {
526506
logger.error(`[${tracker.requestId}] Title generation failed:`, error)
527507
}
528508
)
529509
} else {
530-
logger.debug(`[${tracker.requestId}] Skipping title generation`, {
531-
chatId: actualChatId,
532-
hasTitle: !!currentChat?.title,
533-
conversationLength: conversationHistory.length,
534-
reason: !actualChatId
535-
? 'no chatId'
536-
: currentChat?.title
537-
? 'already has title'
538-
: conversationHistory.length > 0
539-
? 'not first message'
540-
: 'unknown',
541-
})
510+
// logger.debug(`[${tracker.requestId}] Skipping title generation`, {
511+
// chatId: actualChatId,
512+
// hasTitle: !!currentChat?.title,
513+
// conversationLength: conversationHistory.length,
514+
// reason: !actualChatId
515+
// ? 'no chatId'
516+
// : currentChat?.title
517+
// ? 'already has title'
518+
// : conversationHistory.length > 0
519+
// ? 'not first message'
520+
// : 'unknown',
521+
// })
542522
}
543523

544524
// Forward the sim agent stream and capture assistant response
@@ -549,7 +529,7 @@ export async function POST(req: NextRequest) {
549529
while (true) {
550530
const { done, value } = await reader.read()
551531
if (done) {
552-
logger.info(`[${tracker.requestId}] Stream reading completed`)
532+
// logger.info(`[${tracker.requestId}] Stream reading completed`)
553533
break
554534
}
555535

@@ -559,9 +539,9 @@ export async function POST(req: NextRequest) {
559539
controller.enqueue(value)
560540
} catch (error) {
561541
// Client disconnected - stop reading from sim agent
562-
logger.info(
563-
`[${tracker.requestId}] Client disconnected, stopping stream processing`
564-
)
542+
// logger.info(
543+
// `[${tracker.requestId}] Client disconnected, stopping stream processing`
544+
// )
565545
reader.cancel() // Stop reading from sim agent
566546
break
567547
}
@@ -608,15 +588,15 @@ export async function POST(req: NextRequest) {
608588
break
609589

610590
case 'tool_call':
611-
logger.info(
612-
`[${tracker.requestId}] Tool call ${event.data?.partial ? '(partial)' : '(complete)'}:`,
613-
{
614-
id: event.data?.id,
615-
name: event.data?.name,
616-
arguments: event.data?.arguments,
617-
blockIndex: event.data?._blockIndex,
618-
}
619-
)
591+
// logger.info(
592+
// `[${tracker.requestId}] Tool call ${event.data?.partial ? '(partial)' : '(complete)'}:`,
593+
// {
594+
// id: event.data?.id,
595+
// name: event.data?.name,
596+
// arguments: event.data?.arguments,
597+
// blockIndex: event.data?._blockIndex,
598+
// }
599+
// )
620600
if (!event.data?.partial) {
621601
toolCalls.push(event.data)
622602
if (event.data?.id) {
@@ -625,30 +605,24 @@ export async function POST(req: NextRequest) {
625605
}
626606
break
627607

628-
case 'tool_execution':
629-
logger.info(`[${tracker.requestId}] Tool execution started:`, {
630-
toolCallId: event.toolCallId,
631-
toolName: event.toolName,
632-
status: event.status,
633-
})
608+
case 'tool_generating':
609+
// logger.info(`[${tracker.requestId}] Tool generating:`, {
610+
// toolCallId: event.toolCallId,
611+
// toolName: event.toolName,
612+
// })
634613
if (event.toolCallId) {
635-
if (event.status === 'completed') {
636-
startedToolExecutionIds.add(event.toolCallId)
637-
completedToolExecutionIds.add(event.toolCallId)
638-
} else {
639-
startedToolExecutionIds.add(event.toolCallId)
640-
}
614+
startedToolExecutionIds.add(event.toolCallId)
641615
}
642616
break
643617

644618
case 'tool_result':
645-
logger.info(`[${tracker.requestId}] Tool result received:`, {
646-
toolCallId: event.toolCallId,
647-
toolName: event.toolName,
648-
success: event.success,
649-
result: `${JSON.stringify(event.result).substring(0, 200)}...`,
650-
resultSize: JSON.stringify(event.result).length,
651-
})
619+
// logger.info(`[${tracker.requestId}] Tool result received:`, {
620+
// toolCallId: event.toolCallId,
621+
// toolName: event.toolName,
622+
// success: event.success,
623+
// result: `${JSON.stringify(event.result).substring(0, 200)}...`,
624+
// resultSize: JSON.stringify(event.result).length,
625+
// })
652626
if (event.toolCallId) {
653627
completedToolExecutionIds.add(event.toolCallId)
654628
}
@@ -669,54 +643,29 @@ export async function POST(req: NextRequest) {
669643
case 'start':
670644
if (event.data?.responseId) {
671645
responseIdFromStart = event.data.responseId
672-
logger.info(
673-
`[${tracker.requestId}] Received start event with responseId: ${responseIdFromStart}`
674-
)
675646
}
676647
break
677648

678649
case 'done':
679650
if (event.data?.responseId) {
680651
responseIdFromDone = event.data.responseId
681652
lastDoneResponseId = responseIdFromDone
682-
logger.info(
683-
`[${tracker.requestId}] Received done event with responseId: ${responseIdFromDone}`
684-
)
653+
685654
// Mark this done as safe only if no tool call is currently in progress or pending
686655
const announced = announcedToolCallIds.size
687656
const completed = completedToolExecutionIds.size
688657
const started = startedToolExecutionIds.size
689658
const hasToolInProgress = announced > completed || started > completed
690659
if (!hasToolInProgress) {
691660
lastSafeDoneResponseId = responseIdFromDone
692-
logger.info(
693-
`[${tracker.requestId}] Marked done as SAFE (no tools in progress)`
694-
)
695-
} else {
696-
logger.info(
697-
`[${tracker.requestId}] Done received but tools are in progress (announced=${announced}, started=${started}, completed=${completed})`
698-
)
699661
}
700662
}
701-
if (isFirstDone) {
702-
logger.info(
703-
`[${tracker.requestId}] Initial AI response complete, tool count: ${toolCalls.length}`
704-
)
705-
isFirstDone = false
706-
} else {
707-
logger.info(`[${tracker.requestId}] Conversation round complete`)
708-
}
709663
break
710664

711665
case 'error':
712-
logger.error(`[${tracker.requestId}] Stream error event:`, event.error)
713666
break
714667

715668
default:
716-
logger.debug(
717-
`[${tracker.requestId}] Unknown event type: ${event.type}`,
718-
event
719-
)
720669
}
721670
} catch (e) {
722671
// Enhanced error handling for large payloads and parsing issues
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { type NextRequest, NextResponse } from 'next/server'
2+
import { z } from 'zod'
3+
import {
4+
authenticateCopilotRequestSessionOnly,
5+
createBadRequestResponse,
6+
createInternalServerErrorResponse,
7+
createRequestTracker,
8+
createUnauthorizedResponse,
9+
} from '@/lib/copilot/auth'
10+
import { routeExecution } from '@/lib/copilot/tools/server/router'
11+
import { createLogger } from '@/lib/logs/console/logger'
12+
13+
const logger = createLogger('ExecuteCopilotServerToolAPI')
14+
15+
const ExecuteSchema = z.object({
16+
toolName: z.string(),
17+
payload: z.unknown().optional(),
18+
})
19+
20+
export async function POST(req: NextRequest) {
21+
const tracker = createRequestTracker()
22+
try {
23+
const { userId, isAuthenticated } = await authenticateCopilotRequestSessionOnly()
24+
if (!isAuthenticated || !userId) {
25+
return createUnauthorizedResponse()
26+
}
27+
28+
const body = await req.json()
29+
try {
30+
const preview = JSON.stringify(body).slice(0, 300)
31+
logger.debug(`[${tracker.requestId}] Incoming request body preview`, { preview })
32+
} catch {}
33+
34+
const { toolName, payload } = ExecuteSchema.parse(body)
35+
36+
logger.info(`[${tracker.requestId}] Executing server tool`, { toolName })
37+
const result = await routeExecution(toolName, payload)
38+
39+
try {
40+
const resultPreview = JSON.stringify(result).slice(0, 300)
41+
logger.debug(`[${tracker.requestId}] Server tool result preview`, { toolName, resultPreview })
42+
} catch {}
43+
44+
return NextResponse.json({ success: true, result })
45+
} catch (error) {
46+
if (error instanceof z.ZodError) {
47+
logger.debug(`[${tracker.requestId}] Zod validation error`, { issues: error.issues })
48+
return createBadRequestResponse('Invalid request body for execute-copilot-server-tool')
49+
}
50+
logger.error(`[${tracker.requestId}] Failed to execute server tool:`, error)
51+
return createInternalServerErrorResponse('Failed to execute server tool')
52+
}
53+
}

0 commit comments

Comments
 (0)