From ac165a2db44c675ec820c1f1a3977d69b2902271 Mon Sep 17 00:00:00 2001 From: Ryan Westfall Date: Tue, 20 Oct 2026 08:07:39 -0700 Subject: [PATCH] Fix beta build: image check accepts Blob attachments (#131) ## Summary - Closes #130. - [Deploy Beta run 929](https://git.aimloperations.com/ai_ml_operations/chat_web_app/actions/runs/929) failed `npm run build:beta` with `TS2345`: `isImageFile` took a `File`, but the composer file is a `Blob`. - The helper now accepts `Blob`. `URL.createObjectURL` already does. The `as File` casts are gone. - Jest stayed green on that run because it does not typecheck this call. `tsc --noEmit` is clean on this branch. ## Test plan - [ ] `npx tsc --noEmit` in `llm-fe` - [ ] `npm run build:beta` (or the next Deploy Beta) compiles - [ ] Image attach still shows a thumbnail and sends - [ ] Document attach still shows the filename Reviewed-on: https://git.aimloperations.com/ai_ml_operations/chat_web_app/pulls/131 --- .../llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx | 12 ++++++------ llm-fe/src/llm-fe/utils/visionAttachment.test.ts | 1 + llm-fe/src/llm-fe/utils/visionAttachment.ts | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx index 7608a5b..29db94e 100644 --- a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx +++ b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx @@ -335,15 +335,15 @@ const AlwaysScrollToBottom = ({ trigger }: { trigger: string | number }): JSX.El return
; }; -const AttachmentPreview = ({ file }: { file: File | null }): JSX.Element | null => { +const AttachmentPreview = ({ file }: { file: Blob | null }): JSX.Element | null => { const theme = useContext(ThemeContext); const [url, setUrl] = useState(null); useEffect(() => { - if (!isImageFile(file)) { + if (!file || !isImageFile(file)) { setUrl(null); return; } - const next = URL.createObjectURL(file as File); + const next = URL.createObjectURL(file); setUrl(next); return () => URL.revokeObjectURL(next); }, [file]); @@ -370,7 +370,7 @@ const AttachmentPreview = ({ file }: { file: File | null }): JSX.Element | null color: theme?.darkMode ? "rgba(255,255,255,0.6)" : "rgba(0,0,0,0.6)", textAlign: "center", }}> - Attached: {file.name} + Attached: {file instanceof File ? file.name : "attachment"}
); }; @@ -473,7 +473,7 @@ const AsyncDashboardInner = (): JSX.Element => { } try { clearActivity(); - const attachedImage = isImageFile(file) ? URL.createObjectURL(file as File) : null; + const attachedImage = file && isImageFile(file) ? URL.createObjectURL(file) : null; const tempConversations: ConversationPrompt[] = [ ...conversationDetails, new ConversationPrompt({ @@ -753,7 +753,7 @@ const AsyncDashboardInner = (): JSX.Element => { - + )} diff --git a/llm-fe/src/llm-fe/utils/visionAttachment.test.ts b/llm-fe/src/llm-fe/utils/visionAttachment.test.ts index fb8ea27..3eaed26 100644 --- a/llm-fe/src/llm-fe/utils/visionAttachment.test.ts +++ b/llm-fe/src/llm-fe/utils/visionAttachment.test.ts @@ -16,6 +16,7 @@ describe('visionAttachment', () => { it('detects image files', () => { expect(isImageFile(new File(['x'], 'a.png', { type: 'image/png' }))).toBe(true); expect(isImageFile(new File(['x'], 'a.pdf', { type: 'application/pdf' }))).toBe(false); + expect(isImageFile(new Blob(['x'], { type: 'image/png' }))).toBe(true); expect(isImageFile(null)).toBe(false); }); }); diff --git a/llm-fe/src/llm-fe/utils/visionAttachment.ts b/llm-fe/src/llm-fe/utils/visionAttachment.ts index 380039c..65b0e9b 100644 --- a/llm-fe/src/llm-fe/utils/visionAttachment.ts +++ b/llm-fe/src/llm-fe/utils/visionAttachment.ts @@ -1,7 +1,7 @@ export const CHAT_FILE_ACCEPT = '.csv,.xlsx,.txt,.pdf,.PDF,image/png,image/jpeg,image/webp,image/gif'; -export function isImageFile(file: File | null | undefined): boolean { +export function isImageFile(file: Blob | null | undefined): boolean { return Boolean(file && file.type.startsWith('image/')); }