From a5a192d334d53262a1d5a7c3acc8dc71619fe8ec Mon Sep 17 00:00:00 2001 From: Bentlybro Date: Sat, 4 Apr 2026 19:39:46 +0000 Subject: [PATCH] feat(frontend): Wire up migration system - usage tracking, safe delete/disable, migration list/revert - toggleLlmModelAction now sends migration params (migrate_to_slug, reason, custom_credit_cost) - deleteLlmModelAction passes replacement_model_slug query param - fetchLlmModelUsage calls real /llm/models/{slug}/usage endpoint - fetchLlmMigrations calls real /llm/migrations endpoint - revertLlmMigrationAction calls /llm/migrations/{id}/revert - Fix model usage calls to use slug instead of UUID --- .../src/app/(platform)/admin/llms/actions.ts | 53 ++++++++++++++----- .../llms/components/DeleteModelModal.tsx | 2 +- .../llms/components/DisableModelModal.tsx | 2 +- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/autogpt_platform/frontend/src/app/(platform)/admin/llms/actions.ts b/autogpt_platform/frontend/src/app/(platform)/admin/llms/actions.ts index 779bfba81e..944a300517 100644 --- a/autogpt_platform/frontend/src/app/(platform)/admin/llms/actions.ts +++ b/autogpt_platform/frontend/src/app/(platform)/admin/llms/actions.ts @@ -264,37 +264,64 @@ export async function toggleLlmModelAction(formData: FormData): Promise { const modelSlug = getRequiredFormField(formData, "model_id", "Model"); const shouldEnable = formData.get("is_enabled") === "true"; - // Toggle is just a PATCH on is_enabled - await adminFetch(`/api/llm/models/${modelSlug}`, { - method: "PATCH", - body: JSON.stringify({ is_enabled: shouldEnable }), + const payload: Record = { is_enabled: shouldEnable }; + + // Migration params (only when disabling) + if (!shouldEnable) { + const migrateToSlug = formData.get("migrate_to_slug"); + if (migrateToSlug) payload.migrate_to_slug = String(migrateToSlug); + const reason = formData.get("migration_reason"); + if (reason) payload.migration_reason = String(reason); + const customCost = formData.get("custom_credit_cost"); + if (customCost) payload.custom_credit_cost = Number(customCost); + } + + await adminFetch(`/api/llm/models/${modelSlug}/toggle`, { + method: "POST", + body: JSON.stringify(payload), }); revalidatePath(ADMIN_LLM_PATH); } export async function deleteLlmModelAction(formData: FormData): Promise { const modelSlug = getRequiredFormField(formData, "model_id", "Model"); - await adminFetch(`/api/llm/models/${modelSlug}`, { method: "DELETE" }); + const replacementSlug = formData.get("replacement_model_slug"); + const params = new URLSearchParams(); + if (replacementSlug) params.set("replacement_model_slug", String(replacementSlug)); + const query = params.toString() ? `?${params.toString()}` : ""; + await adminFetch(`/api/llm/models/${modelSlug}${query}`, { method: "DELETE" }); revalidatePath(ADMIN_LLM_PATH); } -export async function fetchLlmModelUsage(_modelId: string) { - // Not yet implemented in backend - return 0 - return { usage_count: 0 }; +export async function fetchLlmModelUsage(modelSlug: string) { + const { data } = await adminFetch(`/api/llm/models/${modelSlug}/usage`); + return data; } // ============================================================================= -// Migration Actions (not yet implemented in backend) +// Migration Actions // ============================================================================= -export async function fetchLlmMigrations(_includeReverted: boolean = false) { - return { migrations: [] }; +export async function fetchLlmMigrations(includeReverted: boolean = false) { + const params = new URLSearchParams(); + if (includeReverted) params.set("include_reverted", "true"); + const query = params.toString() ? `?${params.toString()}` : ""; + const { data } = await adminFetch(`/api/llm/migrations${query}`); + return data; } export async function revertLlmMigrationAction( - _formData: FormData, + formData: FormData, ): Promise { - throw new Error("Migrations not yet implemented"); + const migrationId = getRequiredFormField( + formData, + "migration_id", + "Migration", + ); + await adminFetch(`/api/llm/migrations/${migrationId}/revert`, { + method: "POST", + }); + revalidatePath(ADMIN_LLM_PATH); } // ============================================================================= diff --git a/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DeleteModelModal.tsx b/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DeleteModelModal.tsx index 53ee80f67f..602e666ccf 100644 --- a/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DeleteModelModal.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DeleteModelModal.tsx @@ -35,7 +35,7 @@ export function DeleteModelModal({ setUsageLoading(true); setUsageError(null); try { - const usage = await fetchLlmModelUsage(model.id); + const usage = await fetchLlmModelUsage(model.slug); setUsageCount(usage.usage_count); } catch (err) { console.error("Failed to fetch model usage:", err); diff --git a/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DisableModelModal.tsx b/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DisableModelModal.tsx index 8101db72f9..a1e645f497 100644 --- a/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DisableModelModal.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/admin/llms/components/DisableModelModal.tsx @@ -31,7 +31,7 @@ export function DisableModelModal({ async function fetchUsage() { try { - const usage = await fetchLlmModelUsage(model.id); + const usage = await fetchLlmModelUsage(model.slug); setUsageCount(usage.usage_count); } catch { setUsageCount(null);