'use client'; import React, { useState } from 'react'; import { useMutation, useQuery } from '@tanstack/react-query'; import { type Model } from '@/lib/api/schemas/models'; import { ModelService } from '@/lib/api/services/models'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Badge } from '@/components/ui/badge'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Loader2, Send, CheckCircle, XCircle, Info, Key, Globe, } from 'lucide-react'; import { toast } from 'sonner'; interface ModelTesterProps { models: Model[]; } interface ChatCompletionRequest { model: string; messages: { role: 'system' | 'user' | 'assistant'; content: string; }[]; max_tokens?: number; temperature?: number; } interface ChatCompletionResponse { id: string; object: string; created: number; model: string; choices: { index: number; message: { role: string; content: string; }; finish_reason: string; }[]; usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number; }; } const DEFAULT_SYSTEM_MESSAGE = 'You are a helpful assistant. Please respond concisely.'; const DEFAULT_USER_MESSAGE = 'Hello! Can you tell me what model you are and confirm that you are working correctly?'; export function ModelTester({ models }: ModelTesterProps) { const [selectedModelId, setSelectedModelId] = useState(''); const [systemMessage, setSystemMessage] = useState(DEFAULT_SYSTEM_MESSAGE); const [userMessage, setUserMessage] = useState(DEFAULT_USER_MESSAGE); const [maxTokens, setMaxTokens] = useState(150); const [temperature, setTemperature] = useState(0.7); const [response, setResponse] = useState(null); const [error, setError] = useState(null); // Fetch model groups for API key resolution const { data: groups = [] } = useQuery({ queryKey: ['model-groups'], queryFn: () => ModelService.getModelGroups(), refetchOnWindowFocus: false, }); const selectedModel = models.find((model) => model.id === selectedModelId); // Get effective API key and endpoint URL for the selected model const getModelCredentials = (model: Model) => { const group = groups.find((g) => g.provider === model.provider); // Determine API key (individual takes precedence over group) const apiKey = model.api_key || group?.group_api_key; // Determine endpoint URL let endpointUrl = model.url; // If model URL is relative and group has a base URL, combine them if (model.url.startsWith('/') && group?.group_url) { endpointUrl = `${group.group_url.replace(/\/$/, '')}${model.url}`; } // Ensure the URL ends with /chat/completions for chat models if ( model.modelType === 'text' && !endpointUrl.includes('/chat/completions') ) { endpointUrl = endpointUrl.replace(/\/$/, '') + '/chat/completions'; } return { apiKey, endpointUrl, group, }; }; const testModelMutation = useMutation({ mutationFn: async (request: ChatCompletionRequest) => { if (!selectedModel) { throw new Error('No model selected'); } setError(null); setResponse(null); try { console.log(`Testing model via proxy: ${selectedModel.name}`); console.log('Request payload:', request); const response = await ModelService.testModel( selectedModel.id, 'chat-completions', request as unknown as Record ); if (!response.success) { throw new Error(response.error || 'Test failed'); } return response.data as ChatCompletionResponse; } catch (err: unknown) { console.error('Model test error via proxy:', err); const errorMessage = err instanceof Error ? err.message : 'Failed to test model via proxy'; throw new Error(errorMessage); } }, onSuccess: (data) => { setResponse(data); toast.success('Model test completed successfully!'); }, onError: (err: Error) => { const errorMessage = err?.message || 'Unknown error occurred'; setError(errorMessage); toast.error(`Model test failed: ${errorMessage}`); }, }); const handleTest = async () => { if (!selectedModel) { toast.error('Please select a model to test'); return; } if (!userMessage.trim()) { toast.error('Please enter a test message'); return; } const messages = []; if (systemMessage.trim()) { messages.push({ role: 'system' as const, content: systemMessage.trim(), }); } messages.push({ role: 'user' as const, content: userMessage.trim(), }); const request: ChatCompletionRequest = { model: selectedModel.name, messages, max_tokens: maxTokens, temperature: temperature, }; testModelMutation.mutate(request); }; const enabledModels = models.filter((model) => model.isEnabled); const credentials = selectedModel ? getModelCredentials(selectedModel) : null; return ( Model Credential Tester Test model functionality by sending chat completion requests through the secure proxy (resolves CORS and network issues) {/* Model Selection */}
{selectedModel && credentials && (
Endpoint: {credentials.endpointUrl}
API Key:{' '} {credentials.apiKey ? `${credentials.apiKey.substring(0, 8)}...` : 'Not configured'} {selectedModel.api_key_type || 'Unknown'}
Provider: {selectedModel.provider}
Type: {selectedModel.modelType}
{selectedModel.contextLength && (
Context Length:{' '} {selectedModel.contextLength.toLocaleString()}
)} {!credentials.apiKey && ( No API key configured for this model. Testing may still work if the model is free or if authentication is handled elsewhere. For models requiring authentication, please add an API key to the model or its provider group. )}
)}
{/* Test Parameters */}
setMaxTokens(parseInt(e.target.value) || 150)} className='border-input bg-background w-full rounded-md border px-3 py-2 text-sm' />
setTemperature(parseFloat(e.target.value) || 0.7) } className='border-input bg-background w-full rounded-md border px-3 py-2 text-sm' />
{/* System Message */}