Files
cmhub/apps/ai/admin.py
T

116 lines
3.3 KiB
Python

from django import forms
from django.contrib import admin
from .models import AiModel, ModelAlias
from .security import AiKeyEncryptionError, encrypt_api_key
class AiModelAdminForm(forms.ModelForm):
api_key = forms.CharField(
label="API key",
required=False,
widget=forms.PasswordInput(render_value=False),
help_text="Leave blank to keep the existing encrypted key.",
)
class Meta:
model = AiModel
fields = (
"name",
"url",
"model",
"api_type",
"api_key",
"capabilities",
"timeout_seconds",
"connect_timeout_seconds",
"extra_body",
"is_active",
)
def clean(self):
cleaned_data = super().clean()
api_key = cleaned_data.get("api_key")
if not self.instance.pk and not api_key:
raise forms.ValidationError("API key is required when creating an AI model.")
if api_key:
try:
self._api_key_encrypted = encrypt_api_key(api_key)
except AiKeyEncryptionError as exc:
raise forms.ValidationError({"api_key": str(exc)}) from exc
else:
self._api_key_encrypted = ""
return cleaned_data
def save(self, commit=True):
instance = super().save(commit=False)
if self._api_key_encrypted:
instance.api_key_encrypted = self._api_key_encrypted
if commit:
instance.save()
self.save_m2m()
return instance
@admin.register(AiModel)
class AiModelAdmin(admin.ModelAdmin):
form = AiModelAdminForm
list_display = (
"name",
"model",
"api_type",
"capabilities_display",
"api_key_status",
"is_active",
"updated_at",
)
list_filter = ("api_type", "is_active")
search_fields = ("name", "model", "url")
readonly_fields = ("api_key_status", "created_at", "updated_at")
fieldsets = (
(None, {"fields": ("name", "url", "model", "api_type", "is_active")}),
(
"Credentials",
{
"fields": ("api_key", "api_key_status"),
"description": "The stored key is encrypted and never displayed.",
},
),
(
"Capabilities and request defaults",
{
"fields": (
"capabilities",
"timeout_seconds",
"connect_timeout_seconds",
"extra_body",
)
},
),
("Timestamps", {"fields": ("created_at", "updated_at")}),
)
@admin.display(description="capabilities")
def capabilities_display(self, obj):
return ", ".join(sorted(obj.capabilities_set()))
@admin.display(description="API key")
def api_key_status(self, obj):
return obj.api_key_masked or "not set"
@admin.register(ModelAlias)
class ModelAliasAdmin(admin.ModelAdmin):
list_display = (
"operation_type",
"alias",
"ai_model",
"is_default",
"is_active",
"updated_at",
)
list_filter = ("operation_type", "is_default", "is_active")
search_fields = ("alias", "ai_model__name", "ai_model__model")
autocomplete_fields = ("ai_model",)
readonly_fields = ("created_at", "updated_at")