Skip to content

Commit 6e10c79

Browse files
icecrasher321Vikhyath Mondretiwaleedlatif1
authored
feat(enhanced logs): integration + log visualizer canvas (#618)
* feat(logs): enhanced logging system with cleanup and theme fixes - Implement enhanced logging cleanup with S3 archival and retention policies - Fix error propagation in trace spans for manual executions - Add theme-aware styling for frozen canvas modal - Integrate enhanced logging system across all execution pathways - Add comprehensive trace span processing and iteration navigation - Fix boolean parameter types in enhanced logs API * add warning for old logs * fix lint * added cost for streaming outputs * fix overflow issue * fix lint * fix selection on closing sidebar * tooltips z index increase --------- Co-authored-by: Vikhyath Mondreti <vikhyathmondreti@vikhyaths-air.lan> Co-authored-by: Waleed Latif <walif6@gmail.com>
1 parent 1cc8c07 commit 6e10c79

File tree

43 files changed

+4364
-783
lines changed

Some content is hidden

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

43 files changed

+4364
-783
lines changed

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

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import { type NextRequest, NextResponse } from 'next/server'
33
import { v4 as uuidv4 } from 'uuid'
44
import { env } from '@/lib/env'
55
import { createLogger } from '@/lib/logs/console-logger'
6-
import { persistExecutionLogs } from '@/lib/logs/execution-logger'
6+
import { EnhancedLoggingSession } from '@/lib/logs/enhanced-logging-session'
77
import { buildTraceSpans } from '@/lib/logs/trace-spans'
8+
import { processStreamingBlockLogs } from '@/lib/tokenization'
89
import { decryptSecret } from '@/lib/utils'
910
import { db } from '@/db'
1011
import { chat, environment as envTable, userStats, workflow } from '@/db/schema'
@@ -252,11 +253,14 @@ export async function executeWorkflowForChat(
252253

253254
const deployment = deploymentResult[0]
254255
const workflowId = deployment.workflowId
256+
const executionId = uuidv4()
257+
258+
// Set up enhanced logging for chat execution
259+
const loggingSession = new EnhancedLoggingSession(workflowId, executionId, 'chat', requestId)
255260

256261
// Check for multi-output configuration in customizations
257262
const customizations = (deployment.customizations || {}) as Record<string, any>
258263
let outputBlockIds: string[] = []
259-
let outputPaths: string[] = []
260264

261265
// Extract output configs from the new schema format
262266
if (deployment.outputConfigs && Array.isArray(deployment.outputConfigs)) {
@@ -271,13 +275,11 @@ export async function executeWorkflowForChat(
271275
})
272276

273277
outputBlockIds = deployment.outputConfigs.map((config) => config.blockId)
274-
outputPaths = deployment.outputConfigs.map((config) => config.path || '')
275278
} else {
276279
// Use customizations as fallback
277280
outputBlockIds = Array.isArray(customizations.outputBlockIds)
278281
? customizations.outputBlockIds
279282
: []
280-
outputPaths = Array.isArray(customizations.outputPaths) ? customizations.outputPaths : []
281283
}
282284

283285
// Fall back to customizations if we still have no outputs
@@ -287,7 +289,6 @@ export async function executeWorkflowForChat(
287289
customizations.outputBlockIds.length > 0
288290
) {
289291
outputBlockIds = customizations.outputBlockIds
290-
outputPaths = customizations.outputPaths || new Array(outputBlockIds.length).fill('')
291292
}
292293

293294
logger.debug(`[${requestId}] Using ${outputBlockIds.length} output blocks for extraction`)
@@ -407,6 +408,13 @@ export async function executeWorkflowForChat(
407408
{} as Record<string, Record<string, any>>
408409
)
409410

411+
// Start enhanced logging session
412+
await loggingSession.safeStart({
413+
userId: deployment.userId,
414+
workspaceId: '', // TODO: Get from workflow
415+
variables: workflowVariables,
416+
})
417+
410418
const stream = new ReadableStream({
411419
async start(controller) {
412420
const encoder = new TextEncoder()
@@ -458,16 +466,41 @@ export async function executeWorkflowForChat(
458466
},
459467
})
460468

461-
const result = await executor.execute(workflowId)
469+
// Set up enhanced logging on the executor
470+
loggingSession.setupExecutor(executor)
471+
472+
let result
473+
try {
474+
result = await executor.execute(workflowId)
475+
} catch (error: any) {
476+
logger.error(`[${requestId}] Chat workflow execution failed:`, error)
477+
await loggingSession.safeCompleteWithError({
478+
endedAt: new Date().toISOString(),
479+
totalDurationMs: 0,
480+
error: {
481+
message: error.message || 'Chat workflow execution failed',
482+
stackTrace: error.stack,
483+
},
484+
})
485+
throw error
486+
}
462487

463488
if (result && 'success' in result) {
464-
result.logs?.forEach((log: BlockLog) => {
465-
if (streamedContent.has(log.blockId)) {
466-
if (log.output) {
467-
log.output.content = streamedContent.get(log.blockId)
489+
// Update streamed content and apply tokenization
490+
if (result.logs) {
491+
result.logs.forEach((log: BlockLog) => {
492+
if (streamedContent.has(log.blockId)) {
493+
const content = streamedContent.get(log.blockId)
494+
if (log.output) {
495+
log.output.content = content
496+
}
468497
}
469-
}
470-
})
498+
})
499+
500+
// Process all logs for streaming tokenization
501+
const processedCount = processStreamingBlockLogs(result.logs, streamedContent)
502+
logger.info(`[CHAT-API] Processed ${processedCount} blocks for streaming tokenization`)
503+
}
471504

472505
const { traceSpans, totalDuration } = buildTraceSpans(result)
473506
const enrichedResult = { ...result, traceSpans, totalDuration }
@@ -481,8 +514,7 @@ export async function executeWorkflowForChat(
481514
;(enrichedResult.metadata as any).conversationId = conversationId
482515
}
483516
const executionId = uuidv4()
484-
await persistExecutionLogs(workflowId, executionId, enrichedResult, 'chat')
485-
logger.debug(`Persisted logs for deployed chat: ${executionId}`)
517+
logger.debug(`Generated execution ID for deployed chat: ${executionId}`)
486518

487519
if (result.success) {
488520
try {
@@ -506,6 +538,17 @@ export async function executeWorkflowForChat(
506538
)
507539
}
508540

541+
// Complete enhanced logging session (for both success and failure)
542+
if (result && 'success' in result) {
543+
const { traceSpans } = buildTraceSpans(result)
544+
await loggingSession.safeComplete({
545+
endedAt: new Date().toISOString(),
546+
totalDurationMs: result.metadata?.duration || 0,
547+
finalOutput: result.output,
548+
traceSpans,
549+
})
550+
}
551+
509552
controller.close()
510553
},
511554
})
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { eq } from 'drizzle-orm'
2+
import { type NextRequest, NextResponse } from 'next/server'
3+
import { createLogger } from '@/lib/logs/console-logger'
4+
import { db } from '@/db'
5+
import { workflowExecutionLogs, workflowExecutionSnapshots } from '@/db/schema'
6+
7+
const logger = createLogger('FrozenCanvasAPI')
8+
9+
export async function GET(
10+
_request: NextRequest,
11+
{ params }: { params: Promise<{ executionId: string }> }
12+
) {
13+
try {
14+
const { executionId } = await params
15+
16+
logger.debug(`Fetching frozen canvas data for execution: ${executionId}`)
17+
18+
// Get the workflow execution log to find the snapshot
19+
const [workflowLog] = await db
20+
.select()
21+
.from(workflowExecutionLogs)
22+
.where(eq(workflowExecutionLogs.executionId, executionId))
23+
.limit(1)
24+
25+
if (!workflowLog) {
26+
return NextResponse.json({ error: 'Workflow execution not found' }, { status: 404 })
27+
}
28+
29+
// Get the workflow state snapshot
30+
const [snapshot] = await db
31+
.select()
32+
.from(workflowExecutionSnapshots)
33+
.where(eq(workflowExecutionSnapshots.id, workflowLog.stateSnapshotId))
34+
.limit(1)
35+
36+
if (!snapshot) {
37+
return NextResponse.json({ error: 'Workflow state snapshot not found' }, { status: 404 })
38+
}
39+
40+
const response = {
41+
executionId,
42+
workflowId: workflowLog.workflowId,
43+
workflowState: snapshot.stateData,
44+
executionMetadata: {
45+
trigger: workflowLog.trigger,
46+
startedAt: workflowLog.startedAt.toISOString(),
47+
endedAt: workflowLog.endedAt?.toISOString(),
48+
totalDurationMs: workflowLog.totalDurationMs,
49+
blockStats: {
50+
total: workflowLog.blockCount,
51+
success: workflowLog.successCount,
52+
error: workflowLog.errorCount,
53+
skipped: workflowLog.skippedCount,
54+
},
55+
cost: {
56+
total: workflowLog.totalCost ? Number.parseFloat(workflowLog.totalCost) : null,
57+
input: workflowLog.totalInputCost ? Number.parseFloat(workflowLog.totalInputCost) : null,
58+
output: workflowLog.totalOutputCost
59+
? Number.parseFloat(workflowLog.totalOutputCost)
60+
: null,
61+
},
62+
totalTokens: workflowLog.totalTokens,
63+
},
64+
}
65+
66+
logger.debug(`Successfully fetched frozen canvas data for execution: ${executionId}`)
67+
logger.debug(
68+
`Workflow state contains ${Object.keys((snapshot.stateData as any)?.blocks || {}).length} blocks`
69+
)
70+
71+
return NextResponse.json(response)
72+
} catch (error) {
73+
logger.error('Error fetching frozen canvas data:', error)
74+
return NextResponse.json({ error: 'Failed to fetch frozen canvas data' }, { status: 500 })
75+
}
76+
}

0 commit comments

Comments
 (0)