Initial commit: VoIdeaAI - voice-first AI idea assistant

This commit is contained in:
2026-05-13 12:51:42 +03:00
commit 688d043dad
421 changed files with 47915 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
"""Seed data for VoIdeaAI.
Called from app lifespan when DB tables are empty.
Creates default tariff plans and sets system owner by email.
"""
import json
from pathlib import Path
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import get_settings
from app.models.agent import AgentConfig
from app.models.tariff import TariffPlan
from app.services.user_service import UserService
settings = get_settings()
_AGENT_VERSIONS_FILE = Path(__file__).resolve().parent.parent.parent / "AGENT_VERSIONS.json"
def _load_agent_versions() -> dict[str, str]:
try:
return json.loads(_AGENT_VERSIONS_FILE.read_text(encoding="utf-8"))
except (FileNotFoundError, json.JSONDecodeError, OSError):
return {}
AGENT_DESCRIPTIONS: dict[str, str] = {
"conductor": "Дирижёр — оркестратор, единственная точка входа. Маршрутизирует запросы к ролевым агентам, верифицирует ответы.",
"business_analyst": "Бизнес-аналитик — оценивает бизнес-метрики, ROI, конкурентную среду, окупаемость идеи.",
"task_organizer": "Организатор задач — разбивает идею на конкретные шаги реализации с оценкой сроков.",
"lawyer": "Юрист — проверяет идею на соответствие законодательству РФ (44-ФЗ, 152-ФЗ, 223-ФЗ).",
"financial_consultant": "Финансовый консультант — бюджет, прогноз выручки, точка безубыточности.",
"solution_architect": "Архитектор решений — предлагает 2 варианта архитектуры: монолит и микросервисы.",
"tester": "Тестировщик — описывает сценарии тестирования (позитивные и негативные кейсы).",
"ui_designer": "UI-дизайнер — предлагает 2 варианта визуального дизайна интерфейса.",
"smm_specialist": "SMM-специалист — разрабатывает контент-план для соцсетей.",
"life_coach": "Лайф-коуч — ставит SMART-цели, поквартальные вехи развития.",
"accessibility_expert": "Эксперт по доступности — проверяет на WCAG 2.1 AA соответствие.",
"critic": "Критик — конструктивная критика, поиск «слепых зон» идеи.",
"copywriter": "Копирайтер — упаковывает идею в продающий текст для разных аудиторий.",
"keeper": "Хранитель — сохраняет проработанную идею в базу данных.",
"doc_agent": "DocAgent — автоматическая генерация и обновление документации.",
"backlog_agent": "BacklogAgent — управление бэклогом, приоритизация задач.",
"spec_agent": "SpecAgent — написание формальных спецификаций и требований.",
"audit_agent": "AuditAgent — аудит кода, архитектуры, безопасности.",
"observer_agent": "ObserverAgent — мониторинг системы, сбор метрик, алерты.",
"evolution_agent": "EvolutionAgent — автоматическое улучшение кода, рефакторинг.",
"security_agent": "SecurityAgent — проверка безопасности, уязвимости, OWASP.",
"qa_tester_agent": "QATesterAgent — автоматизированное тестирование, регресс.",
"fix_agent": "FixAgent — автоматическое исправление ошибок на основе логов.",
"ui_test_agent": "UITestAgent — тестирование UI/UX, скриншотные тесты.",
"rollout_agent": "RolloutAgent — управление релизами, миграции, откаты.",
}
async def seed_database(db: AsyncSession) -> None:
"""Seed initial data if tables are empty."""
await _seed_tariff_plans(db)
await _seed_agent_descriptions(db)
await _seed_owner(db)
async def _seed_tariff_plans(db: AsyncSession) -> None:
"""Create default Free tariff plan if none exist."""
result = await db.execute(select(TariffPlan).limit(1))
if result.scalar_one_or_none():
return
free_plan = TariffPlan(
name="Бесплатно",
code="free",
description="Базовый доступ ко всем функциям. Сейчас всё бесплатно 🎉",
price_monthly=0,
features={
"limit_ideas": None,
"limit_sessions_per_day": None,
"limit_agents": None,
"limit_team_members": None,
"limit_storage_mb": 100,
"has_voice": True,
"has_voice_commands": True,
"has_drive": True,
"has_advanced_analytics": True,
"has_api_access": True,
"has_priority_support": True,
"has_custom_branding": True,
"_promo": True,
},
is_active=True,
sort_order=0,
)
db.add(free_plan)
await db.commit()
async def _seed_agent_descriptions(db: AsyncSession) -> None:
"""Fill empty agent descriptions and update versions from AGENT_VERSIONS.json."""
agent_versions = _load_agent_versions()
result = await db.execute(select(AgentConfig))
agents = result.scalars().all()
updated = False
for agent in agents:
name = agent.agent_name.lower()
if name in AGENT_DESCRIPTIONS and (not agent.description or agent.description == ""):
agent.description = AGENT_DESCRIPTIONS[name]
updated = True
name_lower = agent.agent_name.lower()
expected_version = agent_versions.get(name_lower) or agent_versions.get(agent.agent_name)
if expected_version and agent.version != expected_version:
agent.version = expected_version
updated = True
if updated:
await db.commit()
async def _seed_owner(db: AsyncSession) -> None:
"""Set system owner by SYSTEM_OWNER_EMAIL if configured."""
if not settings.system_owner_email:
return
svc = UserService(db)
await svc.set_owner_by_email(settings.system_owner_email)