mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")),
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from typing import Any
|
||||
|
||||
from backend.blocks.llm import (
|
||||
DEFAULT_LLM_MODEL,
|
||||
TEST_CREDENTIALS,
|
||||
TEST_CREDENTIALS_INPUT,
|
||||
AIBlockBase,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -9,4 +9,3 @@ class ModelMetadata(NamedTuple):
|
||||
provider: str
|
||||
context_window: int
|
||||
max_output_tokens: int | None
|
||||
|
||||
|
||||
@@ -89,4 +89,3 @@ async def subscribe_to_registry_refresh(
|
||||
exc_info=True,
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -128,4 +128,3 @@ def update_schema_with_llm_registry(
|
||||
field_name,
|
||||
exc,
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 "Credential Provider"
|
||||
field below that matches the key in{" "}
|
||||
Use the <strong>same provider name</strong> in the
|
||||
"Credential Provider" 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: "openai", "anthropic",
|
||||
"groq", "open_router", 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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -69,4 +69,3 @@ export function ProviderList({ providers }: { providers: LlmProvider[] }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user