Refactor LLM registry admin backend and frontend

Refactored backend imports and test mocks to use new admin LLM routes location. Cleaned up and reordered imports for clarity and consistency. Improved code formatting and readability across backend and frontend files. Renamed useLlmRegistryPage to getLlmRegistryPageData for clarity and updated all usages. No functional changes to business logic.
This commit is contained in:
Bentlybro
2026-01-06 12:57:33 +00:00
parent 0d321323f5
commit 9b8b6252c5
29 changed files with 145 additions and 116 deletions

View File

@@ -175,13 +175,15 @@ async def get_execution_analytics_config(
# Get all models from the registry (dynamic, not hardcoded enum)
from backend.data import llm_registry
recommended_model = "gpt-4o-mini" # Using string value - enum accepts any model slug dynamically
recommended_model = (
"gpt-4o-mini" # Using string value - enum accepts any model slug dynamically
)
for registry_model in llm_registry.iter_dynamic_models():
# Only include enabled models in the list
if not registry_model.is_enabled:
continue
model_enum = LlmModel(registry_model.slug) # Create enum instance from slug
label = generate_model_label(model_enum)
# Add "(Recommended)" suffix to the recommended model

View File

@@ -51,9 +51,7 @@ async def _refresh_runtime_state() -> None:
logger.debug("Could not clear v2 builder cache: %s", e)
# Notify all executor services to refresh their registry cache
from backend.data.llm_registry import (
publish_registry_refresh_notification,
)
from backend.data.llm_registry import publish_registry_refresh_notification
publish_registry_refresh_notification()
logger.info("Published registry refresh notification")

View File

@@ -7,7 +7,7 @@ import pytest_mock
from autogpt_libs.auth.jwt_utils import get_jwt_payload
from pytest_snapshot.plugin import Snapshot
import backend.server.v2.admin.llm_routes as llm_routes
import backend.api.features.admin.llm_routes as llm_routes
app = fastapi.FastAPI()
app.include_router(llm_routes.router)
@@ -57,7 +57,7 @@ def test_list_llm_providers_success(
]
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.list_providers",
"backend.api.features.admin.llm_routes.llm_db.list_providers",
new=AsyncMock(return_value=mock_providers),
)
@@ -102,7 +102,7 @@ def test_list_llm_models_success(
]
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.list_models",
"backend.api.features.admin.llm_routes.llm_db.list_models",
new=AsyncMock(return_value=mock_models),
)
@@ -135,12 +135,12 @@ def test_create_llm_provider_success(
}
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.upsert_provider",
"backend.api.features.admin.llm_routes.llm_db.upsert_provider",
new=AsyncMock(return_value=mock_provider),
)
mock_refresh = mocker.patch(
"backend.server.v2.admin.llm_routes._refresh_runtime_state",
"backend.api.features.admin.llm_routes._refresh_runtime_state",
new=AsyncMock(),
)
@@ -196,12 +196,12 @@ def test_create_llm_model_success(
}
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.create_model",
"backend.api.features.admin.llm_routes.llm_db.create_model",
new=AsyncMock(return_value=mock_model),
)
mock_refresh = mocker.patch(
"backend.server.v2.admin.llm_routes._refresh_runtime_state",
"backend.api.features.admin.llm_routes._refresh_runtime_state",
new=AsyncMock(),
)
@@ -265,12 +265,12 @@ def test_update_llm_model_success(
}
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.update_model",
"backend.api.features.admin.llm_routes.llm_db.update_model",
new=AsyncMock(return_value=mock_model),
)
mock_refresh = mocker.patch(
"backend.server.v2.admin.llm_routes._refresh_runtime_state",
"backend.api.features.admin.llm_routes._refresh_runtime_state",
new=AsyncMock(),
)
@@ -315,12 +315,12 @@ def test_toggle_llm_model_success(
}
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.toggle_model",
"backend.api.features.admin.llm_routes.llm_db.toggle_model",
new=AsyncMock(return_value=mock_model),
)
mock_refresh = mocker.patch(
"backend.server.v2.admin.llm_routes._refresh_runtime_state",
"backend.api.features.admin.llm_routes._refresh_runtime_state",
new=AsyncMock(),
)
@@ -354,12 +354,12 @@ def test_delete_llm_model_success(
}
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.delete_model",
"backend.api.features.admin.llm_routes.llm_db.delete_model",
new=AsyncMock(return_value=type("obj", (object,), mock_response)()),
)
mock_refresh = mocker.patch(
"backend.server.v2.admin.llm_routes._refresh_runtime_state",
"backend.api.features.admin.llm_routes._refresh_runtime_state",
new=AsyncMock(),
)
@@ -385,7 +385,7 @@ def test_delete_llm_model_validation_error(
) -> None:
"""Test deletion fails with proper error when validation fails"""
mocker.patch(
"backend.server.v2.admin.llm_routes.llm_db.delete_model",
"backend.api.features.admin.llm_routes.llm_db.delete_model",
new=AsyncMock(side_effect=ValueError("Replacement model 'invalid' not found")),
)

