Skip to content

Commit d081ab2

Browse files
waleedlatif1claude
andauthored
fix(logs): relax fileSchema so execution logs with files render again (#4495)
* fix(logs): relax fileSchema so execution logs with files render again * improvement(logs): align fileSchema with shared UserFile type - contracts/logs.ts: replace local fileSchema with mediaUserFileSchema (the established UserFile boundary schema with .passthrough()) - file-download.tsx: drop local FileData interface, use UserFile from @/executor/types * improvement(contracts): promote userFileSchema to primitives Move the canonical UserFile boundary schema out of tools/media/shared.ts (where it didn't belong — logs aren't media tools) into primitives.ts as userFileSchema. Update logs, stt, and video contracts to import from the shared primitive. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 98f8e85 commit d081ab2

6 files changed

Lines changed: 34 additions & 48 deletions

File tree

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

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,20 @@ import { createLogger } from '@sim/logger'
55
import { ArrowDown } from 'lucide-react'
66
import { useRouter } from 'next/navigation'
77
import { Button, Loader } from '@/components/emcn'
8+
import { cn } from '@/lib/core/utils/cn'
89
import { extractWorkspaceIdFromExecutionKey, getViewerUrl } from '@/lib/uploads/utils/file-utils'
10+
import type { UserFile } from '@/executor/types'
911

1012
const logger = createLogger('FileCards')
1113

12-
interface FileData {
13-
id?: string
14-
name: string
15-
size: number
16-
type: string
17-
key: string
18-
url: string
19-
uploadedAt: string
20-
expiresAt: string
21-
storageProvider?: 's3' | 'blob' | 'local'
22-
bucketName?: string
23-
}
24-
2514
interface FileCardsProps {
26-
files: FileData[]
15+
files: UserFile[]
2716
isExecutionFile?: boolean
2817
workspaceId?: string
2918
}
3019

3120
interface FileCardProps {
32-
file: FileData
21+
file: UserFile
3322
isExecutionFile?: boolean
3423
workspaceId?: string
3524
}
@@ -157,7 +146,7 @@ export function FileDownload({
157146
className,
158147
workspaceId,
159148
}: {
160-
file: FileData
149+
file: UserFile
161150
isExecutionFile?: boolean
162151
className?: string
163152
workspaceId?: string
@@ -220,7 +209,7 @@ export function FileDownload({
220209
return (
221210
<Button
222211
variant='ghost'
223-
className={`h-7 px-2 text-xs ${className}`}
212+
className={cn('h-7 px-2 text-xs', className)}
224213
onClick={handleDownload}
225214
disabled={isDownloading}
226215
>

apps/sim/lib/api/contracts/logs.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { z } from 'zod'
2+
import { userFileSchema } from '@/lib/api/contracts/primitives'
23
import { defineRouteContract } from '@/lib/api/contracts/types'
34

45
const comparisonOperatorSchema = z.enum(['=', '>', '<', '>=', '<=', '!='])
@@ -66,19 +67,6 @@ const workflowSummarySchema = z
6667
})
6768
.partial()
6869

69-
const fileSchema = z.object({
70-
id: z.string(),
71-
name: z.string(),
72-
size: z.number(),
73-
type: z.string(),
74-
url: z.string(),
75-
key: z.string(),
76-
uploadedAt: z.string(),
77-
expiresAt: z.string(),
78-
storageProvider: z.enum(['s3', 'blob', 'local']).optional(),
79-
bucketName: z.string().optional(),
80-
})
81-
8270
const tokenBreakdownSchema = z
8371
.object({
8472
total: z.number().optional(),
@@ -237,7 +225,7 @@ export const workflowLogSummarySchema = z.object({
237225

238226
export const workflowLogDetailSchema = workflowLogSummarySchema.extend({
239227
executionData: executionDataDetailSchema,
240-
files: z.array(fileSchema).nullable(),
228+
files: z.array(userFileSchema).nullable(),
241229
})
242230

243231
export type WorkflowLogSummary = z.output<typeof workflowLogSummarySchema>

apps/sim/lib/api/contracts/primitives.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ export const workflowIdSchema = z.string().min(1, 'Workflow ID is required')
5959
* Use `.optional()` / `.default(...)` at the call site, not here, so each
6060
* query field controls its own omission/default semantics.
6161
*/
62+
/**
63+
* Canonical boundary schema for `UserFile` (`apps/sim/executor/types.ts`) — the
64+
* shape produced by the executor and persisted in `workflowExecutionLogs.files`,
65+
* forwarded through tool inputs, and rendered in the logs UI. `.passthrough()`
66+
* tolerates legacy/extra fields on stored rows (e.g. `uploadedAt`, `expiresAt`,
67+
* `storageProvider`) without rejecting the whole payload.
68+
*/
69+
export const userFileSchema = z
70+
.object({
71+
id: z.string().optional().default(''),
72+
name: z.string().min(1),
73+
url: z.string().optional().default(''),
74+
size: z.coerce.number().nonnegative(),
75+
type: z.string().optional().default('application/octet-stream'),
76+
key: z.string().min(1),
77+
context: z.string().optional(),
78+
base64: z.string().optional(),
79+
})
80+
.passthrough()
81+
6282
export const booleanQueryFlagSchema = z.preprocess(
6383
(value) => {
6484
if (typeof value === 'boolean') return value

apps/sim/lib/api/contracts/tools/media/shared.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,6 @@ import { z } from 'zod'
33
export const AWS_REGION_PATTERN =
44
/^(eu-isoe|us-isob|us-iso|us-gov|af|ap|ca|cn|eu|il|me|mx|sa|us)-(central|north|northeast|northwest|south|southeast|southwest|east|west)-\d{1,2}$/
55

6-
export const mediaUserFileSchema = z
7-
.object({
8-
id: z.string().optional().default(''),
9-
name: z.string().min(1),
10-
url: z.string().optional().default(''),
11-
size: z.coerce.number().nonnegative(),
12-
type: z.string().optional().default('application/octet-stream'),
13-
key: z.string().min(1),
14-
context: z.string().optional(),
15-
base64: z.string().optional(),
16-
})
17-
.passthrough()
18-
196
export const toolJsonResponseSchema = z
207
.object({
218
success: z.boolean().optional(),

apps/sim/lib/api/contracts/tools/media/stt.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { z } from 'zod'
2-
import { mediaUserFileSchema, toolJsonResponseSchema } from '@/lib/api/contracts/tools/media/shared'
2+
import { userFileSchema } from '@/lib/api/contracts/primitives'
3+
import { toolJsonResponseSchema } from '@/lib/api/contracts/tools/media/shared'
34
import { defineRouteContract } from '@/lib/api/contracts/types'
45

56
export const sttProviders = ['whisper', 'deepgram', 'elevenlabs', 'assemblyai', 'gemini'] as const
67
const MISSING_STT_FIELDS_ERROR = 'Missing required fields: provider and apiKey'
78

8-
export const sttUserFileSchema = mediaUserFileSchema.extend({
9+
export const sttUserFileSchema = userFileSchema.extend({
910
type: z.string().optional().default(''),
1011
})
1112

apps/sim/lib/api/contracts/tools/media/video.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { z } from 'zod'
2-
import { mediaUserFileSchema, toolJsonResponseSchema } from '@/lib/api/contracts/tools/media/shared'
2+
import { userFileSchema } from '@/lib/api/contracts/primitives'
3+
import { toolJsonResponseSchema } from '@/lib/api/contracts/tools/media/shared'
34
import { defineRouteContract } from '@/lib/api/contracts/types'
45

56
export const videoProviders = ['runway', 'veo', 'luma', 'minimax', 'falai'] as const
@@ -19,7 +20,7 @@ export const videoToolBodySchema = z
1920
duration: z.coerce.number().optional(),
2021
aspectRatio: z.string().optional(),
2122
resolution: z.string().optional(),
22-
visualReference: mediaUserFileSchema.optional(),
23+
visualReference: userFileSchema.optional(),
2324
cameraControl: z.unknown().optional(),
2425
endpoint: z.string().optional(),
2526
promptOptimizer: z.boolean().optional(),

0 commit comments

Comments
 (0)