import React, { useState } from 'react'; import { Input } from '../../ui/input'; import { LabelWithTooltip } from '../../ui/label-with-tooltip'; import { Collapsible } from '../../ui/collapsible'; import { Eye, EyeOff } from 'lucide-react'; import { LLM_PROVIDERS, isReasoningCapableModel, type AgentConfig } from '@dexto/core'; type LLMConfig = AgentConfig['llm']; interface LLMConfigSectionProps { value: LLMConfig; onChange: (value: LLMConfig) => void; errors?: Record; open?: boolean; onOpenChange?: (open: boolean) => void; errorCount?: number; sectionErrors?: string[]; } export function LLMConfigSection({ value, onChange, errors = {}, open, onOpenChange, errorCount = 0, sectionErrors = [], }: LLMConfigSectionProps) { const [showApiKey, setShowApiKey] = useState(false); const handleChange = (field: keyof LLMConfig, newValue: string | number | undefined) => { onChange({ ...value, [field]: newValue } as LLMConfig); }; return (
{/* Provider */}
Provider * {errors['llm.provider'] && (

{errors['llm.provider']}

)}
{/* Model */}
Model * handleChange('model', e.target.value)} placeholder="e.g., gpt-5, claude-sonnet-4-5-20250929" aria-invalid={!!errors['llm.model']} /> {errors['llm.model'] && (

{errors['llm.model']}

)}
{/* API Key */}
API Key *
handleChange('apiKey', e.target.value)} placeholder="$OPENAI_API_KEY or direct value" aria-invalid={!!errors['llm.apiKey']} className="pr-10" />
{errors['llm.apiKey'] && (

{errors['llm.apiKey']}

)}
{/* Max Iterations */}
Max Iterations { const val = e.target.value; if (val === '') { handleChange('maxIterations', undefined); } else { const num = parseInt(val, 10); if (!isNaN(num)) { handleChange('maxIterations', num); } } }} min="1" placeholder="50" aria-invalid={!!errors['llm.maxIterations']} /> {errors['llm.maxIterations'] && (

{errors['llm.maxIterations']}

)}
{/* Base URL */}
Base URL handleChange('baseURL', e.target.value || undefined)} placeholder="https://api.openai.com/v1" aria-invalid={!!errors['llm.baseURL']} /> {errors['llm.baseURL'] && (

{errors['llm.baseURL']}

)}
{/* Temperature */}
Temperature handleChange( 'temperature', e.target.value ? parseFloat(e.target.value) : undefined ) } min="0" max="1" step="0.1" placeholder="0.0 - 1.0" aria-invalid={!!errors['llm.temperature']} /> {errors['llm.temperature'] && (

{errors['llm.temperature']}

)}
{/* Max Input/Output Tokens */}
Max Input Tokens handleChange( 'maxInputTokens', e.target.value ? parseInt(e.target.value, 10) : undefined ) } min="1" placeholder="Auto (128k fallback)" aria-invalid={!!errors['llm.maxInputTokens']} /> {errors['llm.maxInputTokens'] && (

{errors['llm.maxInputTokens']}

)}
Max Output Tokens handleChange( 'maxOutputTokens', e.target.value ? parseInt(e.target.value, 10) : undefined ) } min="1" placeholder="Auto (provider default)" aria-invalid={!!errors['llm.maxOutputTokens']} /> {errors['llm.maxOutputTokens'] && (

{errors['llm.maxOutputTokens']}

)}
{/* Provider-Specific Options */} {/* Reasoning Effort - Only for models that support it (o1, o3, codex, gpt-5.x) */} {value.model && isReasoningCapableModel(value.model) && (
Reasoning Effort

Only applies to reasoning models (o1, o3, codex, gpt-5.x)

)}
); }