Add pricing field to GEMINI_IMAGE_MODELS array:
- nano-banana: $0.039 per-run
- nano-banana-pro: $0.134 per-run
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Security & API route fixes:
- Remove API key query param support in fal/models route to prevent credential leakage
- Fix MIME type fallback in save-generation route (now uses prefix-based fallback: image/* -> png, video/* -> mp4, unknown -> bin)
- Add timeout (60s) and size limits (500MB) for remote fetch in save-generation route using AbortController
CSS fix:
- Consolidate duplicate :root blocks in globals.css
- Change handle selector from data-handletype="prompt" to data-handletype="text"
- Update --handle-color-text CSS variable from #a3a3a3 to #3b82f6
Test file global mock isolation:
- Fix useAnnotationStore mock to support Zustand selector pattern
- Use vi.stubGlobal() instead of direct global assignments to prevent test leakage
- Restore global.Image in afterAll hook in AnnotationModal.test.tsx
- Wrap async Done assertion in waitFor() in AnnotationModal.test.tsx
- Use idiomatic fireEvent.dragOver() in AnnotationNode.test.tsx
- Fix global.confirm stub with proper beforeAll/afterAll lifecycle in CostDialog.test.tsx
- Import ReactNode type properly in EdgeToolbar.test.tsx
- Update save-generation route test to expect AbortController signal in fetch call
Fixes issues with external provider models (e.g., seedream-4.5) where:
- Boolean/integer params with "image" in name were misclassified as image inputs
- Array params without items.type specification were incorrectly rejected
- Handle ID mismatch between node components (image-0) and store (image)
- dynamicInputs not passed to API in node regeneration code path
Changes:
- Update isImageInput() to check schema types, not just names
- Map both indexed (image-0) and legacy (image) handle IDs to schema names
- Add dynamicInputs to nanoBanana regeneration request body
- Add hidden backward-compat handles for edge routing
- Add comprehensive tests for schema classification and handle mapping
- Fix Toast test to use specific selector for close button
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create server.js with extended requestTimeout (10 min) for long-running fal.ai video generation
- Update package.json dev script to use custom server
- Add comments explaining why queue API is not used (file size limitations)
- Use WHATWG URL API instead of deprecated url.parse()
- Added support for long-running video generation using a queue system with a 10-minute timeout.
- Updated maxDuration constant to reflect the new timeout for video generation.
- Enhanced API request handling to accommodate video generation, including error handling and polling for completion.
- Updated model retrieval tests to reflect the inclusion of additional models and improved provider filtering.
- Modified component tests to track model usage and reflect changes in the UI for provider selection.
When providers like fal.ai or Replicate expect array parameters (e.g.,
image_urls), single values from dynamicInputs are now automatically
wrapped in arrays based on the OpenAPI schema's type property.
Changes:
- Add isArray field to ModelInput type for schema detection
- Update getFalInputMapping() to return schemaArrayParams
- Update getInputMappingFromSchema() to return schemaArrayParams
- Apply array wrapping in both fal.ai and Replicate handlers
- Add tests for array wrapping behavior
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add GEMINI_IMAGE_MODELS constant with nano-banana and nano-banana-pro
- Update GET handler to include Gemini models in response
- Support provider=gemini filter (returns only Gemini models)
- Gemini models appear first in 'all providers' results
- No API key needed for Gemini (always available)
Add tests verifying the parameters field (UI settings) is passed to
provider APIs:
- Replicate: parameters passed to prediction input
- Replicate: dynamicInputs take precedence when merging
- fal.ai: parameters passed to request body
- fal.ai: dynamicInputs take precedence when merging
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive tests for /api/models route covering:
- Basic functionality: fal.ai without key, both providers with keys
- Provider filtering by query param
- Capabilities filtering
- Search query param (client-side for Replicate, server-side for fal.ai)
- Caching behavior: cache hits, cache misses, refresh bypass
- Error handling: partial provider failures, all providers failing
- Pagination for both Replicate and fal.ai (max 15 pages)
- Capability inference for Replicate models (video keywords, i2v keywords)
- fal.ai category mapping to ModelCapability
- Model sorting by provider then name
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive tests for /api/generate fal.ai path:
- Image generation success (images array response)
- Video generation success (video object response)
- Works without API key (unauthenticated allowed)
- Rate limit (429) handling with and without key
- Various response formats (image, images, video, output)
- Large video (>20MB) returns URL instead of base64
- Empty dynamicInputs values are filtered
- Dynamic inputs passed to fal.ai request
- Environment variable API key fallback
- Error response formats (error.message, detail array)
- No media URL in response handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive tests for /api/generate Replicate path:
- Image generation success with API key header
- Video generation success
- 401 when API key missing
- Rate limit (429) handling
- Prediction failure handling
- Prediction timeout (5 min max)
- Polling for prediction completion
- Large video (>20MB) returns URL instead of base64
- Dynamic inputs passed to prediction
- Environment variable API key fallback
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests for input validation and edge cases:
- Reject requests with no prompt, images, or dynamic inputs
- Accept image-to-image (images only, no prompt)
- Accept dynamicInputs with prompt, image frames, and image_url
- Handle multiple images in request
Add provider routing tests:
- Route to Gemini when no selectedModel provided
- Route to Gemini when selectedModel.provider is gemini
- Return 401 for Replicate without API key
Add response handling tests:
- Verify proper response structure
- Handle default MIME type fallback
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive tests for /api/generate route covering Gemini path:
- Success with prompt only and with images
- Config options (aspectRatio, resolution, googleSearch)
- Model mapping (nano-banana, nano-banana-pro)
- API key handling (env var vs header)
- Error cases (missing key, rate limits, no candidates, text-only response)
- MIME type extraction from data URLs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests Google/Gemini provider path covering success, validation, rate limits,
and API error handling. Uses vi.hoisted for proper mock class constructor.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Always render image/text handles with placeholders for unused inputs
- Placeholder handles (dimmed 30%) preserve connections for models that
don't use that input type (e.g., text-to-image models)
- Normalize dynamic handle IDs to standard values (image, text, image-0)
- Update findCompatibleHandle to return normalized IDs
- Map normalized handles to schema names in getConnectedInputs
- Exclude image_size from image input detection
- Switch save-generation to hash suffix format for better readability
Co-Authored-By: Claude <noreply@anthropic.com>
- Add MD5 hashing of buffer content for deduplication
- Check for existing file by hash prefix before saving
- Use hash-prefixed filenames: {hash}_{prompt}.{ext}
- Add isDuplicate field to response JSON
- Log deduplication events for observability
- Fix isImageInput() to use word-boundary checks instead of substring
matching, preventing num_images from being misclassified as image input
- Fix stale node data in workflow execution by getting fresh data from
store instead of using captured array references
- Add missing parameters to regenerateNode request bodies for both
nanoBanana and generateVideo nodes
- Update fal.ai schema fetch to use Model Search API endpoint
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Group file operations (save, open folder) together with subtle separator
- Separate settings button with visual spacing
- Add hover background states to icon buttons for better interactivity
- Improve unsaved indicator with ring outline for better visibility
- Use consistent padding (p-1.5) and rounded corners
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add support for multiple file extensions (png, jpg, jpeg, webp, gif, mp4, webm, mov)
- Search for file by ID with any supported extension
- Return appropriate MIME type based on actual file extension
- Return video field for video files, image field for image files
- Include contentType field in response to indicate type
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Updated .env.example to include new API keys for OpenAI, Replicate, and fal.ai.
- Modified API routes to prioritize user-provided API keys over environment variables for Gemini and OpenAI.
- Improved error messages for missing API keys to guide users on configuration.
- Enhanced ProjectSetupModal to support OpenAI provider and manage API key overrides.
- Updated workflow store to handle new provider settings and API key integration for OpenAI.
- Added functionality to auto-save generated images and videos to the specified directory.
- Introduced a model search dialog for selecting external models in GenerateImageNode and GenerateVideoNode.
- Updated node titles to reflect selected models dynamically.
- Improved parameter handling by expanding the ModelParameters component to notify parent nodes on schema load.
- Adjusted handle positions and styles for better alignment and visual consistency.
- Enhanced input validation and processing for dynamic inputs across the workflow.
- Handle colors: cyan (#22d3ee) for image, magenta (#d946ef) for text
- Labels positioned outside node boundary (left of inputs, right of outputs)
- Label text color matches handle color
- Inputs sorted: image handles at top, text handles at bottom
- Visual gap between image and text input groups
- Added CSS variables for handle colors (used by label styling)
- Output handles also have colored labels (Video/Image)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Render dynamic input handles from model schema (GenerateImage/VideoNode)
- Update connection validation with getHandleType() helper
- Add dynamicInputs to getConnectedInputs() return value
- Pass dynamicInputs through generation pipeline (workflowStore -> API)
- Update generateWithFal/Replicate to use dynamic inputs
- Process large images in dynamicInputs to URLs for external providers
- Support prompt from dynamicInputs when top-level prompt is empty
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ModelInput type for connectable inputs (image/text handles)
- Update model schema API to return both parameters and inputs
- Categorize properties: image inputs, text inputs, and parameters
- Sort inputs by required status and type
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use 'models' array instead of 'data' array
- Iterate paths to find POST endpoint (not always at '/')
- Resolve $ref schema references from components.schemas
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous /api endpoint returned 404 for all models. Now using
the correct Model Search API with OpenAPI expansion:
GET https://api.fal.ai/v1/models?endpoint_id={modelId}&expand=openapi-3.0
Also added fallback path traversal for alternate OpenAPI structures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Many fal.ai models don't expose OpenAPI schemas at /api endpoint.
Return empty parameters instead of error so generation still works
and UI doesn't show error message.
- Add modelsFetchError state with retry button in GenerateImage/VideoNode
- Show clear errors when model list fails to load (API key issues, connection)
- Improve API route error messages to include model name
- Add rate limit handling (429) with user-friendly messages
- Parse provider-specific error responses for better detail
- Improve timeout message for video generation
- Include parameters in requestPayload for nanoBanana and generateVideo nodes
- Add logging for custom parameters in Replicate and fal.ai provider functions
- Parameters already spread via ...input.parameters in existing provider code
- Create /api/models/[modelId] endpoint for fetching model parameter schemas
- Add ModelParameter interface to lib/providers/types.ts
- Support both Replicate and fal.ai providers
- Filter out internal params, prioritize user-relevant ones
- 10-minute cache TTL for schema responses
Extend /api/generate to support video output from Replicate and fal.ai:
- Detect video content type in Replicate response
- Handle fal.ai video.url field for video model responses
- Return video/videoUrl fields with contentType for videos
- Add generateVideo case to executeWorkflow and regenerateNode
- Handle large videos (>20MB) by returning URL directly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import uploadImageForUrl, shouldUseImageUrl, deleteImages from @/lib/images
- Process images for Replicate/fal providers before generation
- Small images (<256KB) use data URIs directly
- Large images converted to temporary URLs via /api/images/[id]
- Cleanup uploaded images in finally block (success or failure)
- Gemini path unchanged (handles base64 directly)
- Add image parameter to predictionInput when images array provided
- Log image input presence for debugging
- Uses 'image' param name (most common for img2img models)
- Replicate's search API endpoint unreliable, use client-side filtering
- Always cache full model list for Replicate (not per-search-query)
- Apply search filter on cached models for faster subsequent searches
- Remove unused ReplicateSearchResponse and ReplicateSearchResult types
Refactor generate API route to support multiple providers:
- Accept both legacy format and new format with selectedModel
- Route requests to Replicate, fal.ai, or Gemini based on provider
- Extract Gemini logic into generateWithGemini() helper function
- Add generateWithReplicate() and generateWithFal() server-side helpers
- Get provider API keys from request headers (X-Replicate-API-Key, X-Fal-API-Key)
- Preserve backward compatibility for existing Gemini-based workflows
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Video models were getting text-to-image capability by default.
Now properly detects video models (by keywords like video, animate,
motion, luma, kling, minimax) and only assigns video capabilities.
Both APIs return paginated results but we were only fetching page 1.
Now fetches up to 5 pages from Replicate (~125 models) and 10 pages
from fal.ai to get more complete model lists.
UAT-005: Video models were showing in image-only dropdown because
the API wasn't filtering by capabilities param. Now filters results
based on ?capabilities=text-to-image,image-to-image query param.
UAT-004: Replicate shows first page only (~25 models) - this is
expected API behavior. Full pagination can be added later.
Add /api/models endpoint that aggregates models from all configured
providers. Supports caching, search, provider filtering, and handles
partial failures gracefully.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add defensive null checks before calling .map() on data.results
in both the API route and provider implementation. Returns empty
array with success:true when results is undefined, preventing
"Cannot read properties of undefined (reading 'map')" crash.
Fixes: UAT-001 from 02-01-ISSUES.md
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
fal.ai API only accepts a single category parameter, but we were
sending multiple categories. Now we fetch all active models and
filter client-side using the existing isRelevantModel() function.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Next.js API route for server-side fal.ai model fetching:
- GET /api/providers/fal/models endpoint
- Optional API key auth (fal.ai works without but rate limited)
- Filter to image/video categories only
- Normalize response to ProviderModel interface
- Support search query parameter
- Follow established pattern from replicate API route
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- GET /api/providers/replicate/models endpoint
- Accepts X-API-Key header or api_key query param
- Optional search param for model search
- Maps Replicate models to ProviderModel interface
- Infers capabilities from model name/description
- Returns { success, models } or { success, error }
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>