View File

@@ -14,8 +14,8 @@ import backend.data.block
from backend.blocks import load_all_blocks
from backend.blocks.llm import LlmModel
from backend.data.block import AnyBlockSchema, BlockCategory, BlockInfo, BlockSchema
from backend.data.llm_registry import get_all_model_slugs_for_validation
from backend.data.db import query_raw_with_schema
from backend.data.llm_registry import get_all_model_slugs_for_validation
from backend.integrations.providers import ProviderName
from backend.util.cache import cached
from backend.util.models import Pagination
@@ -36,7 +36,10 @@ logger = logging.getLogger(__name__)
def _get_llm_models() -> list[str]:
"""Get LLM model names for search matching from the registry."""
return [slug.lower().replace("-", " ") for slug in get_all_model_slugs_for_validation()]
return [
slug.lower().replace("-", " ") for slug in get_all_model_slugs_for_validation()
]
MAX_LIBRARY_AGENT_RESULTS = 100
MAX_MARKETPLACE_AGENT_RESULTS = 100

View File

@@ -18,6 +18,7 @@ from prisma.errors import PrismaError
import backend.api.features.admin.credit_admin_routes
import backend.api.features.admin.execution_analytics_routes
import backend.api.features.admin.llm_routes
import backend.api.features.admin.store_admin_routes
import backend.api.features.builder
import backend.api.features.builder.routes
@@ -37,7 +38,6 @@ import backend.data.db
import backend.data.graph
import backend.data.user
import backend.integrations.webhooks.utils
import backend.api.features.admin.llm_routes
import backend.server.v2.llm.routes as public_llm_routes
import backend.util.service
import backend.util.settings
@@ -127,7 +127,10 @@ async def lifespan_context(app: fastapi.FastAPI):
await backend.data.graph.fix_llm_provider_credentials()
# migrate_llm_models uses registry default model
from backend.blocks.llm import LlmModel
await backend.data.graph.migrate_llm_models(LlmModel(llm_registry.get_default_model_slug()))
await backend.data.graph.migrate_llm_models(
LlmModel(llm_registry.get_default_model_slug())
)
await backend.integrations.webhooks.utils.migrate_legacy_triggered_graphs()
with launch_darkly_context():

View File

