From ac6e953d3be0ac8aa4b4b2be97c153f8bd8939db Mon Sep 17 00:00:00 2001 From: shrimbly Date: Sat, 17 Jan 2026 20:10:25 +1300 Subject: [PATCH] fix(24-03): remove fal.ai pricing API calls causing 429 errors --- .planning/ROADMAP.md | 17 +++---- .planning/STATE.md | 15 ++++--- src/app/api/models/route.ts | 89 +------------------------------------ 3 files changed, 19 insertions(+), 102 deletions(-) diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index f6f0c6ab..b0676c00 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -367,20 +367,21 @@ Plans: - [x] 25-01: Template types, TemplateCard component, TemplateExplorerView grid layout - [x] 25-02: Sidebar filters (search, category, tags), WelcomeModal integration -#### Phase 26: Template Preview Rendering +#### Phase 26: Template Preview Rendering ✅ **Goal**: Visual preview of workflow templates showing node layout and connections before loading **Depends on**: Phase 25 **Research**: Unlikely (React Flow rendering, existing patterns) -**Plans**: TBD +**Plans**: 1 plan -**Features:** -- Miniature workflow visualization in preview panel -- Show nodes, connections, and overall structure -- Non-interactive preview (view only) +**Final Implementation:** +- Horizontal card layout with thumbnail image, details, and "Use workflow" button +- Two-column grid layout for better scanning +- Conditional dialog width (6xl for explorer, 2xl for other views) +- Direct workflow loading without intermediate modal Plans: -- [ ] 26-01: TBD (run /gsd:plan-phase 26 to break down) +- [x] 26-01: Horizontal cards with direct workflow loading, conditional dialog sizing #### Phase 27: Node Defaults Infrastructure @@ -456,7 +457,7 @@ Phases execute in numeric order: 1 → 2 → ... → 24 → 25 → 26 → 27 → | 23. Model Browser Improvements | v1.2 | 1/1 | Complete | 2026-01-13 | | 24. Improved Cost Summary | v1.2 | 2/3 | In progress | - | | 25. Template Explorer UI | v1.3 | 2/2 | Complete | 2026-01-16 | -| 26. Template Preview Rendering | v1.3 | 0/? | Not started | - | +| 26. Template Preview Rendering | v1.3 | 1/1 | Complete | 2026-01-17 | | 27. Node Defaults Infrastructure | v1.3 | 0/? | Not started | - | | 28. Node Defaults UI | v1.3 | 0/? | Not started | - | | 29. Canvas Performance | v1.3 | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index c3811cc9..1fde7bf4 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -9,12 +9,12 @@ See: .planning/PROJECT.md (updated 2026-01-09) ## Current Position -Phase: 25 of 29 (Template Explorer UI) -Plan: 2 of 2 in current phase +Phase: 26 of 29 (Template Preview Rendering) +Plan: 1 of 1 in current phase Status: Complete -Last activity: 2026-01-16 - Completed 25-02-PLAN.md +Last activity: 2026-01-17 - Completed 26-01-PLAN.md -Progress: █████████░ 90% +Progress: █████████░ 92% ## Performance Metrics @@ -49,6 +49,7 @@ Progress: █████████░ 90% | 20. Integration Tests | 2/2 | 11 min | 5.5 min | | 24. Improved Cost Summary | 2/3 | 10 min | 5 min | | 25. Template Explorer UI | 2/2 | 23 min | 11.5 min | +| 26. Template Preview Rendering | 1/1 | 25 min | 25 min | **Recent Trend:** - Last 5 plans: 5 min, 9 min, 2 min, 8 min, 15 min @@ -152,7 +153,7 @@ Recent decisions affecting current work: ## Session Continuity -Last session: 2026-01-16 -Stopped at: Completed 25-02-PLAN.md (sidebar filters and WelcomeModal integration) +Last session: 2026-01-17 +Stopped at: Completed 26-01-PLAN.md (horizontal cards with direct workflow loading) Resume file: None -Next action: Execute 24-03-PLAN.md (CostDialog UI) or Phase 26 (Template Preview Rendering) +Next action: Execute 24-03-PLAN.md (CostDialog UI) or Phase 27 (Node Defaults Infrastructure) diff --git a/src/app/api/models/route.ts b/src/app/api/models/route.ts index d3b74fe9..b3840d48 100644 --- a/src/app/api/models/route.ts +++ b/src/app/api/models/route.ts @@ -124,18 +124,6 @@ interface FalModel { openapi?: Record; } -interface FalPricingResponse { - prices: FalPrice[]; - has_more: boolean; - next_cursor: string | null; -} - -interface FalPrice { - endpoint_id: string; - unit_price: number; - unit: string; // "image", "video", "second", etc. - currency: string; -} // ============ Response Types ============ @@ -293,68 +281,6 @@ function mapFalModel(model: FalModel): ProviderModel { }; } -/** - * Fetch pricing for a list of fal.ai endpoint IDs - * Returns a Map of endpoint_id -> pricing info - * Best-effort: errors are logged but don't fail the request - */ -async function fetchFalPricing( - endpointIds: string[], - apiKey: string | null -): Promise> { - const pricingMap = new Map(); - - if (endpointIds.length === 0) { - return pricingMap; - } - - const headers: HeadersInit = {}; - if (apiKey) { - headers["Authorization"] = `Key ${apiKey}`; - } - - try { - // Batch endpoint IDs (API supports up to ~50 at once based on URL length limits) - const batchSize = 50; - for (let i = 0; i < endpointIds.length; i += batchSize) { - const batch = endpointIds.slice(i, i + batchSize); - const endpointIdsParam = batch.join(","); - const url = `${FAL_API_BASE}/models/pricing?endpoint_id=${encodeURIComponent(endpointIdsParam)}`; - - const response = await fetch(url, { headers }); - - if (!response.ok) { - console.warn(`[Models] fal.ai pricing API error: ${response.status}`); - continue; - } - - const data: FalPricingResponse = await response.json(); - - for (const price of data.prices) { - // Map fal.ai units to our pricing type - // "image" -> per-run (single generation) - // "video", "second" -> per-second (duration-based) - const pricingType: "per-run" | "per-second" = - price.unit === "image" ? "per-run" : "per-second"; - - pricingMap.set(price.endpoint_id, { - type: pricingType, - amount: price.unit_price, - currency: price.currency, - }); - } - } - } catch (error) { - // Best-effort: log warning but don't fail - console.warn( - `[Models] Failed to fetch fal.ai pricing:`, - error instanceof Error ? error.message : "Unknown error" - ); - } - - return pricingMap; -} - async function fetchFalModels( apiKey: string | null, searchQuery?: string @@ -395,19 +321,8 @@ async function fetchFalModels( pageCount++; } - // Fetch pricing for all models (best-effort) - if (allModels.length > 0) { - const endpointIds = allModels.map((m) => m.id); - const pricingMap = await fetchFalPricing(endpointIds, apiKey); - - // Merge pricing into models - for (const model of allModels) { - const pricing = pricingMap.get(model.id); - if (pricing) { - model.pricing = pricing; - } - } - } + // Note: Pricing not fetched - external provider pricing is unreliable + // CostDialog shows model links instead of prices for fal.ai/Replicate return allModels; }