feat(ai): add full image model catalog and tier filters

This commit is contained in:
2026-04-07 21:45:51 +02:00
parent 2d89c0620f
commit 3c161ac9a6
2 changed files with 120 additions and 17 deletions

View File

@@ -22,23 +22,78 @@ export const IMAGE_MODELS: AiModel[] = [
creditCost: 4, creditCost: 4,
minTier: "free", minTier: "free",
}, },
// Phase 2 — uncomment when model selector UI is ready: {
// { id: "black-forest-labs/flux.2-klein-4b",
// id: "black-forest-labs/flux.2-klein-4b", name: "FLUX.2 Klein 4B",
// name: "FLUX.2 Klein", tier: "budget",
// tier: "budget", description: "Low-cost image generation for quick drafts",
// description: "Photorealism, fastest Flux", estimatedCost: "~€0.02",
// estimatedCost: "~€0.02", creditCost: 2,
// minTier: "free", minTier: "free",
// }, },
// { {
// id: "openai/gpt-5-image", id: "bytedance-seed/seedream-4.5",
// name: "GPT-5 Image", name: "Seedream 4.5",
// tier: "premium", tier: "standard",
// description: "Best instruction following, text in image", description: "Balanced detail and speed for everyday prompts",
// estimatedCost: "~€0.15", estimatedCost: "~€0.05",
// minTier: "starter", creditCost: 5,
// }, minTier: "free",
},
{
id: "google/gemini-3.1-flash-image-preview",
name: "Gemini 3.1 Flash Image",
tier: "standard",
description: "Fast multimodal image generation",
estimatedCost: "~€0.06",
creditCost: 6,
minTier: "free",
},
{
id: "openai/gpt-5-image-mini",
name: "GPT-5 Image Mini",
tier: "premium",
description: "Higher-fidelity instruction following",
estimatedCost: "~€0.08",
creditCost: 8,
minTier: "starter",
},
{
id: "sourceful/riverflow-v2-fast",
name: "Riverflow V2 Fast",
tier: "premium",
description: "Fast premium output for production workflows",
estimatedCost: "~€0.09",
creditCost: 9,
minTier: "starter",
},
{
id: "sourceful/riverflow-v2-pro",
name: "Riverflow V2 Pro",
tier: "premium",
description: "Pro quality with stronger composition",
estimatedCost: "~€0.12",
creditCost: 12,
minTier: "starter",
},
{
id: "google/gemini-3-pro-image-preview",
name: "Gemini 3 Pro Image",
tier: "premium",
description: "Advanced quality and prompt adherence",
estimatedCost: "~€0.13",
creditCost: 13,
minTier: "starter",
},
{
id: "openai/gpt-5-image",
name: "GPT-5 Image",
tier: "premium",
description: "Highest quality and best text rendering",
estimatedCost: "~€0.15",
creditCost: 15,
minTier: "starter",
},
]; ];
export const DEFAULT_MODEL_ID = "google/gemini-2.5-flash-image"; export const DEFAULT_MODEL_ID = "google/gemini-2.5-flash-image";
@@ -46,3 +101,16 @@ export const DEFAULT_MODEL_ID = "google/gemini-2.5-flash-image";
export function getModel(id: string): AiModel | undefined { export function getModel(id: string): AiModel | undefined {
return IMAGE_MODELS.find((m) => m.id === id); return IMAGE_MODELS.find((m) => m.id === id);
} }
const IMAGE_MODEL_TIER_ORDER: Record<AiModel["minTier"], number> = {
free: 0,
starter: 1,
pro: 2,
max: 3,
business: 4,
};
export function getAvailableImageModels(tier: AiModel["minTier"]): AiModel[] {
const maxTier = IMAGE_MODEL_TIER_ORDER[tier];
return IMAGE_MODELS.filter((model) => IMAGE_MODEL_TIER_ORDER[model.minTier] <= maxTier);
}

View File

@@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import {
DEFAULT_MODEL_ID,
IMAGE_MODELS,
getAvailableImageModels,
getModel,
} from "@/lib/ai-models";
describe("ai image models registry", () => {
it("contains all 9 PRD models in stable order", () => {
expect(IMAGE_MODELS.map((model) => model.id)).toEqual([
"google/gemini-2.5-flash-image",
"black-forest-labs/flux.2-klein-4b",
"bytedance-seed/seedream-4.5",
"google/gemini-3.1-flash-image-preview",
"openai/gpt-5-image-mini",
"sourceful/riverflow-v2-fast",
"sourceful/riverflow-v2-pro",
"google/gemini-3-pro-image-preview",
"openai/gpt-5-image",
]);
expect(DEFAULT_MODEL_ID).toBe("google/gemini-2.5-flash-image");
});
it("filters by subscription tier", () => {
expect(getAvailableImageModels("free").every((model) => model.minTier === "free")).toBe(
true,
);
});
it("resolves model lookup", () => {
expect(getModel(DEFAULT_MODEL_ID)?.creditCost).toBeGreaterThan(0);
});
});