@@ -1,7 +1,6 @@
from typing import Any
from backend.blocks.llm import (
DEFAULT_LLM_MODEL,
TEST_CREDENTIALS,
TEST_CREDENTIALS_INPUT,
AIBlockBase,

View File

@@ -25,6 +25,7 @@ from prisma.models import AgentBlock
from prisma.types import AgentBlockCreateInput
from pydantic import BaseModel
from backend.data.llm_registry import update_schema_with_llm_registry
from backend.data.model import NodeExecutionStats
from backend.integrations.providers import ProviderName
from backend.util import json
@@ -38,7 +39,6 @@ from backend.util.exceptions import (
)
from backend.util.settings import Config
from backend.data.llm_registry import update_schema_with_llm_registry
from .model import (
ContributorDetails,
Credentials,

View File

@@ -1445,6 +1445,7 @@ async def migrate_llm_models(migrate_to: LlmModel):
# Get all model slugs from the registry (dynamic, not hardcoded enum)
from backend.data import llm_registry
enum_values = list(llm_registry.get_all_model_slugs_for_validation())
escaped_enum_values = repr(tuple(enum_values)) # hack but works

View File

@@ -6,8 +6,13 @@ replacing hardcoded model configurations with a flexible admin-managed system.
"""
from backend.data.llm_registry.model_types import ModelMetadata
from backend.data.llm_registry import notifications
from backend.data.llm_registry import schema_utils
# Re-export for backwards compatibility
from backend.data.llm_registry.notifications import (
REGISTRY_REFRESH_CHANNEL,
publish_registry_refresh_notification,
subscribe_to_registry_refresh,
)
from backend.data.llm_registry.registry import (
RegistryModel,
RegistryModelCost,
@@ -27,13 +32,6 @@ from backend.data.llm_registry.registry import (
register_static_costs,
register_static_metadata,
)
# Re-export for backwards compatibility
from backend.data.llm_registry.notifications import (
REGISTRY_REFRESH_CHANNEL,
publish_registry_refresh_notification,
subscribe_to_registry_refresh,
)
from backend.data.llm_registry.schema_utils import (
is_llm_model_field,
refresh_llm_discriminator_mapping,
@@ -72,4 +70,3 @@ __all__ = [
"refresh_llm_model_options",
"update_schema_with_llm_registry",
]

View File

@@ -9,4 +9,3 @@ class ModelMetadata(NamedTuple):
provider: str
context_window: int
max_output_tokens: int | None

View File

@@ -89,4 +89,3 @@ async def subscribe_to_registry_refresh(
exc_info=True,
)
raise

View File

@@ -338,4 +338,3 @@ def get_default_model_slug() -> str:
# Fallback to preferred slug even if not in registry (for backwards compatibility)
return preferred_slug

View File

@@ -128,4 +128,3 @@ def update_schema_with_llm_registry(
field_name,
exc,
)

View File

@@ -40,12 +40,11 @@ from pydantic_core import (
)
from typing_extensions import TypedDict
from backend.data.llm_registry import update_schema_with_llm_registry
from backend.integrations.providers import ProviderName
from backend.util.json import loads as json_loads
from backend.util.settings import Secrets
from backend.data.llm_registry import update_schema_with_llm_registry
# Type alias for any provider name (including custom ones)
AnyProviderName = str # Will be validated as ProviderName at runtime
USER_TIMEZONE_NOT_SET = "not-set"

View File

@@ -2,7 +2,8 @@ from __future__ import annotations
from typing import Any, Iterable, Sequence
import prisma, prisma.models
import prisma
import prisma.models
from backend.data.db import transaction
from backend.server.v2.llm import model as llm_model
@@ -511,7 +512,9 @@ async def delete_model(
)
def _map_migration(record: prisma.models.LlmModelMigration) -> llm_model.LlmModelMigration:
def _map_migration(
record: prisma.models.LlmModelMigration,
) -> llm_model.LlmModelMigration:
return llm_model.LlmModelMigration(
id=record.id,
source_model_slug=record.sourceModelSlug,

View File

@@ -178,7 +178,9 @@ class LlmMigrationsResponse(pydantic.BaseModel):
class RevertMigrationRequest(pydantic.BaseModel):
re_enable_source_model: bool = True # Whether to re-enable the source model if disabled
re_enable_source_model: bool = (
True # Whether to re-enable the source model if disabled
)
class RevertMigrationResponse(pydantic.BaseModel):
@@ -186,6 +188,8 @@ class RevertMigrationResponse(pydantic.BaseModel):
source_model_slug: str
target_model_slug: str
nodes_reverted: int
nodes_already_changed: int = 0 # Nodes that were modified since migration (not reverted)
nodes_already_changed: int = (
0 # Nodes that were modified since migration (not reverted)
)
source_model_re_enabled: bool = False # Whether the source model was re-enabled
message: str

View File

@@ -89,11 +89,15 @@ export async function createLlmModelAction(formData: FormData) {
const creatorId = formData.get("creator_id");
// Fetch provider to get default credentials
const providersResponse = await getV2ListLlmProviders({ include_models: false });
const providersResponse = await getV2ListLlmProviders({
include_models: false,
});
if (providersResponse.status !== 200) {
throw new Error("Failed to fetch providers");
}
const provider = providersResponse.data.providers.find((p) => p.id === providerId);
const provider = providersResponse.data.providers.find(
(p) => p.id === providerId,
);
if (!provider) {
throw new Error("Provider not found");
@@ -222,7 +226,9 @@ export async function deleteLlmModelAction(formData: FormData) {
return response.data;
}
export async function fetchLlmModelUsage(modelId: string): Promise<LlmModelUsageResponse> {
export async function fetchLlmModelUsage(
modelId: string,
): Promise<LlmModelUsageResponse> {
const response = await getV2GetModelUsageCount(modelId);
if (response.status !== 200) {
throw new Error("Failed to fetch model usage");
@@ -235,16 +241,20 @@ export async function fetchLlmModelUsage(modelId: string): Promise<LlmModelUsage
// =============================================================================
export async function fetchLlmMigrations(
includeReverted: boolean = false
includeReverted: boolean = false,
): Promise<LlmMigrationsResponse> {
const response = await getV2ListModelMigrations({ include_reverted: includeReverted });
const response = await getV2ListModelMigrations({
include_reverted: includeReverted,
});
if (response.status !== 200) {
throw new Error("Failed to fetch migrations");
}
return response.data;
}
export async function revertLlmMigrationAction(formData: FormData): Promise<void> {
export async function revertLlmMigrationAction(
formData: FormData,
): Promise<void> {
const migrationId = String(formData.get("migration_id"));
const response = await postV2RevertAModelMigration(migrationId, null);
@@ -266,7 +276,9 @@ export async function fetchLlmCreators(): Promise<LlmCreatorsResponse> {
return response.data;
}
export async function createLlmCreatorAction(formData: FormData): Promise<void> {
export async function createLlmCreatorAction(
formData: FormData,
): Promise<void> {
const payload: UpsertLlmCreatorRequest = {
name: String(formData.get("name") || "").trim(),
display_name: String(formData.get("display_name") || "").trim(),
@@ -289,7 +301,9 @@ export async function createLlmCreatorAction(formData: FormData): Promise<void>
revalidatePath(ADMIN_LLM_PATH);
}
export async function updateLlmCreatorAction(formData: FormData): Promise<void> {
export async function updateLlmCreatorAction(
formData: FormData,
): Promise<void> {
const creatorId = String(formData.get("creator_id"));
const payload: UpsertLlmCreatorRequest = {
name: String(formData.get("name") || "").trim(),
@@ -313,7 +327,9 @@ export async function updateLlmCreatorAction(formData: FormData): Promise<void>
revalidatePath(ADMIN_LLM_PATH);
}
export async function deleteLlmCreatorAction(formData: FormData): Promise<void> {
export async function deleteLlmCreatorAction(
formData: FormData,
): Promise<void> {
const creatorId = String(formData.get("creator_id"));
const response = await deleteV2DeleteModelCreator(creatorId);

View File

@@ -63,7 +63,9 @@ export function AddModelModal({ providers, creators }: Props) {
<h3 className="text-sm font-semibold text-foreground">
Basic Information
</h3>
<p className="text-xs text-muted-foreground">Core model details</p>
<p className="text-xs text-muted-foreground">
Core model details
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
@@ -244,8 +246,8 @@ export function AddModelModal({ providers, creators }: Props) {
</div>
</div>
<p className="text-xs text-muted-foreground">
Credit cost is always in platform credits. Credentials are inherited
from the selected provider.
Credit cost is always in platform credits. Credentials are
inherited from the selected provider.
</p>
</div>
@@ -285,4 +287,3 @@ export function AddModelModal({ providers, creators }: Props) {
</Dialog>
);
}

View File

@@ -33,8 +33,8 @@ export function AddProviderModal() {
Before Adding a Provider
</h4>
<p className="text-xs text-muted-foreground">
To use a new provider, you must first configure its credentials in the
backend:
To use a new provider, you must first configure its credentials in
the backend:
</p>
<ol className="list-inside list-decimal space-y-1 text-xs text-muted-foreground">
<li>
@@ -55,8 +55,9 @@ export function AddProviderModal() {
</code>
</li>
<li>
Use the <strong>same provider name</strong> in the &quot;Credential Provider&quot;
field below that matches the key in{" "}
Use the <strong>same provider name</strong> in the
&quot;Credential Provider&quot; field below that matches the key
in{" "}
<code className="rounded bg-muted px-1 py-0.5 font-mono">
PROVIDER_CREDENTIALS
</code>
@@ -79,7 +80,9 @@ export function AddProviderModal() {
<h3 className="text-sm font-semibold text-foreground">
Basic Information
</h3>
<p className="text-xs text-muted-foreground">Core provider details</p>
<p className="text-xs text-muted-foreground">
Core provider details
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
@@ -158,7 +161,8 @@ export function AddProviderModal() {
placeholder="openai"
/>
<p className="text-xs text-muted-foreground">
<strong>Important:</strong> This must exactly match the key in the{" "}
<strong>Important:</strong> This must exactly match the key in
the{" "}
<code className="rounded bg-muted px-1 py-0.5 font-mono text-xs">
PROVIDER_CREDENTIALS
</code>{" "}
@@ -166,7 +170,8 @@ export function AddProviderModal() {
<code className="rounded bg-muted px-1 py-0.5 font-mono text-xs">
block_cost_config.py
</code>
. Common values: "openai", "anthropic", "groq", "open_router", etc.
. Common values: &quot;openai&quot;, &quot;anthropic&quot;,
&quot;groq&quot;, &quot;open_router&quot;, etc.
</p>
</div>
</div>
@@ -177,7 +182,9 @@ export function AddProviderModal() {
<h3 className="text-sm font-semibold text-foreground">
Capabilities
</h3>
<p className="text-xs text-muted-foreground">Provider feature flags</p>
<p className="text-xs text-muted-foreground">
Provider feature flags
</p>
</div>
<div className="grid gap-3 sm:grid-cols-2">
{[
@@ -233,4 +240,3 @@ export function AddProviderModal() {
</Dialog>
);
}

View File

@@ -12,10 +12,7 @@ import {
} from "@/components/atoms/Table/Table";
import { Button } from "@/components/atoms/Button/Button";
import { Dialog } from "@/components/molecules/Dialog/Dialog";
import {
deleteLlmCreatorAction,
updateLlmCreatorAction,
} from "../actions";
import { deleteLlmCreatorAction, updateLlmCreatorAction } from "../actions";
import { useRouter } from "next/navigation";
export function CreatorsTable({ creators }: { creators: LlmModelCreator[] }) {
@@ -174,7 +171,7 @@ function DeleteCreatorButton({ creatorId }: { creatorId: string }) {
action={async (formData) => {
if (
confirm(
"Delete this creator? Models using this creator will have their creator set to none."
"Delete this creator? Models using this creator will have their creator set to none.",
)
) {
await deleteLlmCreatorAction(formData);

View File

@@ -21,7 +21,7 @@ export function DeleteModelModal({
// Filter out the current model and disabled models from replacement options
const replacementOptions = availableModels.filter(
(m) => m.id !== model.id && m.is_enabled
(m) => m.id !== model.id && m.is_enabled,
);
async function fetchUsage() {
@@ -80,7 +80,9 @@ export function DeleteModelModal({
<div className="space-y-4">
<div className="rounded-lg border border-amber-500/30 bg-amber-500/10 p-4 dark:border-amber-400/30 dark:bg-amber-400/10">
<div className="flex items-start gap-3">
<div className="flex-shrink-0 text-amber-600 dark:text-amber-400"></div>
<div className="flex-shrink-0 text-amber-600 dark:text-amber-400">
</div>
<div className="text-sm text-foreground">
<p className="font-semibold">You are about to delete:</p>
<p className="mt-1">
@@ -89,7 +91,8 @@ export function DeleteModelModal({
</p>
{usageCount !== null && (
<p className="mt-2 font-semibold">
Impact: {usageCount} block{usageCount !== 1 ? "s" : ""} currently use this model
Impact: {usageCount} block{usageCount !== 1 ? "s" : ""}{" "}
currently use this model
</p>
)}
<p className="mt-2 text-muted-foreground">
@@ -109,8 +112,9 @@ export function DeleteModelModal({
/>
<label className="text-sm font-medium">
<span className="block mb-2">
Select Replacement Model <span className="text-destructive">*</span>
<span className="mb-2 block">
Select Replacement Model{" "}
<span className="text-destructive">*</span>
</span>
<select
required

View File

@@ -24,7 +24,7 @@ export function DisableModelModal({
// Filter out the current model and disabled models from replacement options
const migrationOptions = availableModels.filter(
(m) => m.id !== model.id && m.is_enabled
(m) => m.id !== model.id && m.is_enabled,
);
async function fetchUsage() {
@@ -76,7 +76,12 @@ export function DisableModelModal({
styling={{ maxWidth: "600px" }}
>
<Dialog.Trigger>
<Button type="button" variant="outline" size="small" className="min-w-0">
<Button
type="button"
variant="outline"
size="small"
className="min-w-0"
>
Disable
</Button>
</Dialog.Trigger>
@@ -88,14 +93,14 @@ export function DisableModelModal({
<div className="space-y-4">
<div className="rounded-lg border border-amber-500/30 bg-amber-500/10 p-4 dark:border-amber-400/30 dark:bg-amber-400/10">
<div className="flex items-start gap-3">
<div className="flex-shrink-0 text-amber-600 dark:text-amber-400"></div>
<div className="flex-shrink-0 text-amber-600 dark:text-amber-400">
</div>
<div className="text-sm text-foreground">
<p className="font-semibold">You are about to disable:</p>
<p className="mt-1">
<span className="font-medium">{model.display_name}</span>{" "}
<span className="text-muted-foreground">
({model.slug})
</span>
<span className="text-muted-foreground">({model.slug})</span>
</p>
{usageCount === null ? (
<p className="mt-2 text-muted-foreground">
@@ -116,7 +121,7 @@ export function DisableModelModal({
</div>
{hasUsage && (
<div className="rounded-lg border border-border bg-muted/50 p-4 space-y-4">
<div className="space-y-4 rounded-lg border border-border bg-muted/50 p-4">
<label className="flex items-start gap-3">
<input
type="checkbox"
@@ -134,18 +139,19 @@ export function DisableModelModal({
Migrate existing workflows to another model
</span>
<p className="mt-1 text-muted-foreground">
Creates a revertible migration record. If unchecked, existing
workflows will use automatic fallback to an enabled model from
the same provider.
Creates a revertible migration record. If unchecked,
existing workflows will use automatic fallback to an enabled
model from the same provider.
</p>
</div>
</label>
{wantsMigration && (
<div className="space-y-4 border-t border-border pt-4">
<label className="text-sm font-medium block">
<label className="block text-sm font-medium">
<span className="mb-2 block">
Replacement Model <span className="text-destructive">*</span>
Replacement Model{" "}
<span className="text-destructive">*</span>
</span>
<select
required
@@ -167,10 +173,10 @@ export function DisableModelModal({
)}
</label>
<label className="text-sm font-medium block">
<label className="block text-sm font-medium">
<span className="mb-2 block">
Migration Reason{" "}
<span className="text-muted-foreground font-normal">
<span className="font-normal text-muted-foreground">
(optional)
</span>
</span>
@@ -186,10 +192,10 @@ export function DisableModelModal({
</p>
</label>
<label className="text-sm font-medium block">
<label className="block text-sm font-medium">
<span className="mb-2 block">
Custom Credit Cost{" "}
<span className="text-muted-foreground font-normal">
<span className="font-normal text-muted-foreground">
(optional)
</span>
</span>

View File

@@ -44,7 +44,7 @@ export function EditModelModal({
className="space-y-4"
>
<input type="hidden" name="model_id" value={model.id} />
<div className="grid gap-4 md:grid-cols-2">
<label className="text-sm font-medium">
Display Name
@@ -154,7 +154,9 @@ export function EditModelModal({
defaultValue={cost?.credential_provider ?? provider?.name ?? ""}
className="mt-1 w-full rounded border border-input bg-background p-2 text-sm"
>
<option value="" disabled>Select provider</option>
<option value="" disabled>
Select provider
</option>
{providers.map((p) => (
<option key={p.id} value={p.name}>
{p.display_name} ({p.name})
@@ -170,11 +172,7 @@ export function EditModelModal({
<input type="hidden" name="credential_type" value="api_key" />
<Dialog.Footer>
<Button
variant="ghost"
size="small"
onClick={() => setOpen(false)}
>
<Button variant="ghost" size="small" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button variant="primary" size="small" type="submit">
@@ -186,4 +184,3 @@ export function EditModelModal({
</Dialog>
);
}

View File

@@ -93,7 +93,11 @@ export function LlmRegistryDashboard({
</div>
<AddModelModal providers={providers} creators={creators} />
</div>
<ModelsTable models={models} providers={providers} creators={creators} />
<ModelsTable
models={models}
providers={providers}
creators={creators}
/>
</div>
</div>
</div>

View File

@@ -61,7 +61,7 @@ function MigrationRow({ migration }: { migration: LlmModelMigration }) {
await revertLlmMigrationAction(formData);
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to revert migration"
err instanceof Error ? err.message : "Failed to revert migration",
);
} finally {
setIsReverting(false);

View File

@@ -136,10 +136,7 @@ export function ModelsTable({
providers={providers}
creators={creators}
/>
<DeleteModelModal
model={model}
availableModels={models}
/>
<DeleteModelModal model={model} availableModels={models} />
</div>
</TableCell>
</TableRow>
@@ -162,5 +159,3 @@ function EnableModelButton({ modelId }: { modelId: string }) {
</form>
);
}

View File

@@ -69,4 +69,3 @@ export function ProviderList({ providers }: { providers: LlmProvider[] }) {
</div>
);
}

View File

@@ -1,9 +1,9 @@
import { withRoleAccess } from "@/lib/withRoleAccess";
import { useLlmRegistryPage } from "./useLlmRegistryPage";
import { getLlmRegistryPageData } from "./useLlmRegistryPage";
import { LlmRegistryDashboard } from "./components/LlmRegistryDashboard";
async function LlmRegistryPage() {
const data = await useLlmRegistryPage();
const data = await getLlmRegistryPageData();
return <LlmRegistryDashboard {...data} />;
}

View File

@@ -1,5 +1,5 @@
/**
* Hook for LLM Registry page data fetching and state management.
* Server-side data fetching for LLM Registry page.
*/
import {
@@ -9,7 +9,7 @@ import {
fetchLlmProviders,
} from "./actions";
export async function useLlmRegistryPage() {
export async function getLlmRegistryPageData() {
// Fetch providers and models (required)
const [providersResponse, modelsResponse] = await Promise.all([
fetchLlmProviders(),
@@ -44,4 +44,3 @@ export async function useLlmRegistryPage() {
creators,
};
}