Skip to content

Commit 3db8f82

Browse files
feat(deployment-version): capture deployment version in log (#2304)
* feat(deployment-version): capture deployment version in log * improvement: terminal store, logs version, toolbar --------- Co-authored-by: Emir Karabeg <emirkarabeg@berkeley.edu>
1 parent 207a149 commit 3db8f82

File tree

25 files changed

+8020
-41
lines changed

25 files changed

+8020
-41
lines changed

apps/sim/app/api/logs/[id]/route.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { db } from '@sim/db'
2-
import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
2+
import {
3+
permissions,
4+
workflow,
5+
workflowDeploymentVersion,
6+
workflowExecutionLogs,
7+
} from '@sim/db/schema'
38
import { and, eq } from 'drizzle-orm'
49
import { type NextRequest, NextResponse } from 'next/server'
510
import { getSession } from '@/lib/auth'
@@ -29,6 +34,7 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
2934
workflowId: workflowExecutionLogs.workflowId,
3035
executionId: workflowExecutionLogs.executionId,
3136
stateSnapshotId: workflowExecutionLogs.stateSnapshotId,
37+
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
3238
level: workflowExecutionLogs.level,
3339
trigger: workflowExecutionLogs.trigger,
3440
startedAt: workflowExecutionLogs.startedAt,
@@ -46,9 +52,15 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
4652
workflowWorkspaceId: workflow.workspaceId,
4753
workflowCreatedAt: workflow.createdAt,
4854
workflowUpdatedAt: workflow.updatedAt,
55+
deploymentVersion: workflowDeploymentVersion.version,
56+
deploymentVersionName: workflowDeploymentVersion.name,
4957
})
5058
.from(workflowExecutionLogs)
5159
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
60+
.leftJoin(
61+
workflowDeploymentVersion,
62+
eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId)
63+
)
5264
.innerJoin(
5365
permissions,
5466
and(
@@ -81,6 +93,9 @@ export async function GET(_request: NextRequest, { params }: { params: Promise<{
8193
id: log.id,
8294
workflowId: log.workflowId,
8395
executionId: log.executionId,
96+
deploymentVersionId: log.deploymentVersionId,
97+
deploymentVersion: log.deploymentVersion ?? null,
98+
deploymentVersionName: log.deploymentVersionName ?? null,
8499
level: log.level,
85100
duration: log.totalDurationMs ? `${log.totalDurationMs}ms` : null,
86101
trigger: log.trigger,

apps/sim/app/api/logs/route.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { db } from '@sim/db'
2-
import { pausedExecutions, permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
2+
import {
3+
pausedExecutions,
4+
permissions,
5+
workflow,
6+
workflowDeploymentVersion,
7+
workflowExecutionLogs,
8+
} from '@sim/db/schema'
39
import { and, desc, eq, gte, inArray, isNotNull, isNull, lte, or, type SQL, sql } from 'drizzle-orm'
410
import { type NextRequest, NextResponse } from 'next/server'
511
import { z } from 'zod'
@@ -51,6 +57,7 @@ export async function GET(request: NextRequest) {
5157
workflowId: workflowExecutionLogs.workflowId,
5258
executionId: workflowExecutionLogs.executionId,
5359
stateSnapshotId: workflowExecutionLogs.stateSnapshotId,
60+
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
5461
level: workflowExecutionLogs.level,
5562
trigger: workflowExecutionLogs.trigger,
5663
startedAt: workflowExecutionLogs.startedAt,
@@ -71,13 +78,16 @@ export async function GET(request: NextRequest) {
7178
pausedStatus: pausedExecutions.status,
7279
pausedTotalPauseCount: pausedExecutions.totalPauseCount,
7380
pausedResumedCount: pausedExecutions.resumedCount,
81+
deploymentVersion: workflowDeploymentVersion.version,
82+
deploymentVersionName: workflowDeploymentVersion.name,
7483
}
7584
: {
7685
// Basic mode - exclude large fields for better performance
7786
id: workflowExecutionLogs.id,
7887
workflowId: workflowExecutionLogs.workflowId,
7988
executionId: workflowExecutionLogs.executionId,
8089
stateSnapshotId: workflowExecutionLogs.stateSnapshotId,
90+
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
8191
level: workflowExecutionLogs.level,
8292
trigger: workflowExecutionLogs.trigger,
8393
startedAt: workflowExecutionLogs.startedAt,
@@ -98,6 +108,8 @@ export async function GET(request: NextRequest) {
98108
pausedStatus: pausedExecutions.status,
99109
pausedTotalPauseCount: pausedExecutions.totalPauseCount,
100110
pausedResumedCount: pausedExecutions.resumedCount,
111+
deploymentVersion: workflowDeploymentVersion.version,
112+
deploymentVersionName: sql<null>`NULL`, // Only needed in full mode for details panel
101113
}
102114

103115
const baseQuery = db
@@ -107,6 +119,10 @@ export async function GET(request: NextRequest) {
107119
pausedExecutions,
108120
eq(pausedExecutions.executionId, workflowExecutionLogs.executionId)
109121
)
122+
.leftJoin(
123+
workflowDeploymentVersion,
124+
eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId)
125+
)
110126
.innerJoin(
111127
workflow,
112128
and(
@@ -397,6 +413,9 @@ export async function GET(request: NextRequest) {
397413
id: log.id,
398414
workflowId: log.workflowId,
399415
executionId: log.executionId,
416+
deploymentVersionId: log.deploymentVersionId,
417+
deploymentVersion: log.deploymentVersion ?? null,
418+
deploymentVersionName: log.deploymentVersionName ?? null,
400419
level: log.level,
401420
duration: log.totalDurationMs ? `${log.totalDurationMs}ms` : null,
402421
trigger: log.trigger,

apps/sim/app/api/v1/logs/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export async function GET(request: NextRequest) {
111111
id: workflowExecutionLogs.id,
112112
workflowId: workflowExecutionLogs.workflowId,
113113
executionId: workflowExecutionLogs.executionId,
114+
deploymentVersionId: workflowExecutionLogs.deploymentVersionId,
114115
level: workflowExecutionLogs.level,
115116
trigger: workflowExecutionLogs.trigger,
116117
startedAt: workflowExecutionLogs.startedAt,
@@ -161,6 +162,7 @@ export async function GET(request: NextRequest) {
161162
id: log.id,
162163
workflowId: log.workflowId,
163164
executionId: log.executionId,
165+
deploymentVersionId: log.deploymentVersionId,
164166
level: log.level,
165167
trigger: log.trigger,
166168
startedAt: log.startedAt.toISOString(),

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
430430
edges: any[]
431431
loops: Record<string, any>
432432
parallels: Record<string, any>
433+
deploymentVersionId?: string
433434
} | null = null
434435

435436
let processedInput = input
@@ -444,6 +445,10 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
444445
edges: workflowData.edges,
445446
loops: workflowData.loops || {},
446447
parallels: workflowData.parallels || {},
448+
deploymentVersionId:
449+
!shouldUseDraftState && 'deploymentVersionId' in workflowData
450+
? (workflowData.deploymentVersionId as string)
451+
: undefined,
447452
}
448453

449454
const serializedWorkflow = new Serializer().serializeWorkflow(

apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export function Knowledge() {
199199
</div>
200200
</div>
201201

202-
<div className='mt-[24px] grid grid-cols-1 gap-x-[20px] gap-y-[40px] md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
202+
<div className='mt-[24px] grid grid-cols-1 gap-[20px] md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
203203
{isLoading ? (
204204
<BaseCardSkeletonGrid count={8} />
205205
) : filteredAndSortedKnowledgeBases.length === 0 ? (

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/components/line-chart/line-chart.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,23 @@ export function LineChart({
3030
}) {
3131
const containerRef = useRef<HTMLDivElement | null>(null)
3232
const uniqueId = useRef(`chart-${Math.random().toString(36).substring(2, 9)}`).current
33-
const [containerWidth, setContainerWidth] = useState<number>(420)
34-
const width = containerWidth
33+
const [containerWidth, setContainerWidth] = useState<number | null>(null)
34+
const width = containerWidth ?? 0
3535
const height = 166
3636
const padding = { top: 16, right: 28, bottom: 26, left: 26 }
3737
useEffect(() => {
3838
if (!containerRef.current) return
3939
const element = containerRef.current
4040
const ro = new ResizeObserver((entries) => {
4141
const entry = entries[0]
42-
if (entry?.contentRect) {
42+
if (entry?.contentRect && entry.contentRect.width > 0) {
4343
const w = Math.max(280, Math.floor(entry.contentRect.width))
4444
setContainerWidth(w)
4545
}
4646
})
4747
ro.observe(element)
4848
const rect = element.getBoundingClientRect()
49-
if (rect?.width) setContainerWidth(Math.max(280, Math.floor(rect.width)))
49+
if (rect?.width && rect.width > 0) setContainerWidth(Math.max(280, Math.floor(rect.width)))
5050
return () => ro.disconnect()
5151
}, [])
5252
const chartWidth = width - padding.left - padding.right
@@ -95,6 +95,16 @@ export function LineChart({
9595

9696
const hasExternalWrapper = !label || label === ''
9797

98+
if (containerWidth === null) {
99+
return (
100+
<div
101+
ref={containerRef}
102+
className={cn('w-full', !hasExternalWrapper && 'rounded-lg border bg-card p-4')}
103+
style={{ height }}
104+
/>
105+
)
106+
}
107+
98108
if (data.length === 0) {
99109
return (
100110
<div

apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/dashboard.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ interface WorkflowExecution {
4040
const DEFAULT_SEGMENTS = 72
4141
const MIN_SEGMENT_PX = 10
4242

43+
/**
44+
* Predetermined heights for skeleton bars to avoid hydration mismatch.
45+
* Using static values instead of Math.random() ensures server/client consistency.
46+
*/
47+
const SKELETON_BAR_HEIGHTS = [
48+
45, 72, 38, 85, 52, 68, 30, 90, 55, 42, 78, 35, 88, 48, 65, 28, 82, 58, 40, 75, 32, 95, 50, 70,
49+
]
50+
4351
/**
4452
* Skeleton loader for a single graph card
4553
*/
@@ -56,12 +64,12 @@ function GraphCardSkeleton({ title }: { title: string }) {
5664
<div className='flex h-[166px] flex-col justify-end gap-[4px]'>
5765
{/* Skeleton bars simulating chart */}
5866
<div className='flex items-end gap-[2px]'>
59-
{Array.from({ length: 24 }).map((_, i) => (
67+
{SKELETON_BAR_HEIGHTS.map((height, i) => (
6068
<Skeleton
6169
key={i}
6270
className='flex-1'
6371
style={{
64-
height: `${Math.random() * 80 + 20}%`,
72+
height: `${height}%`,
6573
}}
6674
/>
6775
))}
@@ -803,7 +811,6 @@ export default function Dashboard({
803811
<div className='flex-1 overflow-y-auto rounded-t-[6px] bg-[var(--surface-1)] px-[14px] py-[10px]'>
804812
{globalDetails ? (
805813
<LineChart
806-
key={`runs-${expandedWorkflowId || 'all'}-${Object.keys(selectedSegments).length}-${filteredExecutions.length}`}
807814
data={globalDetails.executionCounts}
808815
label=''
809816
color='var(--brand-tertiary)'
@@ -832,7 +839,6 @@ export default function Dashboard({
832839
<div className='flex-1 overflow-y-auto rounded-t-[6px] bg-[var(--surface-1)] px-[14px] py-[10px]'>
833840
{globalDetails ? (
834841
<LineChart
835-
key={`errors-${expandedWorkflowId || 'all'}-${Object.keys(selectedSegments).length}-${filteredExecutions.length}`}
836842
data={globalDetails.failureCounts}
837843
label=''
838844
color='var(--text-error)'
@@ -861,7 +867,6 @@ export default function Dashboard({
861867
<div className='flex-1 overflow-y-auto rounded-t-[6px] bg-[var(--surface-1)] px-[14px] py-[10px]'>
862868
{globalDetails ? (
863869
<LineChart
864-
key={`latency-${expandedWorkflowId || 'all'}-${Object.keys(selectedSegments).length}-${filteredExecutions.length}`}
865870
data={globalDetails.latencies}
866871
label=''
867872
color='var(--c-F59E0B)'

apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ export function LogDetails({
209209
)}
210210

211211
{/* Details Section */}
212-
<div className='flex flex-col'>
212+
<div className='flex min-w-0 flex-col overflow-hidden'>
213213
{/* Level */}
214214
<div className='flex h-[48px] items-center justify-between border-[var(--border)] border-b p-[8px]'>
215215
<span className='font-medium text-[12px] text-[var(--text-tertiary)]'>
@@ -233,14 +233,30 @@ export function LogDetails({
233233
</div>
234234

235235
{/* Duration */}
236-
<div className='flex h-[48px] items-center justify-between p-[8px]'>
236+
<div
237+
className={`flex h-[48px] items-center justify-between border-b p-[8px] ${log.deploymentVersion ? 'border-[var(--border)]' : 'border-transparent'}`}
238+
>
237239
<span className='font-medium text-[12px] text-[var(--text-tertiary)]'>
238240
Duration
239241
</span>
240-
<span className='font-medium text-[14px] text-[var(--text-secondary)]'>
242+
<span className='font-medium text-[13px] text-[var(--text-secondary)]'>
241243
{log.duration || '—'}
242244
</span>
243245
</div>
246+
247+
{/* Version */}
248+
{log.deploymentVersion && (
249+
<div className='flex h-[48px] items-center gap-[8px] p-[8px]'>
250+
<span className='flex-shrink-0 font-medium text-[12px] text-[var(--text-tertiary)]'>
251+
Version
252+
</span>
253+
<div className='flex w-0 flex-1 justify-end'>
254+
<span className='max-w-full truncate rounded-[6px] bg-[#14291B] px-[9px] py-[2px] font-medium text-[#86EFAC] text-[12px]'>
255+
{log.deploymentVersionName || `v${log.deploymentVersion}`}
256+
</span>
257+
</div>
258+
</div>
259+
)}
244260
</div>
245261

246262
{/* Workflow State */}

apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/logs-toolbar.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,18 @@ export function LogsToolbar({
343343
</Button>
344344

345345
{/* View mode toggle */}
346-
<div className='flex h-[32px] items-center rounded-[6px] border border-[var(--border)] bg-[var(--surface-elevated)] p-[2px]'>
346+
<div
347+
className='flex h-[32px] cursor-pointer items-center rounded-[6px] border border-[var(--border)] bg-[var(--surface-elevated)] p-[2px]'
348+
onClick={() => onViewModeChange(isDashboardView ? 'logs' : 'dashboard')}
349+
>
347350
<Button
348351
variant={!isDashboardView ? 'active' : 'ghost'}
349-
onClick={() => onViewModeChange('logs')}
350352
className='h-[26px] rounded-[4px] px-[10px]'
351353
>
352354
Logs
353355
</Button>
354356
<Button
355357
variant={isDashboardView ? 'active' : 'ghost'}
356-
onClick={() => onViewModeChange('dashboard')}
357358
className='h-[26px] rounded-[4px] px-[10px]'
358359
>
359360
Dashboard

apps/sim/app/workspace/[workspaceId]/logs/logs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ export default function Logs() {
609609

610610
{/* Log Details - rendered inside table container */}
611611
<LogDetails
612-
log={logDetailQuery.data || selectedLog}
612+
log={logDetailQuery.data ? { ...selectedLog, ...logDetailQuery.data } : selectedLog}
613613
isOpen={isSidebarOpen}
614614
onClose={handleCloseSidebar}
615615
onNavigateNext={handleNavigateNext}

0 commit comments

Comments
 (0)