From 913e6ee697e29ea8aa49c7d816d5b1ccd2e5283f Mon Sep 17 00:00:00 2001 From: shrimbly Date: Sun, 11 Jan 2026 19:58:39 +1300 Subject: [PATCH] fix(06-04): use fal.ai Model Search API for parameter schema 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 --- src/app/api/models/[modelId]/route.ts | 57 ++++++++++++++++++++------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/app/api/models/[modelId]/route.ts b/src/app/api/models/[modelId]/route.ts index c094b211..cf628870 100644 --- a/src/app/api/models/[modelId]/route.ts +++ b/src/app/api/models/[modelId]/route.ts @@ -192,7 +192,8 @@ async function fetchReplicateSchema( } /** - * Fetch and parse schema from fal.ai + * Fetch and parse schema from fal.ai using Model Search API + * Uses: GET https://api.fal.ai/v1/models?endpoint_id={modelId}&expand=openapi-3.0 */ async function fetchFalSchema( modelId: string, @@ -203,32 +204,58 @@ async function fetchFalSchema( headers["Authorization"] = `Key ${apiKey}`; } - // Fetch OpenAPI spec from /api endpoint - // Note: Not all fal.ai models expose this endpoint, so 404 is expected for some - const response = await fetch(`https://fal.run/${modelId}/api`, { headers }); + // Use fal.ai Model Search API with OpenAPI expansion + const url = `https://api.fal.ai/v1/models?endpoint_id=${encodeURIComponent(modelId)}&expand=openapi-3.0`; + console.log(`[fetchFalSchema] Fetching schema from: ${url}`); + + const response = await fetch(url, { headers }); if (!response.ok) { - // 404 is common - many fal.ai models don't expose OpenAPI schema - // Return empty params rather than error so generation can still work - if (response.status === 404) { - console.log(`[fetchFalSchema] Model ${modelId} does not expose OpenAPI schema (404)`); - return []; - } - throw new Error(`fal.ai API error: ${response.status}`); + // Return empty params if API fails so generation still works + console.log(`[fetchFalSchema] Model Search API returned ${response.status}`); + return []; + } + + const data = await response.json(); + + // Response is { data: [{ openapi: {...}, ... }] } + const modelData = data.data?.[0]; + if (!modelData?.openapi) { + console.log(`[fetchFalSchema] No OpenAPI schema in response for ${modelId}`); + return []; } - const spec = await response.json(); + const spec = modelData.openapi; // Navigate to request body schema // Path: paths["/"]["post"].requestBody.content["application/json"].schema const requestBodySchema = spec.paths?.["/"]?.post?.requestBody?.content?.["application/json"]?.schema; if (!requestBodySchema) { - return []; + // Try alternate path structure + const postPath = Object.values(spec.paths || {})[0] as Record | undefined; + const postOp = postPath?.post as Record | undefined; + const reqBody = postOp?.requestBody as Record | undefined; + const content = reqBody?.content as Record> | undefined; + const jsonContent = content?.["application/json"]; + + if (!jsonContent?.schema) { + console.log(`[fetchFalSchema] Could not find request body schema in OpenAPI spec`); + return []; + } + + return extractParametersFromSchema(jsonContent.schema as Record); } - const properties = requestBodySchema.properties as Record> | undefined; - const required = (requestBodySchema.required as string[]) || []; + return extractParametersFromSchema(requestBodySchema as Record); +} + +/** + * Extract ModelParameters from an OpenAPI schema object + */ +function extractParametersFromSchema(schema: Record): ModelParameter[] { + const properties = schema.properties as Record> | undefined; + const required = (schema.required as string[]) || []; if (!properties) { return [];