From 307e81bd61f40cd52dce94d5dc22cdbd305cd598 Mon Sep 17 00:00:00 2001 From: 9qeklajc <9qeklajc> Date: Fri, 24 Oct 2025 12:37:11 +0200 Subject: [PATCH] fix model update --- ui/components/EditModelForm.tsx | 361 +++++++++++++++++----- ui/components/ModelSelector.tsx | 22 +- ui/components/settings/admin-settings.tsx | 26 +- ui/lib/api/services/admin.ts | 55 +++- 4 files changed, 345 insertions(+), 119 deletions(-) diff --git a/ui/components/EditModelForm.tsx b/ui/components/EditModelForm.tsx index 8d908654..773829c8 100644 --- a/ui/components/EditModelForm.tsx +++ b/ui/components/EditModelForm.tsx @@ -3,11 +3,9 @@ import React, { useState, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; -import { - ManualModelSchema, - type ManualModel, - type Model, -} from '@/lib/api/schemas/models'; +import { z } from 'zod'; +import { type Model } from '@/lib/api/schemas/models'; +import { AdminService } from '@/lib/api/services/admin'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; @@ -29,63 +27,208 @@ import { } from '@/components/ui/form'; import { Edit3, Loader2 } from 'lucide-react'; import { toast } from 'sonner'; +import { Switch } from '@/components/ui/switch'; + +const EditModelFormSchema = z.object({ + name: z.string().min(1, 'Name is required'), + description: z.string().optional(), + context_length: z.coerce.number().min(0), + prompt: z.coerce.number().min(0), + completion: z.coerce.number().min(0), + request: z.coerce.number().min(0).optional(), + image: z.coerce.number().min(0).optional(), + enabled: z.boolean(), +}); + +type EditModelFormData = z.infer; interface EditModelFormProps { model: Model; - onModelUpdate: (modelId: string, updatedModel: ManualModel) => void; + providerId?: number; + onModelUpdate?: () => void; onCancel?: () => void; isOpen: boolean; } export function EditModelForm({ model, + providerId, onModelUpdate, onCancel, isOpen, }: EditModelFormProps) { const [isSubmitting, setIsSubmitting] = useState(false); + const [adminModelData, setAdminModelData] = useState(null); + const [isNewOverride, setIsNewOverride] = useState(false); - const form = useForm({ - resolver: zodResolver(ManualModelSchema) as any, // eslint-disable-line @typescript-eslint/no-explicit-any + const form = useForm({ + resolver: zodResolver(EditModelFormSchema), defaultValues: { name: model.name, - full_name: model.full_name, - input_cost: model.input_cost, - output_cost: model.output_cost, - provider: model.provider, - modelType: model.modelType as ManualModel['modelType'], description: model.description || '', - contextLength: model.contextLength || 0, + context_length: model.contextLength || 4096, + prompt: model.input_cost, + completion: model.output_cost, + request: 0, + image: 0, + enabled: model.isEnabled !== false, }, }); - // Reset form when model changes useEffect(() => { - form.reset({ - name: model.name, - full_name: model.full_name, - input_cost: model.input_cost, - output_cost: model.output_cost, - provider: model.provider, - modelType: model.modelType as ManualModel['modelType'], - description: model.description || '', - contextLength: model.contextLength || 0, - }); - }, [model, form]); + if (isOpen && providerId) { + loadAdminModel(); + } else if (isOpen && !providerId) { + console.error('EditModelForm opened without providerId', { + model, + providerId, + }); + toast.error('Missing provider information for this model'); + } + }, [isOpen, model.id, providerId]); + + const loadAdminModel = async () => { + if (!providerId) { + console.error('loadAdminModel called without providerId'); + return; + } + + try { + console.log('Loading admin model:', { + providerId, + modelId: model.full_name, + }); + + const adminModel = await AdminService.getProviderModel( + providerId, + model.full_name + ); + + setAdminModelData(adminModel); + setIsNewOverride(false); + + form.reset({ + name: adminModel.name, + description: adminModel.description || '', + context_length: adminModel.context_length, + prompt: adminModel.pricing.prompt || 0, + completion: adminModel.pricing.completion || 0, + request: adminModel.pricing.request || 0, + image: adminModel.pricing.image || 0, + enabled: adminModel.enabled !== false, + }); + } catch (error: any) { + console.log('Model not in database, will create new override:', error); + setIsNewOverride(true); + setAdminModelData({ + id: model.full_name, + name: model.name, + description: model.description || '', + created: Math.floor(Date.now() / 1000), + context_length: model.contextLength || 4096, + architecture: { + modality: model.modelType || 'text', + input_modalities: [model.modelType || 'text'], + output_modalities: [model.modelType || 'text'], + tokenizer: '', + instruct_type: null, + }, + pricing: { + prompt: model.input_cost, + completion: model.output_cost, + request: 0, + image: 0, + web_search: 0, + internal_reasoning: 0, + }, + per_request_limits: null, + top_provider: null, + upstream_provider_id: providerId, + enabled: model.isEnabled !== false, + }); + + form.reset({ + name: model.name, + description: model.description || '', + context_length: model.contextLength || 4096, + prompt: model.input_cost, + completion: model.output_cost, + request: 0, + image: 0, + enabled: model.isEnabled !== false, + }); + } + }; + + const onSubmit = async (data: EditModelFormData) => { + if (!providerId) { + console.error('onSubmit called without providerId', { + model, + providerId, + }); + toast.error('Missing provider ID - cannot update model'); + return; + } + + if (!adminModelData) { + console.error('onSubmit called without adminModelData', { + model, + providerId, + adminModelData, + }); + toast.error('Model data not loaded - please try reopening the form'); + return; + } - const onSubmit = async (data: ManualModel) => { setIsSubmitting(true); try { - // Ensure we're sending the correct API key value - const updatedData = { - ...data, + const payload = { + id: adminModelData.id, + name: data.name, + description: data.description || '', + created: adminModelData.created || Math.floor(Date.now() / 1000), + context_length: data.context_length, + architecture: adminModelData.architecture || { + modality: 'text', + input_modalities: ['text'], + output_modalities: ['text'], + tokenizer: '', + instruct_type: null, + }, + pricing: { + prompt: data.prompt, + completion: data.completion, + request: data.request || 0, + image: data.image || 0, + web_search: 0, + internal_reasoning: 0, + }, + per_request_limits: adminModelData.per_request_limits, + top_provider: adminModelData.top_provider, + upstream_provider_id: providerId, + enabled: data.enabled, }; - await onModelUpdate(model.id, updatedData); - toast.success('Model updated successfully!'); + + if (isNewOverride) { + console.log('Creating new model override'); + await AdminService.createProviderModel(providerId, payload); + toast.success('Model override created successfully!'); + } else { + console.log('Updating existing model override'); + await AdminService.updateProviderModel( + providerId, + adminModelData.id, + payload + ); + toast.success('Model updated successfully!'); + } + + onModelUpdate?.(); onCancel?.(); } catch (error) { - toast.error('Failed to update model. Please try again.'); - console.error('Error updating model:', error); + const action = isNewOverride ? 'create' : 'update'; + toast.error(`Failed to ${action} model. Please try again.`); + console.error(`Error ${action}ing model:`, error); } finally { setIsSubmitting(false); } @@ -103,33 +246,18 @@ export function EditModelForm({ - Edit Model + {isNewOverride ? 'Create Model Override' : 'Edit Model Override'} - Update the details for "{model.name}" + {isNewOverride + ? `Create an override for "${model.name}"` + : `Update the model override for "${model.name}"`}
- ( - - Original Model Name - - - - - Original name from the provider (cannot be changed) - - - - )} - /> - ( - Provider * + Context Length * - AI model provider or company + Maximum context window size @@ -173,10 +303,32 @@ export function EditModelForm({ />
+ ( + + Description + +