"use client"; import { useState, useEffect } from "react"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "./ui/tabs"; import { Check, ChevronsUpDown, Info } from "lucide-react"; import { Button } from "./ui/button"; import { Switch } from "./ui/switch"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "./ui/command"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { cn } from "@/lib/utils"; import OpenAIConfig from "./OpenAIConfig"; import GoogleConfig from "./GoogleConfig"; import AnthropicConfig from "./AnthropicConfig"; import OllamaConfig from "./OllamaConfig"; import CustomConfig from "./CustomConfig"; import { updateLLMConfig, changeProvider as changeProviderUtil, } from "@/utils/providerUtils"; import { IMAGE_PROVIDERS, LLM_PROVIDERS } from "@/utils/providerConstants"; import { LLMConfig } from "@/types/llm_config"; const DALLE_3_QUALITY_OPTIONS = [ { label: "Standard", value: "standard", description: "Faster generation with lower cost", }, { label: "HD", value: "hd", description: "Higher quality images with increased cost", }, ]; const GPT_IMAGE_1_5_QUALITY_OPTIONS = [ { label: "Low", value: "low", description: "Fastest and most cost-effective", }, { label: "Medium", value: "medium", description: "Balanced quality and speed", }, { label: "High", value: "high", description: "Best quality with longer generation time", }, ]; // Button state interface interface ButtonState { isLoading: boolean; isDisabled: boolean; text: string; showProgress: boolean; progressPercentage?: number; status?: string; } interface LLMProviderSelectionProps { initialLLMConfig: LLMConfig; onConfigChange: (config: LLMConfig) => void; buttonState: ButtonState; setButtonState: ( state: ButtonState | ((prev: ButtonState) => ButtonState) ) => void; } export default function LLMProviderSelection({ initialLLMConfig, onConfigChange, setButtonState, }: LLMProviderSelectionProps) { const [llmConfig, setLlmConfig] = useState(initialLLMConfig); const [openImageProviderSelect, setOpenImageProviderSelect] = useState(false); const isImageGenerationDisabled = llmConfig.DISABLE_IMAGE_GENERATION ?? false; useEffect(() => { onConfigChange(llmConfig); }, [llmConfig]); useEffect(() => { const needsModelSelection = (llmConfig.LLM === "openai" && !llmConfig.OPENAI_MODEL) || (llmConfig.LLM === "google" && !llmConfig.GOOGLE_MODEL) || (llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL) || (llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL) || (llmConfig.LLM === "anthropic" && !llmConfig.ANTHROPIC_MODEL); const needsProviderApiKey = (llmConfig.LLM === "openai" && !llmConfig.OPENAI_API_KEY) || (llmConfig.LLM === "google" && !llmConfig.GOOGLE_API_KEY) || (llmConfig.LLM === "anthropic" && !llmConfig.ANTHROPIC_API_KEY); const needsImageProviderApiKey = !llmConfig.DISABLE_IMAGE_GENERATION && ((llmConfig.IMAGE_PROVIDER === "dall-e-3" && !llmConfig.OPENAI_API_KEY) || (llmConfig.IMAGE_PROVIDER === "gpt-image-1.5" && !llmConfig.OPENAI_API_KEY) || (llmConfig.IMAGE_PROVIDER === "gemini_flash" && !llmConfig.GOOGLE_API_KEY) || (llmConfig.IMAGE_PROVIDER === "nanobanana_pro" && !llmConfig.GOOGLE_API_KEY) || (llmConfig.IMAGE_PROVIDER === "pexels" && !llmConfig.PEXELS_API_KEY) || (llmConfig.IMAGE_PROVIDER === "pixabay" && !llmConfig.PIXABAY_API_KEY)); const needsApiKey = needsProviderApiKey || needsImageProviderApiKey; const needsOllamaUrl = llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_URL; const needsComfyUIConfig = !llmConfig.DISABLE_IMAGE_GENERATION && llmConfig.IMAGE_PROVIDER === "comfyui" && (!llmConfig.COMFYUI_URL || !llmConfig.COMFYUI_WORKFLOW); setButtonState({ isLoading: false, isDisabled: needsModelSelection || needsApiKey || needsOllamaUrl || needsComfyUIConfig, text: needsModelSelection ? "Please Select a Model" : needsApiKey ? "Please Enter API Key" : needsOllamaUrl ? "Please Enter Ollama URL" : needsComfyUIConfig ? "Please Configure ComfyUI" : "Save Configuration", showProgress: false, }); }, [llmConfig]); const input_field_changed = (new_value: string | boolean, field: string) => { const updatedConfig = updateLLMConfig(llmConfig, field, new_value); setLlmConfig(updatedConfig); }; const getApiKeyValue = (field?: string) => { switch (field) { case "OPENAI_API_KEY": return llmConfig.OPENAI_API_KEY || ""; case "GOOGLE_API_KEY": return llmConfig.GOOGLE_API_KEY || ""; case "ANTHROPIC_API_KEY": return llmConfig.ANTHROPIC_API_KEY || ""; case "PEXELS_API_KEY": return llmConfig.PEXELS_API_KEY || ""; case "PIXABAY_API_KEY": return llmConfig.PIXABAY_API_KEY || ""; default: return ""; } }; const handleApiKeyInputChange = (field: string | undefined, value: string) => { switch (field) { case "OPENAI_API_KEY": input_field_changed(value, "openai_api_key"); break; case "GOOGLE_API_KEY": input_field_changed(value, "google_api_key"); break; case "ANTHROPIC_API_KEY": input_field_changed(value, "anthropic_api_key"); break; case "PEXELS_API_KEY": input_field_changed(value, "pexels_api_key"); break; case "PIXABAY_API_KEY": input_field_changed(value, "pixabay_api_key"); break; default: break; } }; const handleProviderChange = (provider: string) => { const newConfig = changeProviderUtil(llmConfig, provider); setLlmConfig(newConfig); }; useEffect(() => { if (!llmConfig.USE_CUSTOM_URL) { setLlmConfig({ ...llmConfig, OLLAMA_URL: "http://localhost:11434" }); } else { if (!llmConfig.OLLAMA_URL) { setLlmConfig({ ...llmConfig, OLLAMA_URL: "http://localhost:11434" }); } } }, [llmConfig.USE_CUSTOM_URL]); useEffect(() => { setLlmConfig((prevConfig) => { const updates: Partial = {}; if (!prevConfig.DISABLE_IMAGE_GENERATION && !prevConfig.IMAGE_PROVIDER) { if (prevConfig.LLM === "openai") { updates.IMAGE_PROVIDER = "gpt-image-1.5"; } else if (prevConfig.LLM === "google") { updates.IMAGE_PROVIDER = "gemini_flash"; } else { updates.IMAGE_PROVIDER = "pexels"; } } if (!prevConfig.OLLAMA_URL) { updates.OLLAMA_URL = "http://localhost:11434"; } if (Object.keys(updates).length === 0) { return prevConfig; } return { ...prevConfig, ...updates }; }); }, []); useEffect(() => { setLlmConfig((prevConfig) => { const updates: Partial = {}; if ( prevConfig.IMAGE_PROVIDER === "dall-e-3" && !prevConfig.DALL_E_3_QUALITY ) { updates.DALL_E_3_QUALITY = "standard"; } if ( prevConfig.IMAGE_PROVIDER === "gpt-image-1.5" && !prevConfig.GPT_IMAGE_1_5_QUALITY ) { updates.GPT_IMAGE_1_5_QUALITY = "medium"; } if (Object.keys(updates).length === 0) { return prevConfig; } return { ...prevConfig, ...updates }; }); }, [llmConfig.IMAGE_PROVIDER]); const renderQualitySelector = () => { if (llmConfig.IMAGE_PROVIDER === "dall-e-3") { return (
{DALLE_3_QUALITY_OPTIONS.map((option) => ( ))}
); } if (llmConfig.IMAGE_PROVIDER === "gpt-image-1.5") { return (
{GPT_IMAGE_1_5_QUALITY_OPTIONS.map((option) => ( ))}
); } return null; }; return (
{/* Provider Selection - Fixed Header */}
OpenAI Google Anthropic Ollama Custom
{/* Scrollable Content */}
{/* OpenAI Content */} {/* Google Content */} {/* Anthropic Content */} {/* Ollama Content */} {/* Custom Content */} {/* Image Generation Toggle */}
{ input_field_changed(checked, "disable_image_generation"); if (checked) { setOpenImageProviderSelect(false); } }} />

When enabled, slides will not include automatically generated images.

{!isImageGenerationDisabled && ( <> {/* Image Provider Selection */}
No provider found. {Object.values(IMAGE_PROVIDERS).map( (provider, index) => ( { input_field_changed(value, "image_provider"); setOpenImageProviderSelect(false); }} >
{provider.label}
{provider.description}
) )}
{renderQualitySelector()} {/* Dynamic API Key Input for Image Provider */} {llmConfig.IMAGE_PROVIDER && IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER] && (() => { const provider = IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER]; // Show info message when using same API key as main provider if ( provider.value === "dall-e-3" && llmConfig.LLM === "openai" ) { return <>; } if ( provider.value === "gpt-image-1.5" && llmConfig.LLM === "openai" ) { return <>; } if ( provider.value === "gemini_flash" && llmConfig.LLM === "google" ) { return <>; } if ( provider.value === "nanobanana_pro" && llmConfig.LLM === "google" ) { return <>; } // Show ComfyUI configuration if (provider.value === "comfyui") { return (
{ input_field_changed( e.target.value, "comfyui_url" ); }} />

Use your machine IP address (not localhost) when running in Docker