feat: add fal_ai model pricing and image preview in log viewer

Add pricing for 23 fal_ai image generation models (nano-banana, flux, gemini,
seedream, kling, iclight). Display generated images inline in the log details
drawer instead of raw JSON for image generation responses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Albert Sebastian 2026-04-14 04:11:14 +05:30
parent 5f1d3c1fa8
commit ea84e98c78
4 changed files with 218 additions and 0 deletions

View file

@ -12733,6 +12733,190 @@
"/v1/images/generations"
]
},
"fal_ai/nano-banana-2": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.01,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/nano-banana-2/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.01,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/nano-banana-pro": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.02,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/nano-banana-pro/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.02,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/flux-2/klein/9b": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.025,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/flux-2/klein/9b/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.025,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/flux-2/klein/9b/base/edit/lora": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.025,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/flux-pro": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.04,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/flux-pro/kontext": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.05,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/gemini-3-pro-image-preview": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.04,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/gemini-3-pro-image-preview/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.04,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/gemini-3.1-flash-image-preview": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.02,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/gemini-3.1-flash-image-preview/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.02,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/gemini-25-flash-image": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.02,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/gemini-25-flash-image/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.02,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/bytedance/seedream/v4/text-to-image": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.03,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/bytedance/seedream/v4/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.03,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/bytedance/seedream/v4.5/text-to-image": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.03,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/bytedance/seedream/v4.5/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.03,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/bytedance/seedream/v5/lite/text-to-image": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.03,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/bytedance/seedream/v5/lite/edit": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.03,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/kling-image/omni": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.04,
"supported_endpoints": [
"/v1/images/generations"
]
},
"fal_ai/iclight-v2": {
"litellm_provider": "fal_ai",
"mode": "image_generation",
"output_cost_per_image": 0.02,
"supported_endpoints": [
"/v1/images/generations"
]
},
"featherless_ai/featherless-ai/Qwerky-72B": {
"litellm_provider": "featherless_ai",
"max_input_tokens": 32768,

View file

@ -97,6 +97,25 @@ export function OutputCard({ message, completionTokens, outputCost }: OutputCard
content={message.content}
toolCalls={message.toolCalls}
/>
{message.imageUrls && message.imageUrls.length > 0 && (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: message.content ? 8 : 0 }}>
{message.imageUrls.map((url, idx) => (
<a key={idx} href={url} target="_blank" rel="noopener noreferrer">
<img
src={url}
alt={`Generated image ${idx + 1}`}
style={{
maxWidth: 300,
maxHeight: 300,
borderRadius: 6,
border: '1px solid #f0f0f0',
objectFit: 'contain',
}}
/>
</a>
))}
</div>
)}
</div>
</div>
</div>

View file

@ -7,6 +7,7 @@ export interface ParsedMessage {
content: string;
toolCalls?: ToolCall[];
toolCallId?: string;
imageUrls?: string[];
}
export interface ToolCall {

View file

@ -64,6 +64,20 @@ export const parseMessages = (request: any, response: any): ParsedMessages => {
};
}
// If no chat response, check for image generation response
if (!responseMessage && response?.data && Array.isArray(response.data)) {
const imageUrls = response.data
.map((img: any) => img.url || (img.b64_json ? `data:image/png;base64,${img.b64_json}` : null))
.filter(Boolean);
if (imageUrls.length > 0) {
responseMessage = {
role: 'assistant',
content: '',
imageUrls: imageUrls,
};
}
}
return { requestMessages, responseMessage };
};