Conversation
🧪 BenchmarkShould we run the Virtual MCP strategy benchmark for this PR? React with 👍 to run the benchmark.
Benchmark will run on the next push after you react. |
Release OptionsSuggested: Patch ( React with an emoji to override the release type:
Current version:
|
There was a problem hiding this comment.
5 issues found across 11 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/api/routes/decopilot/schemas.ts">
<violation number="1" location="apps/mesh/src/api/routes/decopilot/schemas.ts:91">
P2: Restrict `imageMode.aspectRatio` to the supported ratio values instead of accepting any string.</violation>
</file>
<file name="apps/mesh/src/web/components/chat/input.tsx">
<violation number="1" location="apps/mesh/src/web/components/chat/input.tsx:527">
P2: Hiding the upload button in image mode does not disable drag-and-drop uploads, because the editor still mounts `FileUploader`. That leaves image mode accepting files that the backend ignores or rejects.</violation>
</file>
<file name="apps/mesh/src/web/components/chat/select-model.tsx">
<violation number="1" location="apps/mesh/src/web/components/chat/select-model.tsx:783">
P1: Filtering the selector in image mode does not enforce an image-capable selected model, so image requests can still be sent with the previously selected text model.</violation>
</file>
<file name="apps/mesh/src/api/routes/decopilot/stream-core.ts">
<violation number="1" location="apps/mesh/src/api/routes/decopilot/stream-core.ts:241">
P1: Validate image-model support on the server before calling `imageModel()`. Otherwise invalid image-mode requests fail at runtime after the message has already been saved.</violation>
<violation number="2" location="apps/mesh/src/api/routes/decopilot/stream-core.ts:318">
P2: Handle aborted image requests before marking the run failed. As written, cancelling image generation is recorded as a failed thread.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
4 issues found across 9 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/web/components/chat/image-mode-toggle.tsx">
<violation number="1" location="apps/mesh/src/web/components/chat/image-mode-toggle.tsx:51">
P1: Guard enabling image mode until an image-capable model is available; otherwise image requests can be sent with the current text model and fail at runtime.</violation>
</file>
<file name="apps/mesh/src/web/components/chat/store/chat-store.ts">
<violation number="1" location="apps/mesh/src/web/components/chat/store/chat-store.ts:432">
P1: Preserve the current `credentialId` when auto-selecting an image model; otherwise the stored model can lose its connection and later be sent with the wrong key.
(Based on your team's feedback about treating the chat model and credential as an atomic pair.) [FEEDBACK_USED]</violation>
<violation number="2" location="apps/mesh/src/web/components/chat/store/chat-store.ts:438">
P2: Don’t enter image mode unless an image-capable model is available; right now the store can keep the old text model selected and send an invalid image request.</violation>
</file>
<file name="apps/mesh/src/web/components/chat/select-model.tsx">
<violation number="1" location="apps/mesh/src/web/components/chat/select-model.tsx:788">
P1: Guard the image-mode model list against connections that have no image-generation models. As written, image mode can be enabled with an empty selector while requests still use the previous non-image model and fail.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
4 issues found across 7 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/web/components/chat/store/chat-store.ts">
<violation number="1" location="apps/mesh/src/web/components/chat/store/chat-store.ts:217">
P2: Switching threads while image mode is active loses the previously selected text model. `setActiveThread()` clears `_previousModel` and disables `imageMode` directly, so the temporary image model stays selected in normal chat instead of being restored.</violation>
</file>
<file name="apps/mesh/src/api/routes/decopilot/stream-core.ts">
<violation number="1" location="apps/mesh/src/api/routes/decopilot/stream-core.ts:272">
P2: Move the new `monitorLlmCall` success/error reporting so only the `generateImage()` result determines model success; otherwise write failures can produce contradictory monitoring events.</violation>
<violation number="2" location="apps/mesh/src/api/routes/decopilot/stream-core.ts:291">
P1: Do not relabel unsupported image bytes as `image/png`; reject the type or preserve the original MIME type.</violation>
</file>
<file name="apps/mesh/src/web/components/chat/image-mode-toggle.tsx">
<violation number="1" location="apps/mesh/src/web/components/chat/image-mode-toggle.tsx:50">
P3: The new availability check disables the button without applying the disabled styles, so it still looks clickable when no image-capable models are available.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| await saveMessagesToThread(requestMessage); | ||
|
|
||
| // ================================================================ | ||
| // Image generation mode — skip MCP/tool setup, call generateImage |
There was a problem hiding this comment.
image generation should be an innate tool, not an if block that duplicates existing code
There was a problem hiding this comment.
Refactored — image generation is now a generate_image built-in tool (like subtask or sandbox). The ~195-line if-block is gone. The image model is selected via a separate picker and passed to the tool, while the language model handles the agentic loop.
44b1872 to
c2abc8c
Compare
There was a problem hiding this comment.
1 issue found across 12 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/api/routes/decopilot/stream-core.ts">
<violation number="1" location="apps/mesh/src/api/routes/decopilot/stream-core.ts:330">
P2: Guard the image-generation prompt the same way the tool registration is guarded; otherwise the model can be told to call `generate_image` when the tool is not available for the selected provider.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
3430cc5 to
1b0bcbb
Compare
Implement image generation in the chat UI using OpenRouter's image models through the AI SDK. Add an "Image" toggle button that filters the model selector to image-capable models, appears with aspect ratio picker, and generates images inline in chat with nice UI. Generated images are stored as base64 in message threads and render with download-on-hover functionality. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Move Image button to right side near model picker for stable positioning, add "image-generation" capability to distinguish output from input modalities, auto-select Gemini model when entering image mode, and filter model picker to only show image generation models. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add server-side capability guard before calling imageModel() - Validate aspectRatio as enum instead of free-form string - Allowlist mediaType from provider response (prevent injection) - Add monitorLlmCall to image path for observability parity - Add streamFinished guard to prevent double FINISH dispatch - Reset imageMode on thread switch, clear _previousModel on reset - Disable Image toggle when no image models available - Fix Firefox download (append anchor to DOM before click) - Clean up mediaType extension parsing for downloads - Revert unrelated conductor.json change - Add IMAGE-GEN-FOLLOWUPS.md tracking deferred items Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… chat - Never persist image model to localStorage so refresh always restores text model - Reset image mode and restore text model on createThread and setActiveThread - Strip parenthetical suffix from model names in compact trigger display Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of raw "No image generated" error, show a clear message explaining that image mode is for generating images. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the ~195-line `if (input.imageMode)` block in stream-core.ts with a `generate_image` built-in tool that runs inside the normal streamText agentic loop. The image model is now selected via a dedicated picker (separate from the language model selector), and the tool handles generateImage() calls, metrics, and error handling internally. Key changes: - New `generate_image` built-in tool following subtask/sandbox pattern - New `ImageModelSelector` component replaces `ImageModeToggle` - Selecting an image model enables image mode; clearing exits it - Language model stays selected for streamText; image model is separate - Remove model save/restore (_previousModel) logic from chat store - Remove imageMode filtering from text model selector Addresses PR review feedback from @pedrofrxncx. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Store generated images in object storage when available, serving them via /api/files instead of embedding large base64 data URLs in messages. Falls back to inline base64 when object storage is not configured. Also fixes tool error handling: return error strings instead of throwing from tool execute, which crashed the entire SSE stream. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1b0bcbb to
4bb39de
Compare
What is this contribution about?
Implements image generation as a first-class feature in the chat UI. Added an "Image" toggle button near the model selector that switches to image generation mode. When active, the model selector filters to only image-capable models (like OpenRouter's Nano Banana 2), aspect ratio controls appear (1:1, 16:9, 9:16), and the user's text becomes an image prompt. Images are generated using the AI SDK's
generateImage()with OpenRouter's image models and rendered inline in chat with nice UI.How to Test
bun run devMigration Notes
No database migrations required. Images are stored as base64 in message threads, same as existing file attachments.
Review Checklist
bun run fmtandbun run checkpass🤖 Generated with Claude Code
Summary by cubic
Adds inline image generation to chat with a dedicated image model picker and a
generate_imagetool. Images stream asfileparts and, when object storage is available, are stored and served via/api/fileswith a download button; falls back to base64 if not.New Features
image-generationmodels with a new capability icon; works with OpenRouter.generate_imagecallsgenerateImage()and writes afilepart;StreamRequestSchemaaddsimageModel { id, aspectRatio };stream-corepasses image config and adds an image-generation hint./api/files; fall back to inline base64 when storage isn’t configured.Bug Fixes
aspectRatioenum (incl. 4:3, 3:4), allowlistmediaType, addmonitorLlmCall, prevent double FINISH, return friendly errors.Written for commit 4bb39de. Summary will update on new commits.