← Admin Panel

Модель данных

admin panel · workzone

Admin Panel держит глобальную конфигурацию платформы — singleton с общими настройками и бюджетами AI. Реестр AI-ресурсов (провайдеры, модели, их назначение по функциям, расходы, инструменты, системный prompt) вынесен в сквозной модуль AI Foundation, а уведомления (каналы, маршруты, личные настройки, лента доставки) — в Notifications. ORM-модель singleton'а живёт в Knowledge Store (knowledge_store/models.py); Admin — тонкий пакет-фасад (schemas + service + routes) поверх неё. Потребители — Harvester, Query Engine, Agent Engine, Auth — читают модель напрямую; никто не импортирует admin, циклов между модулями нет.

1 Конфигурация платформы Singleton. Одна строка на инстанс.
platform_settings глобальные настройки
singleton · CHECK (id = 1)
id BigInteger PKCHECK всегда 1 · → Singleton
org_name Text NOT NULLDEFAULT DEFAULT 'Achilles'
org_logo_url Text NULL
org_description Text NULL
accent_color Text NOT NULLDEFAULT hex · DEFAULT '#6366f1' · → Branding
timezone Text NOT NULLDEFAULT IANA tz · DEFAULT 'UTC'
locale Text NOT NULLDEFAULTCHECK DEFAULT 'ru' · ru · en
date_format Text NOT NULLDEFAULTCHECK DEFAULT 'DD.MM.YYYY'
access_token_ttl Integer NOT NULLDEFAULTCHECK сек · DEFAULT 900 (15 мин) · → Session TTLs
refresh_token_ttl Integer NOT NULLDEFAULTCHECK сек · DEFAULT 2592000 (30 дней)
session_absolute_ttl Integer NOT NULLDEFAULTCHECK сек · DEFAULT 7776000 (90 дней)
maintenance_mode Boolean NOT NULLDEFAULT DEFAULT false · → Режим обслуживания
mcp_enabled Boolean NOT NULLDEFAULT DEFAULT true · рубильник MCP-двери · закрыт ⇒ запрос к /mcp отклонён до проверки ключа · → MCP
ai_monthly_budget Numeric NULL месячный порог расходов · NULL = не задан · → Бюджет AI
ai_budget_alert_enabled Boolean NOT NULLDEFAULTCHECK оповещение · DEFAULT false · CHECK: вкл ⇒ сумма задана
curation_frequency Text NOT NULLDEFAULTCHECK каденс платформенного прогона доводки · DEFAULT 'daily' · daily · weekly · → Curation Pass
curation_weekday Integer NULLCHECK 0–6 (пн–вс) · только для weekly · NULL для daily
curation_time Text NOT NULLDEFAULT 'HH:MM' локального времени организации · DEFAULT '04:00' · тяжёлый прогон в тихие часы
agent_weekly_token_budget BigInteger NULLCHECK недельный токен-потолок агентов на пользователя · блокирующий · NULL = без лимита · расход — SUM(tokens_used) по прогонам · → Расходы AI · → Agent Engine
chat_weekly_token_budget BigInteger NULLCHECK недельный токен-потолок чата на пользователя · справочный в v1 (учёт без блокировки) · NULL = без лимита · расход — SUM(tokens_used) по сообщениям · → Расходы AI
agent_iteration_cap Integer NOT NULLDEFAULTCHECK потолок шагов одного прогона · DEFAULT 15 · предохранитель от зацикливания, не бюджет · → Agent Engine
agent_max_concurrency Integer NOT NULLDEFAULTCHECK потолок одновременных LLM-вызовов фоновой полосы агентов · DEFAULT 4 · отделён от живого чата · → Agent Engine
created_at DateTime(tz) DEFAULT server_default=now()
updated_at DateTime(tz) DEFAULT now() + trigger
Singleton — одна строка на инстанс
CHECK (id = 1) гарантирует единственность. Миграция сразу вставляет строку с defaults — приложение всегда читает / обновляет, никогда не создаёт.
Session TTLs → Auth & Security
Переопределяют defaults auth. Значения в секундах — единый формат с JWT-библиотекой. session_absolute_ttl — предельный срок жизни token family (= absolute_expires_at в auth-модуле). Auth импортирует модель из core и читает TTL при выдаче токенов.
Branding
accent_color — hex-цвет, применяется к login-экрану и шапке через CSS custom property. Тема (dark / light) задаётся на стороне клиента: первый визит следует системной (prefers-color-scheme), выбор хранится в localStorage.
Global config — модель вне admin → Auth & Security
ORM-модель PlatformSettings размещена в knowledge_store/models.py, не в admin. Admin предоставляет CRUD API (settings service / routes); auth и другие модули импортируют модель для чтения. Зависимости идут admin → KS/auth — без циклов.
CHECK vs ENUM
Поля с фиксированным набором (locale, date_format) — Text + CHECK, а не native ENUM PostgreSQL.
почему так
ALTER TYPE ADD VALUE не откатывается в транзакции — Alembic-миграции проще.
SMTP-настройки — в модуле Email → Email
Модель smtp_settings (поля, шифрование пароля, миграция) живёт у транспорта Email. Здесь — только экран редактирования (→ Экран SMTP), пишущий настройки через API.
Бюджет AI — настройка инстанса; расходы — в реестре → Расходы AI → model_usage → Уведомления
Порог месячных расходов на AI админ вводит руками и включает тумблером — не hardcode: сумма и флаг живут здесь, в singleton (ai_monthly_budget + ai_budget_alert_enabled), рядом с maintenance_mode. CHECK не даёт включить оповещение без заданной суммы. Регулятор стоит на экране Расходы AI — общий дом лимитов рядом с недельными токен-потолками; сам месячный счёт по моделям копит реестр AI-ресурсов (model_usage, агрегат по модели · функции · дню) и показывает разрез по моделям там же. При достижении порога поднимается уведомление типа Budget — канал держит модуль уведомлений. Сами модели и их назначения — тоже в AI Foundation.
Расход по людям — производный из журналов, без счётчика → Расходы AI
Экран Расходы AI считает суммы по пользователю на лету из журналов: агентский расход — из agent_runs.tokens_used (через agents.user_id), чат — из messages.tokens_used (через conversations.user_id). Отдельной таблицы-счётчика по пользователю нет — потолок производный, как у агентского бюджета. Оба недельных потолка (agent_weekly_token_budget · chat_weekly_token_budget) живут здесь, в singleton; агентский блокирует прогоны, чат в v1 справочный. Индексация и RAG-поиск к человеку не привязываются — их агрегат держит model_usage (по модели · функции · дню); тот же экран показывает их разрезом по моделям, не по людям. Денежная стоимость по людям, суточный агрегат user_usage при росте нагрузки и блокировка чата — v2.
2 Миграция Создаёт таблицы и заселяет singleton.
alembic/versions/NNN_core_platform_settings.py
Создаёт таблицу platform_settings и тем же шагом выполняет INSERT начальной строки с defaults — singleton существует с первого запуска, приложение его никогда не создаёт. downgrade()DROP TABLE.