Popi/NewAPIWG is now the primary gateway, so the UI and request paths need to avoid legacy provider assumptions while keeping local reference media usable for upstream calls. This change aligns model defaults, point display, inline node controls, and temporary reference image handling around that gateway boundary.
Constraint: Upstream providers cannot fetch localhost reference URLs, but large base64 payloads previously caused request/body reliability issues.
Constraint: Seedance point estimates depend on authenticated NewAPIWG /api/points/estimate responses rather than static catalog prices.
Rejected: Send all reference images directly as large data URLs | would reintroduce oversized JSON/request failures.
Rejected: Restore legacy per-provider key controls by default | current product direction is a single Popi/NewAPI gateway.
Confidence: high
Scope-risk: moderate
Directive: Do not pass /api/images localhost URLs to upstream generation APIs; resolve them server-side or fail before the upstream call.
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts src/app/api/points/estimate/__tests__/route.test.ts src/lib/images/store.test.ts src/components/__tests__/GenerationComposer.test.tsx src/components/__tests__/ImageInputNode.test.tsx src/components/__tests__/PromptNode.test.tsx src/components/__tests__/ProjectSetupModal.test.tsx src/store/execution/__tests__/generateVideoExecutor.test.ts src/store/execution/__tests__/nanoBananaExecutor.test.ts src/store/utils/__tests__/nodeDefaults.test.ts
Tested: npm run build
Tested: git diff --check
Not-tested: npm run lint fails because next lint is parsed as a missing /lint project directory under Next 16.
Multi-image NewAPIWG generations were sending base64 reference images
inside the /api/generate JSON body. Two ordinary canvas references can
push that payload near the dev/server body boundary, leaving the route
to parse an incomplete JSON string and surface a raw syntax error.
Move large NewAPIWG references through the existing in-memory image
serving path: the browser uploads large data URLs as multipart image
files, sends short /api/images/{id} URLs in the generation request, and
the route resolves those temporary URLs back to server-local image data
before calling providers. The route also turns malformed/truncated JSON
into a readable validation error.
Constraint: Keep provider payload semantics unchanged after /api/generate receives the request
Constraint: Do not rely on request origin matching because Next dev can report 0.0.0.0 while the browser uses localhost
Rejected: Increase body size limits only | still sends multi-MB JSON and does not protect hosted/proxied environments
Rejected: Compress reference images client-side | changes model input fidelity and still leaves large JSON possible
Confidence: high
Scope-risk: moderate
Directive: Do not forward /api/images/{id} temporary URLs to external providers; resolve them server-side first
Tested: npm run test:run -- src/store/execution/__tests__/nanoBananaExecutor.test.ts src/app/api/generate/__tests__/route.test.ts
Tested: npm run build
Not-tested: Live NewAPIWG generation with the user's exact two images
The welcome screen now matches the Popi.TV brand and gives unauthenticated users two explicit paths: jump to the main-site login page or provide a Popi/NewAPI gateway key directly. The manually provided key is persisted in the local user store and forwarded through the NewAPI-backed generation, model, LLM, and chat routes without requiring a login token.
Constraint: Users need a usable local path when they are not logged into the main site.\nRejected: Keep the startup login modal for missing tokens | it blocked the in-welcome login/key choices.\nRejected: Rename internal workflow prompts and storage keys | those are compatibility/internal identifiers, not the visible brand surface.\nConfidence: high\nScope-risk: moderate\nDirective: Keep manual gateway-key access scoped to NewAPI-backed paths; non-NewAPI providers should continue requiring a login token.\nTested: npm run test:run -- src/app/api/models/__tests__/route.test.ts src/app/api/models/[modelId]/__tests__/route.test.ts src/app/api/llm/__tests__/route.test.ts src/app/api/chat/__tests__/route.test.ts src/app/api/generate/__tests__/route.test.ts\nTested: npm run test:run -- src/components/__tests__/QuickstartInitialView.test.tsx src/components/__tests__/WelcomeModal.test.tsx\nTested: npm run build\nTested: Browser verification for Popi.TV welcome title and two unauthenticated entry paths\nTested: git diff --cached --check\nNot-tested: Full test suite\nNot-tested: npm run lint still fails because the existing Next 16 next lint script resolves /lint as a project directory
This keeps the testing branch aligned with the current Popi.TV interaction direction: generation is controlled from the selected node context, model/provider selection is simplified around the NewAPI gateway path, Popiai embedding can hydrate workflow state, and media nodes expose names plus dimensions in the canvas header.
Constraint: Testing branch needs the full current source state, not runtime logs or local browser artifacts
Rejected: Commit logs, browserfs state, and handoff bundles | they are local runtime artifacts rather than application source
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep model selection consolidated around the NewAPI gateway unless supplier-specific behavior is intentionally reintroduced
Tested: npm run test:run -- src/components/__tests__/FloatingNodeHeader.test.tsx src/components/__tests__/nodeHeaderMedia.test.ts src/components/__tests__/WorkflowCanvas.test.tsx
Tested: npm run test:run -- src/components/__tests__/ImageInputNode.test.tsx src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/GenerateImageNode.test.tsx
Tested: npm run build
Tested: git diff --check -- src
Not-tested: npm run lint | existing script uses next lint and resolves to nonexistent ./lint directory
Seedream rejects image generation requests whose size resolves below its minimum pixel floor. The NewApiWG adapter now clamps Seedream pixel dimensions to the existing 2K area floor while preserving the requested aspect ratio and leaving non-Seedream image models unchanged.
Constraint: Provider error requires image size to be at least 3686400 pixels
Rejected: Change the composer default resolution | would affect unrelated image models and UI behavior
Confidence: high
Scope-risk: narrow
Directive: Keep Seedream size normalization in the provider adapter so UI controls remain provider-agnostic
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts
Tested: npm run test:run -- src/app/api/generate/__tests__/route.test.ts -t NewApiWG provider
Not-tested: npm run lint currently fails because next lint is parsed as a missing lint directory
Not-tested: npx tsc --noEmit currently fails on pre-existing test type errors
Gemini native image edits can default to the input image dimensions unless the response format also carries the requested image shape. The NewApiWG Gemini payload now sends the existing imageConfig plus responseFormat.image so Popi 16:9 selections survive image-to-image generation while preserving the prior native config path.
Constraint: Gemini image edit defaults can keep the input image dimensions when output format is not explicit
Rejected: Changing composer or node sizing first | the selected aspect ratio already reached the API payload in tests
Confidence: medium
Scope-risk: narrow
Directive: Keep imageConfig and responseFormat in sync for Gemini-native NewApiWG image models
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts src/app/api/generate/__tests__/route.test.ts
Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build
Seedream 5.0 rejects the previous default 1024x1024 size when the user selects high resolution output. The NewApiWG image endpoint now derives a provider pixel size from the visible resolution and aspect-ratio controls unless the model schema supplied an explicit size.
Constraint: Keep the fix server-side and preserve explicit model size parameters when provided.
Rejected: Change the composer resolution UI for one model | the UI already sends the intended 4K and 16:9 values; the loss happened in provider payload mapping.
Confidence: high
Scope-risk: narrow
Directive: Do not default OpenAI-compatible image endpoints to 1024x1024 when resolution/imageSize is present.
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts
Tested: npm run test:run -- src/app/api/generate/__tests__/route.test.ts
Tested: npm run build
Tested: git diff --check
Not-tested: Live Seedream 5.0 paid generation call.
P0/P1 reports clustered around unavailable LLM defaults, video media connections, and missing controls on the generation surface. This routes the default NewApiWG LLM away from the unavailable Doubao channel, makes video a first-class schema and canvas connection type, carries request ids into node errors, and fills the composer, trim, audio, and 3D gaps without changing the canvas workflow model.
Constraint: NewApiWG Doubao channels are not reliably routed in the default gateway
Constraint: Video model schemas expose video fields that must not flow through image handles
Rejected: Continue accepting video through image handles | hides incorrect graph types and loses schema-specific dynamic inputs
Confidence: high
Scope-risk: moderate
Directive: Keep media handle types explicit; add schema mapping when adding a new connectable media type
Tested: npm run test:run -- src/store/execution/__tests__/generateVideoExecutor.test.ts src/store/utils/__tests__/connectedInputs.test.ts src/store/execution/__tests__/generateAudioExecutor.test.ts src/store/execution/__tests__/llmGenerateExecutor.test.ts src/store/__tests__/workflowStore.integration.test.ts src/lib/__tests__/llmModels.test.ts src/store/utils/__tests__/nodeDefaults.test.ts src/components/__tests__/GenerationComposer.test.tsx src/app/api/chat/__tests__/route.test.ts src/app/api/llm/__tests__/route.test.ts src/app/api/models/[modelId]/__tests__/route.test.ts src/app/api/generate/providers/__tests__/newapiwg.test.ts
Tested: npm run build
Tested: git diff --check
Not-tested: npm run lint | package script calls removed Next 16 next lint command
Not-tested: npx tsc --noEmit | existing test type errors remain; changed runtime files filtered clean
NewApiWG model discovery was allowing Doubao text models to appear in media-generation contexts, which made image nodes call a model with no generation channel. The change filters Doubao chat/vision model names out of generation capability inference, bumps model caches, and guards execution for stale saved workflows.
Constraint: NewApiWG /models may return chat models without explicit capability metadata
Rejected: Keep all unknown models visible | it lets text-only models fail inside media nodes
Confidence: high
Scope-risk: narrow
Directive: Do not classify chat/VLM models as media generators without explicit generation capability metadata
Tested: npm test -- src/app/api/generate/providers/__tests__/newapiwg.test.ts
Tested: npm test -- src/components/__tests__/ModelSearchDialog.test.tsx
Not-tested: Full tsc remains blocked by existing unrelated test type errors
Image generation settings arrive at /api/generate as top-level aspectRatio and resolution fields, but the NewApiWG provider path only forwarded model-specific parameters. Native NewApiWG Gemini image generation therefore fell back to default imageConfig values and ignored composer/control-panel ratio choices. The route now merges top-level image settings into provider parameters while preserving explicit model parameter overrides.
Constraint: NewApiWG provider implementations read aspect ratio and image size from GenerationInput.parameters.
Rejected: Parse aspect ratio from prompt text | UI settings should be authoritative and prompts are ambiguous.
Confidence: high
Scope-risk: narrow
Directive: Keep top-level GenerateRequest image settings and provider parameters in sync for image providers.
Tested: npm test -- --run src/app/api/generate/__tests__/route.test.ts src/store/execution/__tests__/nanoBananaExecutor.test.ts src/components/__tests__/GenerationComposer.test.tsx
Tested: npx tsc --noEmit --pretty false filtered to generate route files
Not-tested: Full tsc due existing test mock overload errors in route.test.ts
This aligns image, video, and text defaults with the PopiArt NewApi gateway while keeping provider-specific escape hatches available. Chat now uses the OpenAI-compatible NewApiWG route so workflow editing and LLM generation share the same Doubao-first text default. The canvas composer and quick-add controls are tightened so visible actions map to real clickable behavior, while temporarily unused header actions stay hidden.
Constraint: Text/chat requests must prefer Doubao-Seed-2.0-lite through NewApiWG.
Constraint: Image and video defaults should prefer PopiArt NewApi gateway models when configured.
Rejected: Keep /api/chat on Gemini 3 Flash | conflicts with the requested NewApiWG text/chat default.
Rejected: Hand-roll AI SDK chat streaming | would risk tool-call and UI stream compatibility.
Confidence: high
Scope-risk: moderate
Directive: Do not add new text/chat defaults without updating src/lib/llmModels.ts and the chat route together.
Tested: npm test -- --run src/app/api/chat/__tests__/route.test.ts src/components/__tests__/LLMGenerateNode.test.tsx src/lib/__tests__/llmModels.test.ts src/store/utils/__tests__/nodeDefaults.test.ts src/components/__tests__/GenerationComposer.test.tsx src/lib/chat/tools.test.ts
Tested: npm test -- --run src/lib/__tests__/llmModels.test.ts src/store/utils/__tests__/nodeDefaults.test.ts src/components/__tests__/GenerationComposer.test.tsx src/lib/quickstart/__tests__/templates.test.ts src/lib/quickstart/__tests__/validation.test.ts src/lib/quickstart/__tests__/prompts.test.ts src/components/__tests__/ProjectSetupModal.test.tsx
Tested: npm test -- --run src/components/__tests__/WorkflowCanvas.test.tsx
Not-tested: Full suite; repository has pre-existing unrelated type/test warnings outside this change.
The bottom composer now edits and reruns the current generation node while canvas-visible actions own downstream node creation. NewApiWG media normalization, video frame extraction, and node quick-add keep generated media flows explicit and connected rather than hidden behind the prompt box.
Constraint: Composer must not call providers directly or create hidden downstream branches when a node is selected
Constraint: NewApiWG video families use inconsistent media schemas, so common media aliases are normalized in the provider adapter
Rejected: Let the bottom composer create next-step nodes from selected outputs | hides graph mutation and conflicts with the PRD source-of-truth rules
Rejected: Put NewApiWG model-family payload rules in the composer | provider-specific schemas would leak into UI state
Confidence: high
Scope-risk: moderate
Directive: Keep provider payload normalization in provider/executor layers; do not move it into GenerationComposer
Tested: npm run test:run -- src/components/__tests__/GenerationComposer.test.tsx
Tested: npm run test:run -- src/components/__tests__/WorkflowCanvas.test.tsx
Tested: npm run test:run -- src/components/__tests__/GenerateVideoNode.test.tsx
Tested: npm run test:run -- src/app/api/generate/providers/__tests__/newapiwg.test.ts src/app/api/generate/__tests__/route.test.ts
Tested: npm run test:run -- src/store/execution/__tests__/videoProcessingExecutors.test.ts
Tested: npm run build
Not-tested: npm run lint fails because next lint is incompatible with the current Next 16 script configuration
NewApiWG image and video models need to work against the PopiArt test gateway, with generated remote media visible in the canvas and UI text localized for Chinese users. This wires the gateway-specific image/video paths, model fallback display, remote media proxying, and related i18n cleanup into one releaseable change.
Constraint: Test gateway exposes image and video models through mixed OpenAI-compatible and Gemini-native response shapes
Constraint: Browser video playback can fail when loading signed third-party media URLs directly
Rejected: Keep remote video URLs as direct <video> sources | CORS and range handling made successful generations appear unloaded
Confidence: high
Scope-risk: moderate
Directive: Do not remove the media proxy without validating remote video playback, trimming, and stitching against signed CDN URLs
Tested: npm run build
Tested: npx vitest run src/components/__tests__/OutputNode.test.tsx src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/VideoStitchNode.test.tsx
Tested: npx vitest run src/components/__tests__/WorkflowCanvas.test.tsx src/store/execution/__tests__/generateVideoExecutor.test.ts
Tested: npx vitest run src/app/api/generate/providers/__tests__/newapiwg.test.ts
Not-tested: Live browser replay after pushing to remote deployment
Kie rejects the call when both reference_image_urls and first_frame_url
(or last_frame_url) are populated — the API treats them as mutually
exclusive scene modes. Two changes:
1. kie.ts submit: skip the legacy image fallback whenever any
schema-driven image key was already handled via dynamicInputs. The
executor passes both `images` and `dynamicInputs`, so the fallback
was duplicating user-provided references into first_frame_url.
2. seedance2 I2V schema: mark first_frame_url optional and note the
mutual exclusivity in the handle descriptions so users see it in
the node UI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New endpoint accepts POST with taskId/provider/modelId and returns
either a polling-continue response or the final media result. Each
request is short-lived (~1s) instead of holding a connection open.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /api/generate route now submits Kie tasks and returns immediately
with polling metadata (taskId, pollProvider, etc.) instead of holding
the HTTP connection open for 2-10 minutes. Export buildMediaResponse
for reuse by the poll endpoint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split the monolithic generateWithKie into three reusable lifecycle functions
for client-side polling support. generateWithKie now composes them internally
for backward compatibility. Added polling fields to GenerateResponse type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap fetch and JSON parsing in try-catch to handle network errors,
add 429/5xx to retryable status codes, and implement exponential
backoff (2s → 10s) to reduce API load during long-running generations.
Caps consecutive errors at 5 before failing permanently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Kie API can return 404/422 or HTTP 200 with non-200 code when
polling starts before the task is fully registered. Previously these
were treated as fatal errors, causing the client to see a network error
even though the generation was still processing on Kie's side. Now
these transient responses are retried instead of aborting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
maxDuration was 5 minutes which caused Next.js to kill the route handler
before Seedance 2.0 video generation polling could complete, resulting
in a network error on the client despite successful generation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Kie API expects "bytedance/seedance-2" as the model value, but we
were sending our internal ID "bytedance/seedance-2/text-to-video" which
includes the capability suffix. Strip the suffix for Seedance models
before sending to the API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Kie docs have contradictory info — the parameter description says
"std"/"pro" but the example request body uses "720p"/"1080p". The API
actually accepts "720p"/"1080p" (same as Kling 2.6 Motion Control),
rejecting "std"/"pro" with "mode is not within the range of allowed
options."
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Per Kie docs, background_source accepts "input_video"/"input_image",
not "image"/"video". The previous wrong values were being sent to the
API after schema defaults pre-population, causing request rejection.
Also added background_source to getKieModelDefaults() for consistency.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The dynamic input upload logic only matched data:image URLs, so video
data (data:video/mp4) was passed as raw base64 instead of being
uploaded to get a hosted URL. This caused "File type not supported"
errors for models like Kling 3.0 Motion Control that accept video.
- Rename uploadImageToKie → uploadMediaToKie (keep alias for compat)
- Rename detectImageType → detectMediaType with video magic bytes and
fallback to declared MIME type for non-image content
- Broaden data URL checks from data:image to data: in generateWithKie
- Set uploadPath to videos/audio/images based on detected MIME type
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add Seedance 2.0 and 2.0 Fast with text-to-video and image-to-video
variants. Both support audio generation, web search, and 4-15 second
duration. Image input uses first_frame_url (singular string).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add first_frame_url, last_frame_url, and first_clip_url to the
singular-key checks in generateWithKie() so these params receive a
single string URL instead of being wrapped in an array.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Instead of an inline text field, negative prompt is now a proper text
handle on the node that can receive connections from prompt/LLM nodes.
Updates the API route to merge dynamicInputs.negative_prompt into the
Veo parameters, and aligns the model schema definition accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add early validation for image-to-video models when no image is provided
- Wrap Veo generation/polling in try/catch with specific error messages
- Add 60s AbortController timeout to video download fetch
- Fix SwitchNode toggle knob not sliding (peer-checked on non-sibling)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MAX_BASE64_SIZE check was returning the video URL with the API key
appended as a query parameter to the browser client for videos over 20MB.
Always encode to base64 instead since the buffer is already downloaded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Import generateWithGeminiVideo and route Veo model requests (modelId
starting with 'veo-') to the video provider before falling through
to the existing image generation path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Veo video generation using @google/genai SDK with:
- Model ID mapping (veo-3.1/veo-3.1-fast to API model IDs)
- Async generation with polling (10s intervals, 5min timeout)
- Image-to-video support with base64 image input
- Video download and base64 encoding (URL fallback for >20MB)
- Configurable aspect ratio, duration, resolution, negative prompt, seed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nano Banana 2 (gemini-3.1-flash-image) supports a new searchTypes API
that allows enabling web search and image search independently. This adds
a useImageSearch toggle visible only for Nano Banana 2, while keeping the
existing Google Search toggle using the legacy format for Nano Banana Pro.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Map nano-banana-2 to gemini-3.1-flash-image-preview API model.
Enable resolution control and Google Search grounding for nano-banana-2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 3 tests per provider (Replicate, fal.ai): prompt included with dynamicInputs,
no duplication when dynamicInputs already has prompt, existing non-dynamicInputs path works
- Mock fetch intercepts provider API calls and captures request bodies
- Verifies the fix from the previous commit prevents "prompt is required" errors