 c177363a19
			
		
	
	c177363a19
	
	
	
		
			
			🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			615 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			615 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 'use client'
 | |
| 
 | |
| import { useState, useEffect } from 'react'
 | |
| import { 
 | |
|   CpuChipIcon,
 | |
|   SparklesIcon,
 | |
|   CurrencyDollarIcon,
 | |
|   ServerIcon,
 | |
|   CheckCircleIcon,
 | |
|   ExclamationTriangleIcon,
 | |
|   InformationCircleIcon,
 | |
|   EyeIcon,
 | |
|   EyeSlashIcon,
 | |
|   ArrowPathIcon
 | |
| } from '@heroicons/react/24/outline'
 | |
| 
 | |
| interface GPUInfo {
 | |
|   name: string
 | |
|   memory: string
 | |
|   type: string
 | |
|   driver: string
 | |
| }
 | |
| 
 | |
| interface AIConfig {
 | |
|   // OpenAI Configuration
 | |
|   openaiEnabled: boolean
 | |
|   openaiApiKey: string
 | |
|   openaiOrganization: string
 | |
|   openaiDefaultModel: string
 | |
|   
 | |
|   // Cost Management
 | |
|   dailyCostLimit: number
 | |
|   monthlyCostLimit: number
 | |
|   costAlerts: boolean
 | |
|   
 | |
|   // Local AI (Ollama/Parallama)
 | |
|   localAIEnabled: boolean
 | |
|   localAIType: 'ollama' | 'parallama'
 | |
|   localAIEndpoint: string
 | |
|   localAIModels: string[]
 | |
|   
 | |
|   // GPU Configuration
 | |
|   gpuAcceleration: boolean
 | |
|   preferredGPU: string
 | |
|   maxGPUMemory: number
 | |
|   
 | |
|   // Model Selection
 | |
|   preferredProvider: 'openai' | 'local' | 'hybrid'
 | |
|   fallbackEnabled: boolean
 | |
| }
 | |
| 
 | |
| interface AIConfigurationProps {
 | |
|   systemInfo: any
 | |
|   configData: any
 | |
|   onComplete: (data: any) => void
 | |
|   onBack?: () => void
 | |
|   isCompleted: boolean
 | |
| }
 | |
| 
 | |
