Files
ss-tools/backend/src/api/routes/assistant/_dispatch.py
busya 8e8a3c3235 feat: attention-optimized semantic protocol v2.7
Core changes:
- Add @defgroup/@ingroup to 1791 C2+ contracts (555 files) for HCA 128× pre-training DSA grouping
- Add §0.1 Pre-Training Frequency matrix to semantics-core
- Add §VIII Attention Architecture rules (ATTN_1-4) with MLA/CSA/HCA/DSA mechanics
- Add @defgroup/@ingroup to canonical syntax (§II) and all contract examples

Agent prompts (5 files):
- Add ZERO-STATE RATIONALE with MLA/CSA/HCA/DSA compression mechanics
- Add pre-training note: @RATIONALE/@REJECTED are in-context learned tags
- svelte-coder: add missing #region contract, fix Svelte rule violations
- python-coder/fullstack-coder: honor function contracts from speckit plan
- qa-tester: add attention compliance audit (P3 ATTN_1-4 checks)

Skills (6 files):
- Translate all axiom_config descriptions to English
- Fix doc_dirs to index .opencode/ and .specify/
- Deduplicate 5× complexity_rules → single global_tags catalog
- Reduce semantics-svelte 591→485 lines (remove duplicate code blocks)
- Fix semantics-testing: 'Short IDs' → 'Short hierarchical IDs'
- Fix all examples: flat IDs → hierarchical Domain.Name format
- Fix Svelte examples: replace raw Tailwind + <button> with semantic tokens + /ui

Speckit workflow (commands + templates):
- speckit.plan: add Function-Level Contracts for C3+ with @PRE/@POST/@TEST_EDGE
- speckit.plan: add Attention Compliance Gate (ATTN_1-4 before contract generation)
- speckit.tasks: add function contract inlining format (constraints in task description)
- speckit.specify: load semantics-core for spec density rules
- spec-template: add #region contract, @SEMANTICS grouping, hierarchical IDs
- ux-reference-template: add #region wrapper
- plan-template: add attention gate, @defgroup/@ingroup guidance
- tasks-template: add attention audit + rebuild + orphan check tasks
- constitution.md: translate to English, add Principle VIII (attention-optimized contracts)

Reference modules rewritten (hierarchical IDs + full contracts):
- Auth.Jwt: 6 child contracts with @RATIONALE/@REJECTED/@TEST_EDGE
- Api.Auth: 5 endpoints with @TEST_EDGE + molecular CoT markers
- Migration.Model: @defgroup Migration with 18 @ACTION + 6 @INVARIANT

Scripts:
- add_defgroup_ingroup.py: zero-risk additive @ingroup migration (1791 insertions)
- migrate_hierarchical.py: flat→hierarchical ID dry-run analysis (792 contracts)
- merge_prompts.py: merge all prompts/skills/commands into one review file

Config:
- axiom_config.yaml: 749→395 lines (-47%), English, doc_dirs include prompts
- Fix test_datasets.py import collision (rename → test_datasets_routes.py)
- Fix test_preview.py: SupersetClient→get_superset_client, AsyncMock, logger f-string
2026-06-08 16:30:59 +03:00

