Browse Source
Phase 02: Model Discovery - DISCOVERY.md with Replicate and fal.ai API research - 02-01: Replicate provider implementation (2 tasks) - 02-02: fal.ai provider implementation (2 tasks) - 02-03: Model caching and unified endpoint (3 tasks) - 7 total tasks defined across 3 plans - Ready for execution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>handoff-20260429-1057
4 changed files with 638 additions and 0 deletions
@ -0,0 +1,133 @@ |
|||
--- |
|||
phase: 02-model-discovery |
|||
plan: 01 |
|||
type: execute |
|||
--- |
|||
|
|||
<objective> |
|||
Implement Replicate provider with model discovery from their REST API. |
|||
|
|||
Purpose: Enable dynamic fetching of available models from Replicate's API, normalized to our ProviderModel interface. |
|||
Output: Working Replicate provider that registers in the provider system and an API route for client access. |
|||
</objective> |
|||
|
|||
<execution_context> |
|||
~/.claude/get-shit-done/workflows/execute-phase.md |
|||
~/.claude/get-shit-done/templates/summary.md |
|||
</execution_context> |
|||
|
|||
<context> |
|||
@.planning/PROJECT.md |
|||
@.planning/ROADMAP.md |
|||
@.planning/STATE.md |
|||
@.planning/phases/02-model-discovery/DISCOVERY.md |
|||
|
|||
# Prior phase context: |
|||
@.planning/phases/01-provider-infrastructure/01-02-SUMMARY.md |
|||
|
|||
# Key files: |
|||
@src/lib/providers/types.ts |
|||
@src/lib/providers/index.ts |
|||
@src/types/index.ts |
|||
|
|||
**Tech stack available:** |
|||
- Provider abstraction interfaces (ProviderInterface, ProviderModel) |
|||
- Provider registry with registerProvider() |
|||
- ProviderType includes "replicate" |
|||
|
|||
**Established patterns:** |
|||
- Provider types in src/lib/providers/types.ts |
|||
- Self-registration via registerProvider() |
|||
- API routes return { success, error, data } JSON |
|||
|
|||
**Constraining decisions:** |
|||
- No SDKs - use direct fetch calls |
|||
- API keys from localStorage via provider settings |
|||
</context> |
|||
|
|||
<tasks> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 1: Create Replicate provider implementation</name> |
|||
<files>src/lib/providers/replicate.ts</files> |
|||
<action> |
|||
Create Replicate provider implementing ProviderInterface: |
|||
|
|||
1. Import types from @/lib/providers and @/types |
|||
2. Implement helper function to get API key from localStorage (client) or return null (server) |
|||
3. Implement listModels(): |
|||
- Fetch GET https://api.replicate.com/v1/models with Bearer auth |
|||
- Map results to ProviderModel[] (id: `${owner}/${name}`, name, description, provider: "replicate", coverImage: cover_image_url) |
|||
- Infer capabilities from model name/description (text-to-image default, add image-to-image if "img2img" in name/description) |
|||
- Return first page only (no pagination traversal) |
|||
4. Implement searchModels(query): |
|||
- Fetch GET https://api.replicate.com/v1/search?query={query} with Bearer auth |
|||
- Map and return results same as listModels |
|||
5. Implement getModel(modelId): |
|||
- Parse modelId as owner/name |
|||
- Fetch GET https://api.replicate.com/v1/models/{owner}/{name} |
|||
- Map single result to ProviderModel |
|||
6. Implement generate() as stub returning { success: false, error: "Not implemented" } (Phase 3) |
|||
7. Implement isConfigured() checking if API key exists |
|||
8. Implement getApiKey() returning key from localStorage or null |
|||
9. Call registerProvider(replicateProvider) at module level |
|||
|
|||
Note: This runs client-side for isConfigured/getApiKey but API calls will be proxied through API route. |
|||
</action> |
|||
<verify>TypeScript compiles without errors: npx tsc --noEmit</verify> |
|||
<done>replicate.ts exports working provider, registered in registry when imported</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 2: Create Replicate models API route</name> |
|||
<files>src/app/api/providers/replicate/models/route.ts</files> |
|||
<action> |
|||
Create Next.js API route for fetching Replicate models server-side: |
|||
|
|||
1. Export GET handler |
|||
2. Get API key from request header or query param (client passes it) |
|||
3. Validate API key exists, return 401 if missing |
|||
4. Parse optional `search` query param |
|||
5. If search param: |
|||
- Fetch https://api.replicate.com/v1/search?query={search} |
|||
6. Else: |
|||
- Fetch https://api.replicate.com/v1/models |
|||
7. Both calls use Authorization: Bearer {apiKey} |
|||
8. Map response to ProviderModel[] using same logic as provider |
|||
9. Return { success: true, models: ProviderModel[] } |
|||
10. Handle errors: return { success: false, error: message } |
|||
|
|||
Pattern to follow from existing generate route: |
|||
- Use NextRequest, NextResponse |
|||
- Log request ID for debugging |
|||
- Handle fetch errors gracefully |
|||
</action> |
|||
<verify> |
|||
curl -X GET "http://localhost:3000/api/providers/replicate/models" -H "X-API-Key: test" returns JSON (will error without real key, but route responds) |
|||
</verify> |
|||
<done>API route returns model list when called with valid API key, 401 without key</done> |
|||
</task> |
|||
|
|||
</tasks> |
|||
|
|||
<verification> |
|||
Before declaring plan complete: |
|||
- [ ] `npx tsc --noEmit` passes |
|||
- [ ] `npm run build` succeeds |
|||
- [ ] Replicate provider file exists and exports provider |
|||
- [ ] API route file exists at correct path |
|||
- [ ] Provider registers when module is imported |
|||
</verification> |
|||
|
|||
<success_criteria> |
|||
|
|||
- Replicate provider implements ProviderInterface |
|||
- Provider self-registers via registerProvider() |
|||
- API route proxies model fetching with API key from header |
|||
- Models normalized to ProviderModel interface |
|||
- No TypeScript errors |
|||
</success_criteria> |
|||
|
|||
<output> |
|||
After completion, create `.planning/phases/02-model-discovery/02-01-SUMMARY.md` following the summary template. |
|||
</output> |
|||
@ -0,0 +1,140 @@ |
|||
--- |
|||
phase: 02-model-discovery |
|||
plan: 02 |
|||
type: execute |
|||
--- |
|||
|
|||
<objective> |
|||
Implement fal.ai provider with model discovery from their REST API. |
|||
|
|||
Purpose: Enable dynamic fetching of available models from fal.ai's API, normalized to our ProviderModel interface. |
|||
Output: Working fal.ai provider that registers in the provider system and an API route for client access. |
|||
</objective> |
|||
|
|||
<execution_context> |
|||
~/.claude/get-shit-done/workflows/execute-phase.md |
|||
~/.claude/get-shit-done/templates/summary.md |
|||
</execution_context> |
|||
|
|||
<context> |
|||
@.planning/PROJECT.md |
|||
@.planning/ROADMAP.md |
|||
@.planning/STATE.md |
|||
@.planning/phases/02-model-discovery/DISCOVERY.md |
|||
|
|||
# Prior plan context: |
|||
@.planning/phases/02-model-discovery/02-01-SUMMARY.md |
|||
|
|||
# Key files: |
|||
@src/lib/providers/types.ts |
|||
@src/lib/providers/index.ts |
|||
@src/lib/providers/replicate.ts |
|||
@src/types/index.ts |
|||
|
|||
**Tech stack available:** |
|||
- Provider abstraction interfaces (ProviderInterface, ProviderModel) |
|||
- Provider registry with registerProvider() |
|||
- ProviderType includes "fal" |
|||
- Replicate provider pattern established |
|||
|
|||
**Established patterns:** |
|||
- Provider implementation in src/lib/providers/{provider}.ts |
|||
- API route at src/app/api/providers/{provider}/models/route.ts |
|||
- Self-registration via registerProvider() |
|||
|
|||
**Constraining decisions:** |
|||
- No SDKs - use direct fetch calls |
|||
- API keys from localStorage via provider settings |
|||
</context> |
|||
|
|||
<tasks> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 1: Create fal.ai provider implementation</name> |
|||
<files>src/lib/providers/fal.ts</files> |
|||
<action> |
|||
Create fal.ai provider implementing ProviderInterface: |
|||
|
|||
1. Import types from @/lib/providers and @/types |
|||
2. Implement helper function to get API key from localStorage (client) or return null (server) |
|||
3. Implement listModels(): |
|||
- Fetch GET https://api.fal.ai/v1/models with optional Key auth header |
|||
- Filter to categories: text-to-image, image-to-image, text-to-video, image-to-video |
|||
- Map models to ProviderModel[]: |
|||
- id: endpoint_id |
|||
- name: metadata.display_name |
|||
- description: metadata.description |
|||
- provider: "fal" |
|||
- coverImage: metadata.thumbnail_url |
|||
- capabilities: [metadata.category as ModelCapability] |
|||
- Return first page only (no pagination traversal) |
|||
4. Implement searchModels(query): |
|||
- Fetch GET https://api.fal.ai/v1/models?q={query}&category=text-to-image (or combined categories) |
|||
- Map and return results same as listModels |
|||
5. Implement getModel(modelId): |
|||
- Fetch GET https://api.fal.ai/v1/models?endpoint_id={modelId} |
|||
- Return first result mapped to ProviderModel |
|||
6. Implement generate() as stub returning { success: false, error: "Not implemented" } (Phase 3) |
|||
7. Implement isConfigured() checking if API key exists (note: fal.ai works without key but with rate limits) |
|||
8. Implement getApiKey() returning key from localStorage or null |
|||
9. Call registerProvider(falProvider) at module level |
|||
|
|||
Note: fal.ai categories map directly to our ModelCapability type. |
|||
</action> |
|||
<verify>TypeScript compiles without errors: npx tsc --noEmit</verify> |
|||
<done>fal.ts exports working provider, registered in registry when imported</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 2: Create fal.ai models API route</name> |
|||
<files>src/app/api/providers/fal/models/route.ts</files> |
|||
<action> |
|||
Create Next.js API route for fetching fal.ai models server-side: |
|||
|
|||
1. Export GET handler |
|||
2. Get optional API key from request header (fal.ai works without but rate limited) |
|||
3. Parse optional `search` query param |
|||
4. Build fetch URL: |
|||
- Base: https://api.fal.ai/v1/models |
|||
- Add ?q={search} if search param provided |
|||
- Add category filter for image/video types only |
|||
5. Add Authorization header if API key provided: Key {apiKey} |
|||
6. Map response.models to ProviderModel[] using same logic as provider: |
|||
- Filter to relevant categories (text-to-image, image-to-image, text-to-video, image-to-video) |
|||
- Map endpoint_id, metadata.display_name, metadata.description, etc. |
|||
7. Return { success: true, models: ProviderModel[] } |
|||
8. Handle errors: return { success: false, error: message } |
|||
|
|||
Pattern to follow from Replicate API route (02-01). |
|||
</action> |
|||
<verify> |
|||
curl -X GET "http://localhost:3000/api/providers/fal/models" returns JSON with models array (works without key due to fal.ai's optional auth) |
|||
</verify> |
|||
<done>API route returns model list, works with or without API key, filters to image/video categories</done> |
|||
</task> |
|||
|
|||
</tasks> |
|||
|
|||
<verification> |
|||
Before declaring plan complete: |
|||
- [ ] `npx tsc --noEmit` passes |
|||
- [ ] `npm run build` succeeds |
|||
- [ ] fal.ai provider file exists and exports provider |
|||
- [ ] API route file exists at correct path |
|||
- [ ] Provider registers when module is imported |
|||
- [ ] Models filtered to image/video capabilities only |
|||
</verification> |
|||
|
|||
<success_criteria> |
|||
|
|||
- fal.ai provider implements ProviderInterface |
|||
- Provider self-registers via registerProvider() |
|||
- API route proxies model fetching (optional auth) |
|||
- Models normalized to ProviderModel interface |
|||
- Only image/video models returned (no audio, 3D, etc.) |
|||
- No TypeScript errors |
|||
</success_criteria> |
|||
|
|||
<output> |
|||
After completion, create `.planning/phases/02-model-discovery/02-02-SUMMARY.md` following the summary template. |
|||
</output> |
|||
@ -0,0 +1,168 @@ |
|||
--- |
|||
phase: 02-model-discovery |
|||
plan: 03 |
|||
type: execute |
|||
--- |
|||
|
|||
<objective> |
|||
Add in-memory caching for model lists and create unified models API endpoint. |
|||
|
|||
Purpose: Reduce API calls to external providers and provide single endpoint for fetching models from all configured providers. |
|||
Output: Caching layer with TTL and unified /api/models endpoint returning models from all providers. |
|||
</objective> |
|||
|
|||
<execution_context> |
|||
~/.claude/get-shit-done/workflows/execute-phase.md |
|||
~/.claude/get-shit-done/templates/summary.md |
|||
</execution_context> |
|||
|
|||
<context> |
|||
@.planning/PROJECT.md |
|||
@.planning/ROADMAP.md |
|||
@.planning/STATE.md |
|||
@.planning/phases/02-model-discovery/DISCOVERY.md |
|||
|
|||
# Prior plan context: |
|||
@.planning/phases/02-model-discovery/02-01-SUMMARY.md |
|||
@.planning/phases/02-model-discovery/02-02-SUMMARY.md |
|||
|
|||
# Key files: |
|||
@src/lib/providers/types.ts |
|||
@src/lib/providers/index.ts |
|||
@src/lib/providers/replicate.ts |
|||
@src/lib/providers/fal.ts |
|||
@src/types/index.ts |
|||
|
|||
**Tech stack available:** |
|||
- Provider abstraction interfaces |
|||
- Replicate and fal.ai providers implemented |
|||
- API routes for each provider |
|||
|
|||
**Established patterns:** |
|||
- Provider implementations in src/lib/providers/ |
|||
- API routes return { success, models/error } |
|||
|
|||
**Constraining decisions:** |
|||
- In-memory caching (no Redis) |
|||
- 5-15 minute cache TTL |
|||
</context> |
|||
|
|||
<tasks> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 1: Create model caching utility</name> |
|||
<files>src/lib/providers/cache.ts</files> |
|||
<action> |
|||
Create simple in-memory cache for model lists: |
|||
|
|||
1. Define CacheEntry interface: { data: T, timestamp: number } |
|||
2. Create cache Map: Map<string, CacheEntry<ProviderModel[]>> |
|||
3. Define DEFAULT_TTL = 10 * 60 * 1000 (10 minutes) |
|||
4. Export getCachedModels(key: string): ProviderModel[] | null |
|||
- Return null if not in cache or expired |
|||
- Return cached data if within TTL |
|||
5. Export setCachedModels(key: string, models: ProviderModel[]): void |
|||
- Store with current timestamp |
|||
6. Export invalidateCache(key?: string): void |
|||
- If key provided, delete that entry |
|||
- If no key, clear entire cache |
|||
7. Export getCacheKey(provider: ProviderType, search?: string): string |
|||
- Return `${provider}:models` or `${provider}:search:${search}` |
|||
|
|||
Keep it simple - no LRU eviction, no persistence. Server restarts clear cache. |
|||
</action> |
|||
<verify>TypeScript compiles without errors: npx tsc --noEmit</verify> |
|||
<done>cache.ts exports get/set/invalidate functions for model caching</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 2: Create unified models API endpoint</name> |
|||
<files>src/app/api/models/route.ts</files> |
|||
<action> |
|||
Create unified API route that fetches models from all configured providers: |
|||
|
|||
1. Export GET handler |
|||
2. Parse query params: |
|||
- provider: optional, filter to specific provider |
|||
- search: optional, search query |
|||
- refresh: optional, bypass cache if "true" |
|||
3. Get API keys from request headers: |
|||
- X-Replicate-Key: Replicate API key |
|||
- X-Fal-Key: fal.ai API key |
|||
4. Build list of providers to fetch from: |
|||
- If provider param, use only that provider (if key provided) |
|||
- Otherwise, fetch from all providers with keys |
|||
5. For each provider: |
|||
- Check cache first (unless refresh=true) |
|||
- If cache miss, fetch from provider's API route internally or call provider directly |
|||
- Store result in cache |
|||
6. Combine all provider results into single array |
|||
7. Sort by provider, then by name |
|||
8. Return { success: true, models: ProviderModel[], cached: boolean } |
|||
9. Handle partial failures: return models from successful providers, include errors array |
|||
|
|||
Response format: |
|||
{ |
|||
success: true, |
|||
models: ProviderModel[], |
|||
cached: boolean, |
|||
providers: { |
|||
replicate: { success: true, count: 50 }, |
|||
fal: { success: true, count: 100 } |
|||
}, |
|||
errors?: string[] |
|||
} |
|||
</action> |
|||
<verify> |
|||
curl -X GET "http://localhost:3000/api/models" -H "X-Fal-Key: test" returns combined model list (fal.ai works without key) |
|||
</verify> |
|||
<done>Unified endpoint returns models from all configured providers, uses caching, handles partial failures</done> |
|||
</task> |
|||
|
|||
<task type="auto"> |
|||
<name>Task 3: Add provider module exports</name> |
|||
<files>src/lib/providers/index.ts</files> |
|||
<action> |
|||
Update provider index to export cache utilities and ensure providers auto-register: |
|||
|
|||
1. Add import for cache utilities: export * from "./cache" |
|||
2. Add dynamic imports comment explaining that providers self-register |
|||
3. Export helper function listAllModels(apiKeys: Record<string, string>): Promise<ProviderModel[]> |
|||
- Takes API keys object |
|||
- Calls listModels() on each configured provider |
|||
- Returns combined array |
|||
4. Export helper function searchAllModels(query: string, apiKeys: Record<string, string>): Promise<ProviderModel[]> |
|||
- Same pattern but calls searchModels() |
|||
|
|||
Note: These helpers can be used by the unified API route. |
|||
</action> |
|||
<verify>TypeScript compiles without errors: npx tsc --noEmit</verify> |
|||
<done>Provider index exports cache utilities and helper functions for multi-provider operations</done> |
|||
</task> |
|||
|
|||
</tasks> |
|||
|
|||
<verification> |
|||
Before declaring plan complete: |
|||
- [ ] `npx tsc --noEmit` passes |
|||
- [ ] `npm run build` succeeds |
|||
- [ ] Cache utility exists with get/set/invalidate functions |
|||
- [ ] Unified /api/models endpoint works |
|||
- [ ] Caching prevents redundant API calls |
|||
- [ ] Multiple providers can be queried in single request |
|||
</verification> |
|||
|
|||
<success_criteria> |
|||
|
|||
- In-memory cache with 10-minute TTL |
|||
- Unified /api/models endpoint aggregates all providers |
|||
- Partial failure handling (some providers fail, others succeed) |
|||
- Cache bypass option for manual refresh |
|||
- Phase 2 complete - model discovery working for both providers |
|||
</success_criteria> |
|||
|
|||
<output> |
|||
After completion, create `.planning/phases/02-model-discovery/02-03-SUMMARY.md` following the summary template. |
|||
|
|||
Mark Phase 2 complete in summary. |
|||
</output> |
|||
@ -0,0 +1,197 @@ |
|||
# Phase 2 Discovery: Model Discovery APIs |
|||
|
|||
**Date:** 2026-01-09 |
|||
**Level:** 2 - Standard Research |
|||
|
|||
## Research Objectives |
|||
|
|||
1. Replicate model listing API endpoints and response schema |
|||
2. fal.ai model discovery endpoints and response schema |
|||
3. Pagination approaches for both providers |
|||
4. Authentication header formats |
|||
|
|||
## Findings |
|||
|
|||
### Replicate API |
|||
|
|||
**Endpoint:** `GET https://api.replicate.com/v1/models` |
|||
|
|||
**Authentication:** |
|||
``` |
|||
Authorization: Bearer $REPLICATE_API_TOKEN |
|||
``` |
|||
|
|||
**Query Parameters:** |
|||
- `sort_by`: Sort field (default: `latest_version_created_at`) |
|||
- `sort_direction`: `asc` or `desc` |
|||
|
|||
**Response Schema:** |
|||
```typescript |
|||
interface ReplicateModelsResponse { |
|||
next: string | null; // URL to next page |
|||
previous: string | null; // URL to previous page |
|||
results: ReplicateModel[]; |
|||
} |
|||
|
|||
interface ReplicateModel { |
|||
url: string; // Web page link |
|||
owner: string; // Username/organization |
|||
name: string; // Model identifier |
|||
description: string; // Text summary |
|||
visibility: "public" | "private"; |
|||
github_url?: string; |
|||
paper_url?: string; |
|||
license_url?: string; |
|||
run_count: number; // Usage statistics |
|||
cover_image_url?: string; // Thumbnail |
|||
default_example?: object; // Sample prediction |
|||
latest_version?: { |
|||
id: string; |
|||
openapi_schema: object; // Input/output specs |
|||
}; |
|||
} |
|||
``` |
|||
|
|||
**Pagination:** Cursor-based via `next`/`previous` URLs. Follow `next` URL until null. |
|||
|
|||
**Search Endpoint (Beta):** |
|||
``` |
|||
GET https://api.replicate.com/v1/search?query=... |
|||
``` |
|||
Returns broader search results across models. |
|||
|
|||
### fal.ai API |
|||
|
|||
**Endpoint:** `GET https://api.fal.ai/v1/models` |
|||
|
|||
**Authentication (Optional - higher rate limits):** |
|||
``` |
|||
Authorization: Key $FAL_KEY |
|||
``` |
|||
|
|||
**Query Parameters:** |
|||
| Parameter | Type | Description | |
|||
|-----------|------|-------------| |
|||
| `limit` | integer | Max items to return | |
|||
| `cursor` | string | Pagination cursor from previous response | |
|||
| `endpoint_id` | string/array | Specific endpoint ID(s) to retrieve | |
|||
| `q` | string | Free-text search query | |
|||
| `category` | string | Filter by category (e.g., 'text-to-image') | |
|||
| `status` | enum | `active` or `deprecated` | |
|||
| `expand` | string | Include `openapi-3.0` for full schema | |
|||
|
|||
**Response Schema:** |
|||
```typescript |
|||
interface FalModelsResponse { |
|||
models: FalModel[]; |
|||
next_cursor: string | null; |
|||
has_more: boolean; |
|||
} |
|||
|
|||
interface FalModel { |
|||
endpoint_id: string; // e.g., "fal-ai/flux/dev" |
|||
metadata: { |
|||
display_name: string; |
|||
category: string; // e.g., "text-to-image" |
|||
description: string; |
|||
status: "active" | "deprecated"; |
|||
tags: string[]; |
|||
updated_at: string; // ISO8601 |
|||
is_favorited: boolean | null; |
|||
thumbnail_url: string; |
|||
model_url: string; |
|||
date: string; // ISO8601 |
|||
highlighted: boolean; |
|||
pinned: boolean; |
|||
thumbnail_animated_url?: string; |
|||
github_url?: string; |
|||
license_type?: "commercial" | "research" | "private"; |
|||
}; |
|||
openapi?: object; // When expand=openapi-3.0 |
|||
} |
|||
``` |
|||
|
|||
**Pagination:** Cursor-based. Use `next_cursor` value as `cursor` param. Stop when `has_more` is false. |
|||
|
|||
**Categories of Interest:** |
|||
- `text-to-image` |
|||
- `image-to-image` |
|||
- `text-to-video` |
|||
- `image-to-video` |
|||
|
|||
## Mapping to ProviderModel |
|||
|
|||
Both APIs need to be normalized to our `ProviderModel` interface: |
|||
|
|||
```typescript |
|||
interface ProviderModel { |
|||
id: string; // Replicate: `${owner}/${name}`, fal: endpoint_id |
|||
name: string; // Replicate: name, fal: display_name |
|||
description: string | null; // Replicate: description, fal: metadata.description |
|||
provider: ProviderType; // "replicate" | "fal" |
|||
capabilities: ModelCapability[];// Derived from run type or fal category |
|||
coverImage?: string; // Replicate: cover_image_url, fal: thumbnail_url |
|||
pricing?: { type, amount, currency }; |
|||
} |
|||
``` |
|||
|
|||
**Capability Mapping:** |
|||
- fal.ai: Direct from `category` field |
|||
- Replicate: Infer from model name/description or openapi_schema input/output types |
|||
|
|||
## Implementation Approach |
|||
|
|||
### Provider Module Pattern |
|||
|
|||
Each provider implements `ProviderInterface` and self-registers: |
|||
|
|||
```typescript |
|||
// src/lib/providers/replicate.ts |
|||
const replicateProvider: ProviderInterface = { |
|||
id: "replicate", |
|||
name: "Replicate", |
|||
listModels: async () => { /* fetch and map */ }, |
|||
searchModels: async (query) => { /* use search endpoint */ }, |
|||
getModel: async (id) => { /* fetch specific model */ }, |
|||
generate: async (input) => { /* create prediction */ }, |
|||
isConfigured: () => !!getApiKey(), |
|||
getApiKey: () => /* from store or env */, |
|||
}; |
|||
|
|||
registerProvider(replicateProvider); |
|||
``` |
|||
|
|||
### API Route Pattern |
|||
|
|||
Expose via Next.js API routes for client access: |
|||
|
|||
```typescript |
|||
// src/app/api/providers/[provider]/models/route.ts |
|||
// GET /api/providers/replicate/models?search=flux |
|||
``` |
|||
|
|||
### Caching Strategy |
|||
|
|||
Cache model lists in memory with TTL: |
|||
- **Duration:** 5-15 minutes (models don't change frequently) |
|||
- **Key:** `${provider}:models` or `${provider}:search:${query}` |
|||
- **Invalidation:** Manual refresh button in UI |
|||
|
|||
## Decisions |
|||
|
|||
1. **No SDK usage** - Direct fetch calls to REST APIs as per project constraints |
|||
2. **Server-side fetching** - API routes proxy requests (hides API keys from client) |
|||
3. **Initial page only** - First API call fetches ~50-100 models, pagination on-demand for search |
|||
4. **Category filtering** - Filter to image/video capabilities only (no audio, 3D, etc.) |
|||
|
|||
## Next Steps |
|||
|
|||
1. Create Replicate provider implementation with model fetching |
|||
2. Create fal.ai provider implementation with model fetching |
|||
3. Add API routes for client access to model lists |
|||
4. Implement simple in-memory caching with TTL |
|||
|
|||
## Sources |
|||
|
|||
- [Replicate HTTP API Reference](https://replicate.com/docs/reference/http) |
|||
- [fal.ai Model Search API](https://docs.fal.ai/platform-apis/v1/models) |
|||
Loading…
Reference in new issue