Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions testing/panel/src/routes/api.image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { createFileRoute } from '@tanstack/react-router'
import { generateImage, createImageOptions } from '@tanstack/ai'
import { geminiImage } from '@tanstack/ai-gemini'
import { openaiImage } from '@tanstack/ai-openai'
import { openRouterImage } from '@tanstack/ai-openrouter'

type Provider = 'openai' | 'gemini'
type Provider = 'openai' | 'gemini' | 'openrouter'

export const Route = createFileRoute('/api/image')({
server: {
Expand All @@ -13,21 +14,28 @@ export const Route = createFileRoute('/api/image')({
const { prompt, numberOfImages = 1, size = '1024x1024' } = body
const data = body.data || {}
const provider: Provider = data.provider || body.provider || 'openai'
const model: string = data.model || body.model || 'gpt-image-1'

const defaultModels: Record<Provider, string> = {
openai: 'gpt-image-1',
gemini: 'gemini-2.0-flash-preview-image-generation',
openrouter: 'google/gemini-3.1-flash-image-preview',
}
const model: string =
data.model || body.model || defaultModels[provider]

try {
// Pre-define typed adapter configurations with full type inference
// Model is passed to the adapter factory function for type-safe autocomplete
const adapterConfig = {
gemini: () =>
createImageOptions({
adapter: geminiImage(
(model || 'gemini-2.0-flash-preview-image-generation') as any,
),
adapter: geminiImage(model as any),
}),
openai: () =>
createImageOptions({
adapter: openaiImage((model || 'gpt-image-1') as any),
adapter: openaiImage(model as any),
}),
openrouter: () =>
createImageOptions({
adapter: openRouterImage(model as any),
}),
Comment on lines 16 to 39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n testing/panel/src/routes/api.image.ts

Repository: TanStack/ai

Length of output: 3327


Add runtime validation for provider and model parameters using Zod before adapter initialization.

The request.json() call is untyped and provider/model are used without validation. Unknown providers like "foo" reach adapterConfig[provider]() at line 43, which returns undefined, throwing a TypeError caught and returned as a 500 status. Invalid inputs should return 400 (client error) instead. Implement a Zod schema to validate the request structure and narrow provider/model pairs at the entry point, before indexing defaultModels and adapterConfig.

Per coding guidelines: use Zod for runtime schema validation and type-safe per-model configuration with provider options typed based on the selected model.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@testing/panel/src/routes/api.image.ts` around lines 16 - 39, Validate
incoming request JSON with a Zod schema that enforces provider as a union of
allowed providers and model as an optional string constrained to the expected
shape before using provider/model; parse the body and use the validated result
to set provider/model (instead of reading data.provider/body.provider directly),
then if Zod parsing fails return a 400 error. After validation, use the narrowed
provider value to lookup defaultModels and adapterConfig safely (symbols to
update: provider, model, defaultModels, adapterConfig, createImageOptions,
geminiImage, openaiImage, openRouterImage) so adapterConfig[provider] cannot be
undefined and per-provider model options are type-safe.

}

Expand All @@ -46,8 +54,7 @@ export const Route = createFileRoute('/api/image')({
})

console.log(
'>> image generation result:',
JSON.stringify(result, null, 2),
`>> image generation complete: ${result.images.length} image(s)`,
)

return new Response(
Expand Down
15 changes: 14 additions & 1 deletion testing/panel/src/routes/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState } from 'react'
import { createFileRoute } from '@tanstack/react-router'
import { ImageIcon, Loader2, Download } from 'lucide-react'

type Provider = 'openai' | 'gemini'
type Provider = 'openai' | 'gemini' | 'openrouter'

interface ImageProvider {
id: Provider
Expand All @@ -22,6 +22,19 @@ const PROVIDERS: Array<ImageProvider> = [
// name: 'Gemini (Imagen)',
// sizes: ['1024x1024', '1:1', '16:9', '9:16', '4:3', '3:4'],
// },
{
id: 'openrouter',
name: 'OpenRouter (Gemini 3.1 Flash Image)',
sizes: [
'1024x1024',
'1248x832',
'832x1248',
'1184x864',
'864x1184',
'1344x768',
'768x1344',
],
},
]

interface GeneratedImage {
Expand Down
Loading