217 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# #region AssistantDispatch [C:4] [TYPE Module] [SEMANTICS assistant, dispatch, confirm, execution, orchestration]
# @defgroup AssistantApi Module group.
# @BRIEF Intent dispatch engine and backward-compat wrapper around the central tool registry.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantToolRegistry]
# @RELATION DEPENDS_ON -> [AssistantSchemas]
# @INVARIANT Unsupported operations are rejected via HTTPException(400).
# @INVARIANT All tool handlers live in _tool_registry — _dispatch is a thin wrapper.
from __future__ import annotations
from typing import Any
from fastapi import HTTPException
from sqlalchemy.orm import Session
from src.core.config_manager import ConfigManager
from src.core.logger import belief_scope, logger
from src.core.task_manager import TaskManager
from src.schemas.auth import User
from src.services.git_service import GitService
from ._history import _coerce_query_bool
from ._resolvers import (
_get_environment_name_by_id,
_resolve_dashboard_id_entity,
_resolve_env_id,
)
from ._schemas import AssistantAction
from ._tool_registry import dispatch as _registry_dispatch
# #region _get_git_service [TYPE Function]
# @BRIEF Lazy-init GitService singleton to avoid crash at module import time when /app/storage/ is unavailable.
# @POST Returns GitService instance (created once, cached).
# @SIDE_EFFECT May attempt to create /app/storage/repositories on first call.
_git_service_instance: GitService | None = None
def _get_git_service() -> GitService:
global _git_service_instance
if _git_service_instance is None:
_git_service_instance = GitService()
return _git_service_instance
# #endregion _get_git_service
# #region _clarification_text_for_intent [C:2] [TYPE Function]
# @BRIEF Convert technical missing-parameter errors into user-facing clarification prompts.
# @PRE state was classified as needs_clarification for current intent/error combination.
# @POST Returned text is human-readable and actionable for target operation.
def _clarification_text_for_intent(
intent: dict[str, Any] | None, detail_text: str
) -> str:
operation = (intent or {}).get("operation")
guidance_by_operation: dict[str, str] = {
"run_llm_validation": (
"Нужно уточнение для запуска LLM-валидации: Укажите дашборд (id или slug), окружение и провайдер LLM."
),
"run_llm_documentation": (
"Нужно уточнение для генерации документации: Укажите dataset_id, окружение и провайдер LLM."
),
"create_branch": "Нужно уточнение: укажите дашборд (id/slug/title) и имя ветки.",
"commit_changes": "Нужно уточнение: укажите дашборд (id/slug/title) для коммита.",
"deploy_dashboard": "Нужно уточнение: укажите дашборд (id/slug/title) и целевое окружение.",
"execute_migration": "Нужно уточнение: укажите дашборд (id/slug/title), source_env и target_env.",
"run_backup": "Нужно уточнение: укажите окружение и при необходимости дашборд (id/slug/title).",
}
return guidance_by_operation.get(operation, detail_text)
# #endregion _clarification_text_for_intent
# #region _async_confirmation_summary [C:4] [TYPE Function]
# @BRIEF Build human-readable confirmation prompt for an intent before execution.
# @PRE actions is a non-empty list of planned review actions.
# @POST Returns a formatted summary string suitable for display to the user.
# @SIDE_EFFECT None - pure formatting function.
async def _async_confirmation_summary(intent: dict[str, Any], config_manager: ConfigManager, db: Session) -> str:
with belief_scope('_confirmation_summary'):
logger.reason('Belief protocol reasoning checkpoint for _confirmation_summary')
operation = intent.get('operation', '')
entities = intent.get('entities', {})
descriptions: dict[str, tuple[str, str]] = {
'create_branch': (
'📝 Создание ветки в Git',
'Будет создана ветка **{branch}** для дашборда **{dashboard}**. '
'Изменения можно будет коммитить в эту ветку.'
),
'commit_changes': (
'📝 Коммит изменений в Git',
'Изменения дашборда **{dashboard}** будут закоммичены в Git-репозиторий. '
'После коммита их можно будет развернуть на других окружениях.'
),
'deploy_dashboard': (
'🚀 Деплой дашборда',
'Дашборд **{dashboard}** будет развёрнут в окружение **{env}**. '
'Изменения станут доступны на целевом окружении.'
),
'execute_migration': (
'🔄 Миграция дашборда',
'Дашборд **{dashboard}** будет перенесён с **{src}** на **{tgt}**.'
),
'run_backup': (
'💾 Бэкап',
'Будет запущено резервное копирование окружения **{env}**{dashboard}.'
),
'run_llm_validation': (
'🤖 LLM-валидация дашборда',
'Будет запущена LLM-валидация дашборда **{dashboard}**{env}. '
'Это займёт некоторое время.'
),
'run_llm_documentation': (
'📄 Генерация документации',
'Будет сгенерирована документация для датасета **{dataset}**{env}.'
),
'start_maintenance': (
'🛠 Обслуживание',
'Будет запущено окно обслуживания для таблиц **{tables}**.'
),
'end_maintenance': (
'✅ Завершение обслуживания',
'Обслуживание будет завершено{event}.'
),
}
entry = descriptions.get(operation)
if not entry:
logger.reflect('Belief protocol postcondition checkpoint for _confirmation_summary')
return 'Подтвердите выполнение операции или отмените.'
title, desc = entry
def _label(value: Any, prefix: str=' ') -> str:
logger.reflect('Belief protocol postcondition checkpoint for _confirmation_summary')
return f'{prefix}{value}' if value else ''
dashboard = entities.get('dashboard_id') or entities.get('dashboard_ref')
env_val = entities.get('environment') or entities.get('target_env')
dataset_val = entities.get('dataset_id')
event_val = entities.get('event_id')
tables_val = entities.get('tables')
tables_str = ', '.join(tables_val[:5]) if isinstance(tables_val, list) else str(tables_val or '')
text = desc.format(
branch=_label(entities.get('branch_name')),
dashboard=_label(dashboard),
env=_label(env_val),
src=_label(entities.get('source_env')),
tgt=_label(entities.get('target_env')),
dataset=_label(dataset_val),
tables=tables_str if tables_str else '?',
event=_label(event_val),
)
text = f'**{title}**\n\n{text}'
if operation == 'execute_migration':
flags = []
flags.append('маппинг БД: ' + ('ВКЛ' if _coerce_query_bool(entities.get('replace_db_config', False)) else 'ВЫКЛ'))
flags.append('исправление кроссфильтров: ' + ('ВКЛ' if _coerce_query_bool(entities.get('fix_cross_filters', True)) else 'ВЫКЛ'))
dry_run_enabled = _coerce_query_bool(entities.get('dry_run', False))
flags.append('отчет dry-run: ' + ('ВКЛ' if dry_run_enabled else 'ВЫКЛ'))
text += f" ({', '.join(flags)})"
if dry_run_enabled:
try:
from src.core.migration.dry_run_orchestrator import MigrationDryRunService
from src.core.async_superset_client import AsyncSupersetClient
from src.models.dashboard import DashboardSelection
src_token = entities.get('source_env')
tgt_token = entities.get('target_env')
dashboard_id = await _resolve_dashboard_id_entity(entities, config_manager, env_hint=src_token)
if dashboard_id and src_token and tgt_token:
src_env_id = _resolve_env_id(src_token, config_manager)
tgt_env_id = _resolve_env_id(tgt_token, config_manager)
if src_env_id and tgt_env_id:
env_map = {env.id: env for env in config_manager.get_environments()}
source_env = env_map.get(src_env_id)
target_env = env_map.get(tgt_env_id)
if source_env and target_env and (source_env.id != target_env.id):
selection = DashboardSelection(source_env_id=source_env.id, target_env_id=target_env.id, selected_ids=[dashboard_id], replace_db_config=_coerce_query_bool(entities.get('replace_db_config', False)), fix_cross_filters=_coerce_query_bool(entities.get('fix_cross_filters', True)))
service = MigrationDryRunService()
source_client = AsyncSupersetClient(source_env)
target_client = AsyncSupersetClient(target_env)
report = service.run(selection, source_client, target_client, db)
s = report.get('summary', {})
dash_s = s.get('dashboards', {})
charts_s = s.get('charts', {})
ds_s = s.get('datasets', {})
creates = dash_s.get('create', 0) + charts_s.get('create', 0) + ds_s.get('create', 0)
updates = dash_s.get('update', 0) + charts_s.get('update', 0) + ds_s.get('update', 0)
deletes = dash_s.get('delete', 0) + charts_s.get('delete', 0) + ds_s.get('delete', 0)
text += f'\n\nОтчет dry-run:\n- Будет создано новых объектов: {creates}\n- Будет обновлено: {updates}\n- Будет удалено: {deletes}'
else:
text += '\n\n(Не удалось загрузить отчет dry-run: неверные окружения).'
except Exception as e:
import traceback
logger.warning('[assistant.dry_run_summary][failed] Exception: %s\n%s', e, traceback.format_exc())
text += f'\n\n(Не удалось загрузить отчет dry-run: {e}).'
logger.reflect('Belief protocol postcondition checkpoint for _confirmation_summary')
return f'Выполнить: {text}. Подтвердите или отмените.'
# #endregion _async_confirmation_summary
# #region _dispatch_intent [C:1] [TYPE Function]
# @BRIEF Backward-compat wrapper around the central tool registry dispatch.
# @DEPRECATED Use `dispatch()` from `_tool_registry` directly.
async def _dispatch_intent(intent: dict[str, Any], current_user: User, task_manager: TaskManager, config_manager: ConfigManager, db: Session) -> tuple[str, str | None, list[AssistantAction]]:
"""Backward-compat wrapper around the central tool registry dispatch."""
operation = intent.get('operation')
return await _registry_dispatch(operation, intent, current_user, task_manager, config_manager, db)
# #endregion _dispatch_intent
# #endregion AssistantDispatch