diff --git a/apps/agent/admin.py b/apps/agent/admin.py index b2bfe65..6470566 100644 --- a/apps/agent/admin.py +++ b/apps/agent/admin.py @@ -1,61 +1,57 @@ +from django.contrib import admin +from django.db import models from django.shortcuts import redirect from django.urls import reverse -from unfold.admin import ModelAdmin - +from unfold.admin import ModelAdmin, TabularInline from utils.admin import dovoodi_admin_site, project_admin_site -from apps.agent.models import AgentSettings - +from .models import AgentSettings, AgentPrompt +from unfold.contrib.forms.widgets import WysiwygWidget + + +class AgentPromptInline(TabularInline): + model = AgentPrompt + extra = 0 + fields = ('is_active', 'content') + + formfield_overrides = { + models.TextField: { + 'widget': admin.widgets.AdminTextareaWidget(attrs={ + # 1. REDUCE HEIGHT: Set rows to 1 or 2 + 'rows': 2, + + # 🎨 STYLING + # w-full: Fills the available space + # bg-black: Black background + # text-white: White text (Fixed typo from 'text-blacka') + # border-gray-600: Border color + # leading-normal: Adjusts line height for better vertical centering + 'class': 'w-full p-2 border rounded-md bg-black text-white border-gray-600 focus:ring-primary-500 focus:border-primary-500 leading-normal', + + 'placeholder': 'Enter instruction prompt here...', + + # 2. INCREASE WIDTH: 'min-width' forces the table cell to expand + 'style': 'width: 100%; min-width: 600px; resize: vertical;' + }) + }, + } class AgentSettingsAdmin(ModelAdmin): - """ - Singleton Admin for Agent Configuration. - Acts as a 'Settings Page' by redirecting list view to the edit page of ID=1. - """ - def has_add_permission(self, request): - # Disable 'Add' button to prevent creating multiple configs return False def has_delete_permission(self, request, obj=None): - # Disable 'Delete' button to ensure settings always exist return False def changelist_view(self, request, extra_context=None): - """ - Redirect the 'List View' directly to the 'Edit View' of ID=1. - Auto-creates the default config if it doesn't exist. - """ - # Ensure ID=1 exists - obj, created = self.model.objects.get_or_create(pk=1, defaults={ - "system_prompt": "You are a helpful assistant.", - }) - - # Build the URL dynamically based on the registered admin site + obj, created = self.model.objects.get_or_create(pk=1) url = reverse( f"{self.admin_site.name}:{self.model._meta.app_label}_{self.model._meta.model_name}_change", args=[obj.pk] ) - return redirect(url) - fieldsets = ( - ("Status", { - "fields": ("is_maintenance_mode",), - "classes": ("tab",), - }), - ("Brain (System Instructions)", { - "fields": ("system_prompt",), - "classes": ("tab",), - "description": "Define the core personality and rules for the AI Agent." - }), - ("Model Parameters", { - "fields": ("model_id", "temperature"), - "classes": ("tab",), - "description": "Technical settings for the inference engine." - }), - ) - + inlines = [AgentPromptInline] -# Register with your custom admin site +# Register dovoodi_admin_site.register(AgentSettings, AgentSettingsAdmin) project_admin_site.register(AgentSettings, AgentSettingsAdmin) \ No newline at end of file diff --git a/apps/agent/migrations/0002_alter_agentsettings_options_and_more.py b/apps/agent/migrations/0002_alter_agentsettings_options_and_more.py new file mode 100644 index 0000000..5501614 --- /dev/null +++ b/apps/agent/migrations/0002_alter_agentsettings_options_and_more.py @@ -0,0 +1,56 @@ +# Generated by Django 4.2.27 on 2026-02-15 13:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('agent', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='agentsettings', + options={'verbose_name': 'Agent Control Panel', 'verbose_name_plural': 'Agent Control Panel'}, + ), + migrations.RemoveField( + model_name='agentsettings', + name='model_id', + ), + migrations.RemoveField( + model_name='agentsettings', + name='system_prompt', + ), + migrations.RemoveField( + model_name='agentsettings', + name='temperature', + ), + migrations.AddField( + model_name='agentsettings', + name='updated_at', + field=models.DateTimeField(auto_now=True), + ), + migrations.AlterField( + model_name='agentsettings', + name='is_maintenance_mode', + field=models.BooleanField(default=False, help_text='If checked, the agent will reply with a maintenance message.'), + ), + migrations.CreateModel( + name='AgentPrompt', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text="Internal Label (e.g., 'Core Identity')", max_length=100)), + ('content', models.TextField(help_text='The actual instruction text.')), + ('is_active', models.BooleanField(default=True)), + ('order', models.PositiveIntegerField(default=0, help_text='Order of injection (1, 2, 3...)')), + ('settings', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prompts', to='agent.agentsettings')), + ], + options={ + 'verbose_name': 'System Instruction', + 'verbose_name_plural': 'System Instructions', + 'ordering': ['order'], + }, + ), + ] diff --git a/apps/agent/migrations/0003_alter_agentprompt_options_and_more.py b/apps/agent/migrations/0003_alter_agentprompt_options_and_more.py new file mode 100644 index 0000000..57661fb --- /dev/null +++ b/apps/agent/migrations/0003_alter_agentprompt_options_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 4.2.27 on 2026-02-15 14:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agent', '0002_alter_agentsettings_options_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='agentprompt', + options={}, + ), + migrations.AlterModelOptions( + name='agentsettings', + options={'verbose_name': 'Agent Configuration', 'verbose_name_plural': 'Agent Configuration'}, + ), + migrations.RemoveField( + model_name='agentprompt', + name='order', + ), + migrations.RemoveField( + model_name='agentprompt', + name='title', + ), + migrations.RemoveField( + model_name='agentsettings', + name='is_maintenance_mode', + ), + migrations.AlterField( + model_name='agentprompt', + name='content', + field=models.TextField(help_text='The instruction text.'), + ), + ] diff --git a/apps/agent/models.py b/apps/agent/models.py index c70e22a..dc5c825 100644 --- a/apps/agent/models.py +++ b/apps/agent/models.py @@ -1,25 +1,20 @@ from django.db import models from django.core.cache import cache - class AgentSettings(models.Model): - # Fixed Settings - system_prompt = models.TextField(default="You are a helpful assistant.") - model_id = models.CharField(max_length=50, default="deepseek/deepseek-r1") - temperature = models.FloatField(default=0.3) - - # Switches - is_maintenance_mode = models.BooleanField(default=False) + """ + Singleton Container. + Now just a wrapper to hold the list of prompts. + """ + updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = "Agent Configuration" verbose_name_plural = "Agent Configuration" def save(self, *args, **kwargs): - self.pk = 1 + self.pk = 1 # Force Singleton super().save(*args, **kwargs) - - # Clear cache whenever you save cache.delete("agent_config_1") def __str__(self): @@ -27,6 +22,24 @@ class AgentSettings(models.Model): @classmethod def load(cls): - """Helper to get the singleton instance, creating it if missing.""" obj, created = cls.objects.get_or_create(pk=1) - return obj \ No newline at end of file + return obj + + +class AgentPrompt(models.Model): + """ + Simple Prompt Block. + Just text and a switch. + """ + settings = models.ForeignKey(AgentSettings, on_delete=models.CASCADE, related_name="prompts") + + content = models.TextField(help_text="The instruction text.") + is_active = models.BooleanField(default=True) + + def __str__(self): + # Display the first 50 chars as the name in admin + # if self.is_active: + # return f"Active Prompt" if self.content else "Empty Prompt" + # else: + # return f"Inactive Prompt: {self.content[:50]}..." if self.content else "Empty Prompt" + return "" \ No newline at end of file