Skip to content

Commit b7876ca

Browse files
icecrasher321Sg312emir-karabeg
authored
improvement(copilot): structured metadata context + start block deprecation (#1362)
* progress * progress * deploy command update * add trigger mode modal * fix trigger icons' * fix corners for add trigger card * update serialization error visual in console * works * improvement(copilot-context): structured context for copilot * forgot long description * Update metadata params * progress * add better workflow ux * progress * highlighting works * trigger card * default agent workflow change * fix build error * remove any casts * address greptile comments * Diff input format * address greptile comments * improvement: ui/ux * improvement: changed to vertical scrolling * fix(workflow): ensure new blocks from sidebar click/drag use getUniqueBlockName (with semantic trigger base when applicable) * Validation + build/edit mark complete * fix trigger dropdown * Copilot stuff (lots of it) * Temp update prod dns * fix trigger check * fix * fix trigger mode check * Fix yaml imports * Fix autolayout error * fix deployed chat * Fix copilot input text overflow * fix trigger mode persistence in addBlock with enableTriggerMode flag passed in * Lint * Fix failing tests * Reset ishosted * Lint * input format for legacy starter * Fix executor --------- Co-authored-by: Siddharth Ganesan <[email protected]> Co-authored-by: Emir Karabeg <[email protected]>
1 parent 68df959 commit b7876ca

File tree

128 files changed

+4265
-1277
lines changed

Some content is hidden

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

128 files changed

+4265
-1277
lines changed

apps/sim/app/api/chat/utils.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { hasAdminPermission } from '@/lib/permissions/utils'
1313
import { processStreamingBlockLogs } from '@/lib/tokenization'
1414
import { getEmailDomain } from '@/lib/urls/utils'
1515
import { decryptSecret, generateRequestId } from '@/lib/utils'
16+
import { TriggerUtils } from '@/lib/workflows/triggers'
1617
import { getBlock } from '@/blocks'
1718
import { Executor } from '@/executor'
1819
import type { BlockLog, ExecutionResult } from '@/executor/types'
@@ -430,9 +431,10 @@ export async function executeWorkflowForChat(
430431
(acc, [id, block]) => {
431432
const blockConfig = getBlock(block.type)
432433
const isTriggerBlock = blockConfig?.category === 'triggers'
434+
const isChatTrigger = block.type === 'chat_trigger'
433435

434-
// Skip trigger blocks during chat execution
435-
if (!isTriggerBlock) {
436+
// Keep all non-trigger blocks and also keep the chat_trigger block
437+
if (!isTriggerBlock || isChatTrigger) {
436438
acc[id] = block
437439
}
438440
return acc
@@ -487,8 +489,10 @@ export async function executeWorkflowForChat(
487489

488490
// Filter edges to exclude connections to/from trigger blocks (same as manual execution)
489491
const triggerBlockIds = Object.keys(mergedStates).filter((id) => {
490-
const blockConfig = getBlock(mergedStates[id].type)
491-
return blockConfig?.category === 'triggers'
492+
const type = mergedStates[id].type
493+
const blockConfig = getBlock(type)
494+
// Exclude chat_trigger from the list so its edges are preserved
495+
return blockConfig?.category === 'triggers' && type !== 'chat_trigger'
492496
})
493497

494498
const filteredEdges = edges.filter(
@@ -613,9 +617,29 @@ export async function executeWorkflowForChat(
613617
// Set up logging on the executor
614618
loggingSession.setupExecutor(executor)
615619

620+
// Determine the start block for chat execution
621+
const startBlock = TriggerUtils.findStartBlock(mergedStates, 'chat')
622+
623+
if (!startBlock) {
624+
const errorMessage =
625+
'No Chat trigger configured for this workflow. Add a Chat Trigger block to enable chat execution.'
626+
logger.error(`[${requestId}] ${errorMessage}`)
627+
await loggingSession.safeCompleteWithError({
628+
endedAt: new Date().toISOString(),
629+
totalDurationMs: 0,
630+
error: {
631+
message: errorMessage,
632+
stackTrace: undefined,
633+
},
634+
})
635+
throw new Error(errorMessage)
636+
}
637+
638+
const startBlockId = startBlock.blockId
639+
616640
let result
617641
try {
618-
result = await executor.execute(workflowId)
642+
result = await executor.execute(workflowId, startBlockId)
619643
} catch (error: any) {
620644
logger.error(`[${requestId}] Chat workflow execution failed:`, error)
621645
await loggingSession.safeCompleteWithError({

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,20 @@ describe('Copilot Chat API Route', () => {
220220
content: 'Hello',
221221
},
222222
],
223+
chatMessages: [
224+
{
225+
role: 'user',
226+
content: 'Hello',
227+
},
228+
],
223229
workflowId: 'workflow-123',
224230
userId: 'user-123',
225231
stream: true,
226232
streamToolCalls: true,
233+
model: 'gpt-5',
227234
mode: 'agent',
228235
messageId: 'mock-uuid-1234-5678',
229-
depth: 0,
236+
version: '1.0.0',
230237
chatId: 'chat-123',
231238
}),
232239
})
@@ -284,13 +291,19 @@ describe('Copilot Chat API Route', () => {
284291
{ role: 'assistant', content: 'Previous response' },
285292
{ role: 'user', content: 'New message' },
286293
],
294+
chatMessages: [
295+
{ role: 'user', content: 'Previous message' },
296+
{ role: 'assistant', content: 'Previous response' },
297+
{ role: 'user', content: 'New message' },
298+
],
287299
workflowId: 'workflow-123',
288300
userId: 'user-123',
289301
stream: true,
290302
streamToolCalls: true,
303+
model: 'gpt-5',
291304
mode: 'agent',
292305
messageId: 'mock-uuid-1234-5678',
293-
depth: 0,
306+
version: '1.0.0',
294307
chatId: 'chat-123',
295308
}),
296309
})
@@ -337,13 +350,18 @@ describe('Copilot Chat API Route', () => {
337350
{ role: 'system', content: 'User seems confused about the workflow' },
338351
{ role: 'user', content: 'Hello' },
339352
],
353+
chatMessages: [
354+
{ role: 'system', content: 'User seems confused about the workflow' },
355+
{ role: 'user', content: 'Hello' },
356+
],
340357
workflowId: 'workflow-123',
341358
userId: 'user-123',
342359
stream: true,
343360
streamToolCalls: true,
361+
model: 'gpt-5',
344362
mode: 'agent',
345363
messageId: 'mock-uuid-1234-5678',
346-
depth: 0,
364+
version: '1.0.0',
347365
chatId: 'chat-123',
348366
}),
349367
})
@@ -427,13 +445,15 @@ describe('Copilot Chat API Route', () => {
427445
expect.objectContaining({
428446
body: JSON.stringify({
429447
messages: [{ role: 'user', content: 'What is this workflow?' }],
448+
chatMessages: [{ role: 'user', content: 'What is this workflow?' }],
430449
workflowId: 'workflow-123',
431450
userId: 'user-123',
432451
stream: true,
433452
streamToolCalls: true,
453+
model: 'gpt-5',
434454
mode: 'ask',
435455
messageId: 'mock-uuid-1234-5678',
436-
depth: 0,
456+
version: '1.0.0',
437457
chatId: 'chat-123',
438458
}),
439459
})

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

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { getCopilotModel } from '@/lib/copilot/config'
1515
import type { CopilotProviderConfig } from '@/lib/copilot/types'
1616
import { env } from '@/lib/env'
1717
import { createLogger } from '@/lib/logs/console/logger'
18-
import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/sim-agent'
18+
import { SIM_AGENT_API_URL_DEFAULT, SIM_AGENT_VERSION } from '@/lib/sim-agent'
1919
import { generateChatTitle } from '@/lib/sim-agent/utils'
2020
import { createFileContent, isSupportedFileType } from '@/lib/uploads/file-utils'
2121
import { S3_COPILOT_CONFIG } from '@/lib/uploads/setup'
@@ -38,8 +38,21 @@ const ChatMessageSchema = z.object({
3838
userMessageId: z.string().optional(), // ID from frontend for the user message
3939
chatId: z.string().optional(),
4040
workflowId: z.string().min(1, 'Workflow ID is required'),
41+
model: z
42+
.enum([
43+
'gpt-5-fast',
44+
'gpt-5',
45+
'gpt-5-medium',
46+
'gpt-5-high',
47+
'gpt-4o',
48+
'gpt-4.1',
49+
'o3',
50+
'claude-4-sonnet',
51+
'claude-4.1-opus',
52+
])
53+
.optional()
54+
.default('gpt-5'),
4155
mode: z.enum(['ask', 'agent']).optional().default('agent'),
42-
depth: z.number().int().min(0).max(3).optional().default(0),
4356
prefetch: z.boolean().optional(),
4457
createNewChat: z.boolean().optional().default(false),
4558
stream: z.boolean().optional().default(true),
@@ -97,8 +110,8 @@ export async function POST(req: NextRequest) {
97110
userMessageId,
98111
chatId,
99112
workflowId,
113+
model,
100114
mode,
101-
depth,
102115
prefetch,
103116
createNewChat,
104117
stream,
@@ -147,19 +160,6 @@ export async function POST(req: NextRequest) {
147160
}
148161
}
149162

150-
// Consolidation mapping: map negative depths to base depth with prefetch=true
151-
let effectiveDepth: number | undefined = typeof depth === 'number' ? depth : undefined
152-
let effectivePrefetch: boolean | undefined = prefetch
153-
if (typeof effectiveDepth === 'number') {
154-
if (effectiveDepth === -2) {
155-
effectiveDepth = 1
156-
effectivePrefetch = true
157-
} else if (effectiveDepth === -1) {
158-
effectiveDepth = 0
159-
effectivePrefetch = true
160-
}
161-
}
162-
163163
// Handle chat context
164164
let currentChat: any = null
165165
let conversationHistory: any[] = []
@@ -366,16 +366,18 @@ export async function POST(req: NextRequest) {
366366

367367
const requestPayload = {
368368
messages: messagesForAgent,
369+
chatMessages: messages, // Full unfiltered messages array
369370
workflowId,
370371
userId: authenticatedUserId,
371372
stream: stream,
372373
streamToolCalls: true,
374+
model: model,
373375
mode: mode,
374376
messageId: userMessageIdToUse,
377+
version: SIM_AGENT_VERSION,
375378
...(providerConfig ? { provider: providerConfig } : {}),
376379
...(effectiveConversationId ? { conversationId: effectiveConversationId } : {}),
377-
...(typeof effectiveDepth === 'number' ? { depth: effectiveDepth } : {}),
378-
...(typeof effectivePrefetch === 'boolean' ? { prefetch: effectivePrefetch } : {}),
380+
...(typeof prefetch === 'boolean' ? { prefetch: prefetch } : {}),
379381
...(session?.user?.name && { userName: session.user.name }),
380382
...(agentContexts.length > 0 && { context: agentContexts }),
381383
...(actualChatId ? { chatId: actualChatId } : {}),
@@ -384,6 +386,9 @@ export async function POST(req: NextRequest) {
384386
try {
385387
logger.info(`[${tracker.requestId}] About to call Sim Agent with context`, {
386388
context: (requestPayload as any).context,
389+
messagesCount: messagesForAgent.length,
390+
chatMessagesCount: messages.length,
391+
hasConversationId: !!effectiveConversationId,
387392
})
388393
} catch {}
389394

apps/sim/app/api/copilot/execute-copilot-server-tool/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export async function POST(req: NextRequest) {
4848
return createBadRequestResponse('Invalid request body for execute-copilot-server-tool')
4949
}
5050
logger.error(`[${tracker.requestId}] Failed to execute server tool:`, error)
51-
return createInternalServerErrorResponse('Failed to execute server tool')
51+
const errorMessage = error instanceof Error ? error.message : 'Failed to execute server tool'
52+
return createInternalServerErrorResponse(errorMessage)
5253
}
5354
}

apps/sim/app/api/workflows/[id]/execute/route.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ describe('Workflow Execution API Route', () => {
292292
const Executor = (await import('@/executor')).Executor
293293
expect(Executor).toHaveBeenCalled()
294294

295-
expect(executeMock).toHaveBeenCalledWith('workflow-id')
295+
expect(executeMock).toHaveBeenCalledWith('workflow-id', 'starter-id')
296296
})
297297

298298
/**
@@ -337,7 +337,7 @@ describe('Workflow Execution API Route', () => {
337337
const Executor = (await import('@/executor')).Executor
338338
expect(Executor).toHaveBeenCalled()
339339

340-
expect(executeMock).toHaveBeenCalledWith('workflow-id')
340+
expect(executeMock).toHaveBeenCalledWith('workflow-id', 'starter-id')
341341

342342
expect(Executor).toHaveBeenCalledWith(
343343
expect.objectContaining({

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { LoggingSession } from '@/lib/logs/execution/logging-session'
1414
import { buildTraceSpans } from '@/lib/logs/execution/trace-spans/trace-spans'
1515
import { decryptSecret, generateRequestId } from '@/lib/utils'
1616
import { loadDeployedWorkflowState } from '@/lib/workflows/db-helpers'
17+
import { TriggerUtils } from '@/lib/workflows/triggers'
1718
import {
1819
createHttpResponseFromBlock,
1920
updateWorkflowRunCounts,
@@ -272,6 +273,32 @@ async function executeWorkflow(
272273
true // Enable validation during execution
273274
)
274275

276+
// Determine API trigger start block
277+
// Direct API execution ONLY works with API trigger blocks (or legacy starter in api/run mode)
278+
const startBlock = TriggerUtils.findStartBlock(mergedStates, 'api', false) // isChildWorkflow = false
279+
280+
if (!startBlock) {
281+
logger.error(`[${requestId}] No API trigger configured for this workflow`)
282+
throw new Error(
283+
'No API trigger configured for this workflow. Add an API Trigger block or use a Start block in API mode.'
284+
)
285+
}
286+
287+
const startBlockId = startBlock.blockId
288+
const triggerBlock = startBlock.block
289+
290+
// Check if the API trigger has any outgoing connections (except for legacy starter blocks)
291+
// Legacy starter blocks have their own validation in the executor
292+
if (triggerBlock.type !== 'starter') {
293+
const outgoingConnections = serializedWorkflow.connections.filter(
294+
(conn) => conn.source === startBlockId
295+
)
296+
if (outgoingConnections.length === 0) {
297+
logger.error(`[${requestId}] API trigger has no outgoing connections`)
298+
throw new Error('API Trigger block must be connected to other blocks to execute')
299+
}
300+
}
301+
275302
const executor = new Executor({
276303
workflow: serializedWorkflow,
277304
currentBlockStates: processedBlockStates,
@@ -287,7 +314,7 @@ async function executeWorkflow(
287314
// Set up logging on the executor
288315
loggingSession.setupExecutor(executor)
289316

290-
const result = await executor.execute(workflowId)
317+
const result = await executor.execute(workflowId, startBlockId)
291318

292319
// Check if we got a StreamingExecution result (with stream + execution properties)
293320
// For API routes, we only care about the ExecutionResult part, not the stream

0 commit comments

Comments
 (0)