| export default function AIConfiguration({ 
 | |
|   systemInfo, 
 | |
|   configData, 
 | |
|   onComplete, 
 | |
|   onBack, 
 | |
|   isCompleted 
 | |
| }: AIConfigurationProps) {
 | |
|   const [config, setConfig] = useState<AIConfig>({
 | |
|     openaiEnabled: false,
 | |
|     openaiApiKey: '',
 | |
|     openaiOrganization: '',
 | |
|     openaiDefaultModel: 'gpt-4',
 | |
|     
 | |
|     dailyCostLimit: 50,
 | |
|     monthlyCostLimit: 500,
 | |
|     costAlerts: true,
 | |
|     
 | |
|     localAIEnabled: true,
 | |
|     localAIType: 'ollama',
 | |
|     localAIEndpoint: 'http://localhost:11434',
 | |
|     localAIModels: ['llama2', 'codellama'],
 | |
|     
 | |
|     gpuAcceleration: false,
 | |
|     preferredGPU: '',
 | |
|     maxGPUMemory: 8,
 | |
|     
 | |
|     preferredProvider: 'local',
 | |
|     fallbackEnabled: true
 | |
|   })
 | |
| 
 | |
|   const [showApiKey, setShowApiKey] = useState(false)
 | |
|   const [validatingOpenAI, setValidatingOpenAI] = useState(false)
 | |
|   const [validatingLocal, setValidatingLocal] = useState(false)
 | |
|   const [openaiValid, setOpenaiValid] = useState<boolean | null>(null)
 | |
|   const [localAIValid, setLocalAIValid] = useState<boolean | null>(null)
 | |
| 
 | |
|   // Initialize configuration from existing data
 | |
|   useEffect(() => {
 | |
|     if (configData.ai) {
 | |
|       setConfig(prev => ({ ...prev, ...configData.ai }))
 | |
|     }
 | |
|     
 | |
|     // Auto-detect GPU capabilities
 | |
|     if (systemInfo?.gpus?.length > 0) {
 | |
|       const hasNVIDIA = systemInfo.gpus.some((gpu: GPUInfo) => gpu.type === 'nvidia')
 | |
|       const hasAMD = systemInfo.gpus.some((gpu: GPUInfo) => gpu.type === 'amd')
 | |
|       
 | |
|       if (hasNVIDIA) {
 | |
|         setConfig(prev => ({
 | |
|           ...prev,
 | |
|           gpuAcceleration: true,
 | |
|           localAIType: 'parallama', // Parallama typically better for NVIDIA
 | |
|           preferredGPU: systemInfo.gpus.find((gpu: GPUInfo) => gpu.type === 'nvidia')?.name || ''
 | |
|         }))
 | |
|       } else if (hasAMD) {
 | |
|         setConfig(prev => ({
 | |
|           ...prev,
 | |
|           gpuAcceleration: true,
 | |
|           localAIType: 'ollama', // Ollama works well with AMD
 | |
|           preferredGPU: systemInfo.gpus.find((gpu: GPUInfo) => gpu.type === 'amd')?.name || ''
 | |
|         }))
 | |
|       }
 | |
|     }
 | |
|   }, [systemInfo, configData])
 | |
| 
 | |
|   const validateOpenAI = async () => {
 | |
|     if (!config.openaiApiKey) {
 | |
|       setOpenaiValid(false)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     setValidatingOpenAI(true)
 | |
|     try {
 | |
|       // This would be a real API validation in production
 | |
|       // For now, just simulate validation
 | |
|       await new Promise(resolve => setTimeout(resolve, 1000))
 | |
|       setOpenaiValid(true)
 | |
|     } catch (error) {
 | |
|       setOpenaiValid(false)
 | |
|     } finally {
 | |
|       setValidatingOpenAI(false)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const validateLocalAI = async () => {
 | |
|     if (!config.localAIEndpoint) {
 | |
|       setLocalAIValid(false)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     setValidatingLocal(true)
 | |
|     try {
 | |
|       const response = await fetch(`${config.localAIEndpoint}/api/tags`)
 | |
|       setLocalAIValid(response.ok)
 | |
|     } catch (error) {
 | |
|       setLocalAIValid(false)
 | |
|     } finally {
 | |
|       setValidatingLocal(false)
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const getGPURecommendations = () => {
 | |
|     if (!systemInfo?.gpus?.length) {
 | |
|       return {
 | |
|         recommendation: 'No GPU detected. CPU-only processing will be used.',
 | |
|         type: 'info',
 | |
|         details: 'Consider adding a GPU for better AI performance.'
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const gpus = systemInfo.gpus
 | |
|     const nvidiaGPUs = gpus.filter((gpu: GPUInfo) => gpu.type === 'nvidia')
 | |
|     const amdGPUs = gpus.filter((gpu: GPUInfo) => gpu.type === 'amd')
 | |
| 
 | |
|     if (nvidiaGPUs.length > 0) {
 | |
|       return {
 | |
|         recommendation: 'NVIDIA GPU detected - Parallama recommended for optimal performance',
 | |
|         type: 'success',
 | |
|         details: `${nvidiaGPUs[0].name} with ${nvidiaGPUs[0].memory} VRAM detected. Parallama provides excellent NVIDIA GPU acceleration.`
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (amdGPUs.length > 0) {
 | |
|       return {
 | |
|         recommendation: 'AMD GPU detected - Ollama with ROCm support recommended',
 | |
|         type: 'warning',
 | |
|         details: `${amdGPUs[0].name} detected. Ollama provides good AMD GPU support through ROCm.`
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|       recommendation: 'Integrated GPU detected - Limited AI acceleration available',
 | |
|       type: 'warning',
 | |
|       details: 'Integrated GPUs provide limited AI acceleration. Consider a dedicated GPU for better performance.'
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const getRecommendedModels = () => {
 | |
|     const memoryGB = systemInfo?.memory_mb ? Math.round(systemInfo.memory_mb / 1024) : 8
 | |
| 
 | |
|     if (memoryGB >= 32) {
 | |
|       return ['llama2:70b', 'codellama:34b', 'mixtral:8x7b']
 | |
|     } else if (memoryGB >= 16) {
 | |
|       return ['llama2:13b', 'codellama:13b', 'llama2:7b']
 | |
|     } else {
 | |
|       return ['llama2:7b', 'codellama:7b', 'phi2']
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const handleSubmit = (e: React.FormEvent) => {
 | |
|     e.preventDefault()
 | |
|     
 | |
|     // Validate that at least one AI provider is configured
 | |
|     if (!config.openaiEnabled && !config.localAIEnabled) {
 | |
|       alert('Please enable at least one AI provider (OpenAI or Local AI)')
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     onComplete({ ai: config })
 | |
|   }
 | |
| 
 | |
|   const gpuRecommendation = getGPURecommendations()
 | |
|   const recommendedModels = getRecommendedModels()
 | |
| 
 | |
|   return (
 | |
|     <form onSubmit={handleSubmit} className="space-y-6">
 | |
|       {/* GPU Detection & Recommendations */}
 | |
|       {systemInfo?.gpus && (
 | |
|         <div className="bg-gray-50 rounded-lg p-6">
 | |
|           <h3 className="text-lg font-medium text-gray-900 mb-4 flex items-center">
 | |
|             <CpuChipIcon className="h-6 w-6 text-bzzz-primary mr-2" />
 | |
|             GPU Configuration
 | |
|           </h3>
 | |
|           
 | |
|           <div className={`p-4 rounded-lg border mb-4 ${
 | |
|             gpuRecommendation.type === 'success' ? 'bg-green-50 border-green-200' :
 | |
|             gpuRecommendation.type === 'warning' ? 'bg-yellow-50 border-yellow-200' :
 | |
|             'bg-blue-50 border-blue-200'
 | |
|           }`}>
 | |
|             <div className="flex items-start">
 | |
|               <InformationCircleIcon className={`h-5 w-5 mt-0.5 mr-2 ${
 | |
|                 gpuRecommendation.type === 'success' ? 'text-green-600' :
 | |
|                 gpuRecommendation.type === 'warning' ? 'text-yellow-600' :
 | |
|                 'text-blue-600'
 | |
|               }`} />
 | |
|               <div>
 | |
|                 <div className={`font-medium ${
 | |
|                   gpuRecommendation.type === 'success' ? 'text-green-800' :
 | |
|                   gpuRecommendation.type === 'warning' ? 'text-yellow-800' :
 | |
|                   'text-blue-800'
 | |
|                 }`}>
 | |
|                   {gpuRecommendation.recommendation}
 | |
|                 </div>
 | |
|                 <div className={`text-sm mt-1 ${
 | |
|                   gpuRecommendation.type === 'success' ? 'text-green-700' :
 | |
|                   gpuRecommendation.type === 'warning' ? 'text-yellow-700' :
 | |
|                   'text-blue-700'
 | |
|                 }`}>
 | |
|                   {gpuRecommendation.details}
 | |
|                 </div>
 | |
|               </div>
 | |
|             </div>
 | |
|           </div>
 | |
| 
 | |
|           {systemInfo.gpus.length > 0 && (
 | |
|             <div className="space-y-3">
 | |
|               <div className="flex items-center">
 | |
|                 <input
 | |
|                   type="checkbox"
 | |
|                   id="gpuAcceleration"
 | |
|                   checked={config.gpuAcceleration}
 | |
|                   onChange={(e) => setConfig(prev => ({ ...prev, gpuAcceleration: e.target.checked }))}
 | |
|                   className="h-4 w-4 text-bzzz-primary focus:ring-bzzz-primary border-gray-300 rounded"
 | |
|                 />
 | |
|                 <label htmlFor="gpuAcceleration" className="ml-2 text-sm font-medium text-gray-700">
 | |
|                   Enable GPU acceleration for AI processing
 | |
|                 </label>
 | |
|               </div>
 | |
| 
 | |
|               {config.gpuAcceleration && (
 | |
|                 <div>
 | |
|                   <label className="label">Preferred GPU</label>
 | |
|                   <select
 | |
|                     value={config.preferredGPU}
 | |
|                     onChange={(e) => setConfig(prev => ({ ...prev, preferredGPU: e.target.value }))}
 | |
|                     className="input-field"
 | |
|                   >
 | |
|                     <option value="">Auto-select</option>
 | |
|                     {systemInfo.gpus.map((gpu: GPUInfo, index: number) => (
 | |
|                       <option key={index} value={gpu.name}>
 | |
|                         {gpu.name} ({gpu.type.toUpperCase()}) - {gpu.memory}
 | |
|                       </option>
 | |
|                     ))}
 | |
|                   </select>
 | |
|                 </div>
 | |
|               )}
 | |
|             </div>
 | |
|           )}
 | |
|         </div>
 | |
|       )}
 | |
| 
 | |
|       {/* Local AI Configuration */}
 | |
|       <div className="bg-white border border-gray-200 rounded-lg p-6">
 | |
|         <div className="flex items-center justify-between mb-4">
 | |
|           <h3 className="text-lg font-medium text-gray-900 flex items-center">
 | |
|             <ServerIcon className="h-6 w-6 text-bzzz-primary mr-2" />
 | |
|             Local AI (Ollama/Parallama)
 | |
|           </h3>
 | |
|           <div className="flex items-center">
 | |
|             <input
 | |
|               type="checkbox"
 | |
|               id="localAIEnabled"
 | |
|               checked={config.localAIEnabled}
 | |
|               onChange={(e) => setConfig(prev => ({ ...prev, localAIEnabled: e.target.checked }))}
 | |
|               className="h-4 w-4 text-bzzz-primary focus:ring-bzzz-primary border-gray-300 rounded"
 | |
|             />
 | |
|             <label htmlFor="localAIEnabled" className="ml-2 text-sm font-medium text-gray-700">
 | |
|               Enable Local AI
 | |
|             </label>
 | |
|           </div>
 | |
|         </div>
 | |
| 
 | |
|         {config.localAIEnabled && (
 | |
|           <div className="space-y-4">
 | |
|             <div>
 | |
|               <label className="label">Local AI Provider</label>
 | |
|               <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
 | |
|                 <div
 | |
|                   className={`border-2 rounded-lg p-4 cursor-pointer transition-all ${
 | |
|                     config.localAIType === 'ollama'
 | |
|                       ? 'border-bzzz-primary bg-bzzz-primary bg-opacity-10'
 | |
|                       : 'border-gray-200 hover:border-gray-300'
 | |
|                   }`}
 | |
|                   onClick={() => setConfig(prev => ({ ...prev, localAIType: 'ollama' }))}
 | |
|                 >
 | |
|                   <div className="font-medium text-gray-900">Ollama</div>
 | |
|                   <div className="text-sm text-gray-600">Open-source, self-hosted AI models</div>
 | |
|                   <div className="text-xs text-gray-500 mt-1">Best for: AMD GPUs, CPU-only setups</div>
 | |
|                 </div>
 | |
|                 
 | |
|                 <div
 | |
|                   className={`border-2 rounded-lg p-4 cursor-pointer transition-all ${
 | |
|                     config.localAIType === 'parallama'
 | |
|                       ? 'border-bzzz-primary bg-bzzz-primary bg-opacity-10'
 | |
|                       : 'border-gray-200 hover:border-gray-300'
 | |
|                   }`}
 | |
|                   onClick={() => setConfig(prev => ({ ...prev, localAIType: 'parallama' }))}
 | |
|                 >
 | |
|                   <div className="font-medium text-gray-900">Parallama</div>
 | |
|                   <div className="text-sm text-gray-600">Optimized for parallel processing</div>
 | |
|                   <div className="text-xs text-gray-500 mt-1">Best for: NVIDIA GPUs, high performance</div>
 | |
|                 </div>
 | |
|               </div>
 | |
|             </div>
 | |
| 
 | |
|             <div>
 | |
|               <label className="label">API Endpoint</label>
 | |
|               <div className="flex space-x-2">
 | |
|                 <input
 | |
|                   type="url"
 | |
|                   value={config.localAIEndpoint}
 | |
|                   onChange={(e) => setConfig(prev => ({ ...prev, localAIEndpoint: e.target.value }))}
 | |
|                   placeholder="http://localhost:11434"
 | |
|                   className="input-field flex-1"
 | |
|                 />
 | |
|                 <button
 | |
|                   type="button"
 | |
|                   onClick={validateLocalAI}
 | |
|                   disabled={validatingLocal}
 | |
|                   className="btn-outline whitespace-nowrap"
 | |
|                 >
 | |
|                   {validatingLocal ? (
 | |
|                     <ArrowPathIcon className="h-4 w-4 animate-spin" />
 | |
|                   ) : (
 | |
|                     'Test'
 | |
|                   )}
 | |
|                 </button>
 | |
|               </div>
 | |
|               {localAIValid === true && (
 | |
|                 <div className="flex items-center mt-1 text-green-600 text-sm">
 | |
|                   <CheckCircleIcon className="h-4 w-4 mr-1" />
 | |
|                   Connection successful
 | |
|                 </div>
 | |
|               )}
 | |
|               {localAIValid === false && (
 | |
|                 <div className="flex items-center mt-1 text-red-600 text-sm">
 | |
|                   <ExclamationTriangleIcon className="h-4 w-4 mr-1" />
 | |
|                   Connection failed
 | |
|                 </div>
 | |
|               )}
 | |
|             </div>
 | |
| 
 | |
|             <div>
 | |
|               <label className="label">Recommended Models for your system</label>
 | |
|               <div className="bg-blue-50 border border-blue-200 rounded-lg p-3">
 | |
|                 <div className="text-sm text-blue-800">
 | |
|                   <p className="font-medium mb-2">Based on your system memory ({Math.round(systemInfo?.memory_mb / 1024 || 8)} GB):</p>
 | |
|                   <div className="flex flex-wrap gap-2">
 | |
|                     {recommendedModels.map((model, index) => (
 | |
|                       <span key={index} className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs">
 | |
|                         {model}
 | |
|                       </span>
 | |
|                     ))}
 | |
|                   </div>
 | |
|                 </div>
 | |
|               </div>
 | |
|             </div>
 | |
|           </div>
 | |
|         )}
 | |
|       </div>
 | |
| 
 | |
|       {/* OpenAI Configuration */}
 | |
|       <div className="bg-white border border-gray-200 rounded-lg p-6">
 | |
|         <div className="flex items-center justify-between mb-4">
 | |
|           <h3 className="text-lg font-medium text-gray-900 flex items-center">
 | |
|             <SparklesIcon className="h-6 w-6 text-bzzz-primary mr-2" />
 | |
|             OpenAI API
 | |
|           </h3>
 | |
|           <div className="flex items-center">
 | |
|             <input
 | |
|               type="checkbox"
 | |
|               id="openaiEnabled"
 | |
|               checked={config.openaiEnabled}
 | |
|               onChange={(e) => setConfig(prev => ({ ...prev, openaiEnabled: e.target.checked }))}
 | |
|               className="h-4 w-4 text-bzzz-primary focus:ring-bzzz-primary border-gray-300 rounded"
 | |
|             />
 | |
|             <label htmlFor="openaiEnabled" className="ml-2 text-sm font-medium text-gray-700">
 | |
|               Enable OpenAI API
 | |
|             </label>
 | |
|           </div>
 | |
|         </div>
 | |
| 
 | |
|         {config.openaiEnabled && (
 | |
|           <div className="space-y-4">
 | |
|             <div>
 | |
|               <label className="label">API Key</label>
 | |
|               <div className="flex space-x-2">
 | |
|                 <div className="relative flex-1">
 | |
|                   <input
 | |
|                     type={showApiKey ? 'text' : 'password'}
 | |
|                     value={config.openaiApiKey}
 | |
|                     onChange={(e) => setConfig(prev => ({ ...prev, openaiApiKey: e.target.value }))}
 | |
|                     placeholder="sk-..."
 | |
|                     className="input-field pr-10"
 | |
|                   />
 | |
|                   <button
 | |
|                     type="button"
 | |
|                     onClick={() => setShowApiKey(!showApiKey)}
 | |
|                     className="absolute inset-y-0 right-0 pr-3 flex items-center"
 | |
|                   >
 | |
|                     {showApiKey ? (
 | |
|                       <EyeSlashIcon className="h-5 w-5 text-gray-400" />
 | |
|                     ) : (
 | |
|                       <EyeIcon className="h-5 w-5 text-gray-400" />
 | |
|                     )}
 | |
|                   </button>
 | |
|                 </div>
 | |
|                 <button
 | |
|                   type="button"
 | |
|                   onClick={validateOpenAI}
 | |
|                   disabled={validatingOpenAI || !config.openaiApiKey}
 | |
|                   className="btn-outline whitespace-nowrap"
 | |
|                 >
 | |
|                   {validatingOpenAI ? (
 | |
|                     <ArrowPathIcon className="h-4 w-4 animate-spin" />
 | |
|                   ) : (
 | |
|                     'Validate'
 | |
|                   )}
 | |
|                 </button>
 | |
|               </div>
 | |
|               {openaiValid === true && (
 | |
|                 <div className="flex items-center mt-1 text-green-600 text-sm">
 | |
|                   <CheckCircleIcon className="h-4 w-4 mr-1" />
 | |
|                   API key valid
 | |
|                 </div>
 | |
|               )}
 | |
|               {openaiValid === false && (
 | |
|                 <div className="flex items-center mt-1 text-red-600 text-sm">
 | |
|                   <ExclamationTriangleIcon className="h-4 w-4 mr-1" />
 | |
|                   Invalid API key
 | |
|                 </div>
 | |
|               )}
 | |
|             </div>
 | |
| 
 | |
|             <div>
 | |
|               <label className="label">Organization (Optional)</label>
 | |
|               <input
 | |
|                 type="text"
 | |
|                 value={config.openaiOrganization}
 | |
|                 onChange={(e) => setConfig(prev => ({ ...prev, openaiOrganization: e.target.value }))}
 | |
|                 placeholder="org-..."
 | |
|                 className="input-field"
 | |
|               />
 | |
|             </div>
 | |
| 
 | |
|             <div>
 | |
|               <label className="label">Default Model</label>
 | |
|               <select
 | |
|                 value={config.openaiDefaultModel}
 | |
|                 onChange={(e) => setConfig(prev => ({ ...prev, openaiDefaultModel: e.target.value }))}
 | |
|                 className="input-field"
 | |
|               >
 | |
|                 <option value="gpt-4">GPT-4</option>
 | |
|                 <option value="gpt-4-turbo">GPT-4 Turbo</option>
 | |
|                 <option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
 | |
|               </select>
 | |
|             </div>
 | |
|           </div>
 | |
|         )}
 | |
|       </div>
 | |
| 
 | |
|       {/* Cost Management */}
 | |
|       {config.openaiEnabled && (
 | |
|         <div className="bg-white border border-gray-200 rounded-lg p-6">
 | |
|           <h3 className="text-lg font-medium text-gray-900 mb-4 flex items-center">
 | |
|             <CurrencyDollarIcon className="h-6 w-6 text-bzzz-primary mr-2" />
 | |
|             Cost Management
 | |
|           </h3>
 | |
|           
 | |
|           <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
 | |
|             <div>
 | |
|               <label className="label">Daily Cost Limit ($)</label>
 | |
|               <input
 | |
|                 type="number"
 | |
|                 value={config.dailyCostLimit}
 | |
|                 onChange={(e) => setConfig(prev => ({ ...prev, dailyCostLimit: parseFloat(e.target.value) || 0 }))}
 | |
|                 min="0"
 | |
|                 step="0.01"
 | |
|                 className="input-field"
 | |
|               />
 | |
|             </div>
 | |
| 
 | |
|             <div>
 | |
|               <label className="label">Monthly Cost Limit ($)</label>
 | |
|               <input
 | |
|                 type="number"
 | |
|                 value={config.monthlyCostLimit}
 | |
|                 onChange={(e) => setConfig(prev => ({ ...prev, monthlyCostLimit: parseFloat(e.target.value) || 0 }))}
 | |
|                 min="0"
 | |
|                 step="0.01"
 | |
|                 className="input-field"
 | |
|               />
 | |
|             </div>
 | |
|           </div>
 | |
| 
 | |
|           <div className="mt-4">
 | |
|             <div className="flex items-center">
 | |
|               <input
 | |
|                 type="checkbox"
 | |
|                 id="costAlerts"
 | |
|                 checked={config.costAlerts}
 | |
|                 onChange={(e) => setConfig(prev => ({ ...prev, costAlerts: e.target.checked }))}
 | |
|                 className="h-4 w-4 text-bzzz-primary focus:ring-bzzz-primary border-gray-300 rounded"
 | |
|               />
 | |
|               <label htmlFor="costAlerts" className="ml-2 text-sm font-medium text-gray-700">
 | |
|                 Send alerts when approaching cost limits
 | |
|               </label>
 | |
|             </div>
 | |
|           </div>
 | |
|         </div>
 | |
|       )}
 | |
| 
 | |
|       {/* Provider Preference */}
 | |
|       <div className="bg-white border border-gray-200 rounded-lg p-6">
 | |
|         <h3 className="text-lg font-medium text-gray-900 mb-4">Provider Preference</h3>
 | |
|         
 | |
|         <div className="space-y-3">
 | |
|           <div>
 | |
|             <label className="label">Preferred AI Provider</label>
 | |
|             <select
 | |
|               value={config.preferredProvider}
 | |
|               onChange={(e) => setConfig(prev => ({ ...prev, preferredProvider: e.target.value as 'openai' | 'local' | 'hybrid' }))}
 | |
|               className="input-field"
 | |
|             >
 | |
|               <option value="local">Local AI Only</option>
 | |
|               <option value="openai">OpenAI Only</option>
 | |
|               <option value="hybrid">Hybrid (Local first, OpenAI fallback)</option>
 | |
|             </select>
 | |
|           </div>
 | |
| 
 | |
|           <div className="flex items-center">
 | |
|             <input
 | |
|               type="checkbox"
 | |
|               id="fallbackEnabled"
 | |
|               checked={config.fallbackEnabled}
 | |
|               onChange={(e) => setConfig(prev => ({ ...prev, fallbackEnabled: e.target.checked }))}
 | |
|               className="h-4 w-4 text-bzzz-primary focus:ring-bzzz-primary border-gray-300 rounded"
 | |
|             />
 | |
|             <label htmlFor="fallbackEnabled" className="ml-2 text-sm font-medium text-gray-700">
 | |
|               Enable automatic fallback between providers
 | |
|             </label>
 | |
|           </div>
 | |
|         </div>
 | |
|       </div>
 | |
| 
 | |
|       {/* Action Buttons */}
 | |
|       <div className="flex justify-between pt-6 border-t border-gray-200">
 | |
|         <div>
 | |
|           {onBack && (
 | |
|             <button type="button" onClick={onBack} className="btn-outline">
 | |
|               Back
 | |
|             </button>
 | |
|           )}
 | |
|         </div>
 | |
|         
 | |
|         <button
 | |
|           type="submit"
 | |
|           className="btn-primary"
 | |
|           disabled={!config.openaiEnabled && !config.localAIEnabled}
 | |
|         >
 | |
|           {isCompleted ? 'Continue' : 'Next: Resource Allocation'}
 | |
|         </button>
 | |
|       </div>
 | |
|     </form>
 | |
|   )
 | |
| } |