diff --git a/README.md b/README.md index 8ebf19f..83dbc90 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ # imagent-ui -Web console for imported `imagent-bench` reports plus a local Imagent generation -playground. +Product website for Imagent: home page, OpenRouter-backed generation +playground, benchmark leaderboard, whitepaper, and imported `imagent-bench` +reports. + +The playground uses OpenRouter with the project-standard image model +`google/gemini-3.1-flash-image` (Gemini 3.1 Flash Image). The UI intentionally +shows only that model so contributors compare agent orchestration against a +fixed underlying image model. ## Gittensor Relationship diff --git a/app/api/openrouter/verify/route.ts b/app/api/openrouter/verify/route.ts index 3ae9908..a8f425b 100644 --- a/app/api/openrouter/verify/route.ts +++ b/app/api/openrouter/verify/route.ts @@ -1,5 +1,9 @@ import { NextResponse } from "next/server"; import { resolvePlaygroundEnvironment } from "@/lib/playground"; +import { + IMAGENT_GENERATION_MODEL_ID, + IMAGENT_GENERATION_MODEL_OPTION +} from "@/lib/models"; import { resolvePublicSiteUrl } from "@/lib/site"; type VerifyRequest = { @@ -28,7 +32,7 @@ type OpenRouterModel = { output_modalities?: string[]; modality?: string; }; - pricing?: Record; + pricing?: Record | Array>; }; type OpenRouterModelsResponse = { @@ -40,7 +44,7 @@ type OpenRouterModelsResponse = { }; const OPENROUTER_KEY_URL = "https://openrouter.ai/api/v1/key"; -const OPENROUTER_IMAGE_MODELS_URL = "https://openrouter.ai/api/v1/models?output_modalities=image&sort=pricing-low-to-high"; +const OPENROUTER_IMAGE_MODELS_URL = "https://openrouter.ai/api/v1/images/models"; export async function POST(request: Request) { const body = await parseJson(request); @@ -90,7 +94,7 @@ export async function POST(request: Request) { { verified: true, key: keyPayload.data || null, - models: [], + models: [fixedModelOption()], usingServerKey, warning: modelsPayload.error?.message || `OpenRouter model discovery failed with HTTP ${modelsResponse.status}` }, @@ -98,15 +102,8 @@ export async function POST(request: Request) { ); } - const models = (modelsPayload.data || []) - .map((model) => ({ - id: String(model.id || ""), - name: String(model.name || model.id || ""), - description: String(model.description || ""), - pricing: pricingLabel(model.pricing || {}) - })) - .filter((model) => model.id) - .sort((left, right) => left.name.localeCompare(right.name)); + const fixedModel = (modelsPayload.data || []).find((model) => model.id === IMAGENT_GENERATION_MODEL_ID); + const models = [fixedModelOption(fixedModel)]; return NextResponse.json({ verified: true, @@ -116,6 +113,15 @@ export async function POST(request: Request) { }); } +function fixedModelOption(model?: OpenRouterModel) { + return { + id: IMAGENT_GENERATION_MODEL_ID, + name: String(model?.name || IMAGENT_GENERATION_MODEL_OPTION.name), + description: String(model?.description || IMAGENT_GENERATION_MODEL_OPTION.description), + pricing: pricingLabel(model?.pricing) || IMAGENT_GENERATION_MODEL_OPTION.pricing + }; +} + async function parseJson(response: Request | Response): Promise { try { return (await response.json()) as T; @@ -124,7 +130,10 @@ async function parseJson(response: Request | Response): Promise { } } -function pricingLabel(pricing: Record) { +function pricingLabel(pricing: OpenRouterModel["pricing"]) { + if (!pricing || Array.isArray(pricing)) { + return "OpenRouter pricing"; + } const orderedKeys = ["image", "request", "prompt", "completion"]; const parts = orderedKeys .map((key) => { diff --git a/app/api/playground/generate/route.ts b/app/api/playground/generate/route.ts index 3f7ad83..267b130 100644 --- a/app/api/playground/generate/route.ts +++ b/app/api/playground/generate/route.ts @@ -16,7 +16,6 @@ import { resolvePublicSiteUrl } from "@/lib/site"; type GenerateRequest = { prompt?: string; apiKey?: string; - model?: string; quality?: string; background?: string; }; @@ -48,8 +47,8 @@ export async function POST(request: Request) { const body = (await request.json()) as GenerateRequest; const publicSiteUrl = resolvePublicSiteUrl(); const prompt = String(body.prompt || "").trim(); - const model = String(body.model || DEFAULT_GENERATION_MODEL).trim(); - const quality = String(body.quality || "low").trim(); + const model = DEFAULT_GENERATION_MODEL; + const quality = String(body.quality || "auto").trim(); const background = String(body.background || "auto").trim(); const runtime = await getResolvedPlaygroundRuntime(); const apiKey = String(body.apiKey || (runtime.hasServerApiKey ? process.env.OPENROUTER_API_KEY : "") || "").trim(); diff --git a/app/components/AppFooter.tsx b/app/components/AppFooter.tsx index 02db056..cfd973c 100644 --- a/app/components/AppFooter.tsx +++ b/app/components/AppFooter.tsx @@ -6,8 +6,10 @@ import { RadioTower } from "lucide-react"; import type { Route } from "next"; const footerLinks: Array<{ href: Route; label: string }> = [ + { href: "/", label: "Home" }, { href: "/generation", label: "Generation" }, - { href: "/leaderboard", label: "Leaderboard" } + { href: "/leaderboard", label: "Leaderboard" }, + { href: "/whitepaper", label: "Whitepaper" } ]; export function AppFooter() { @@ -21,7 +23,7 @@ export function AppFooter() {