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
181 lines
6.5 KiB
Python
181 lines
6.5 KiB
Python
# #region AssistantLlmPlannerIntent [C:5] [TYPE Module] [SEMANTICS assistant, llm, intent, planning, authorization]
|
|
# @defgroup AssistantApi Module group.
|
|
# @BRIEF LLM-based intent planning and authorization for the assistant API — separated from tool catalog.
|
|
# @LAYER API
|
|
# @RELATION DEPENDS_ON -> [AssistantLlmPlanner]
|
|
# @RELATION DEPENDS_ON -> [AssistantResolvers]
|
|
# @PRE Assistant routes initialized, user authenticated
|
|
# @POST Intent planning registered with confirmation gate
|
|
# @INVARIANT Production deployments always require confirmation.
|
|
# @SIDE_EFFECT Registers intent planning routes
|
|
# @DATA_CONTRACT UserIntent -> PlannedAction
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from src.core.config_manager import ConfigManager
|
|
from src.core.logger import logger
|
|
from src.plugins.llm_analysis.models import LLMProviderType
|
|
from src.plugins.llm_analysis.service import LLMClient
|
|
from src.schemas.auth import User
|
|
from src.services.llm_prompt_templates import (
|
|
normalize_llm_settings,
|
|
)
|
|
from src.services.llm_provider import LLMProviderService
|
|
|
|
from ._llm_planner import (
|
|
_check_any_permission,
|
|
_coerce_intent_entities,
|
|
)
|
|
from ._resolvers import (
|
|
_is_production_env,
|
|
_resolve_provider_id,
|
|
)
|
|
from ._tool_registry import get_permission_checks
|
|
|
|
|
|
# #region _plan_intent_with_llm [C:2] [TYPE Function]
|
|
# @BRIEF Use active LLM provider to select best tool/operation from dynamic catalog.
|
|
# @PRE tools list contains allowed operations for current user.
|
|
# @POST Returns normalized intent dict when planning succeeds; otherwise None.
|
|
async def _plan_intent_with_llm(
|
|
message: str,
|
|
tools: list[dict[str, Any]],
|
|
db: Session,
|
|
config_manager: ConfigManager,
|
|
) -> dict[str, Any] | None:
|
|
if not tools:
|
|
return None
|
|
|
|
llm_settings = normalize_llm_settings(config_manager.get_config().settings.llm)
|
|
planner_provider_token = llm_settings.get("assistant_planner_provider")
|
|
planner_model_override = llm_settings.get("assistant_planner_model")
|
|
llm_service = LLMProviderService(db)
|
|
providers = llm_service.get_all_providers()
|
|
provider_id = _resolve_provider_id(planner_provider_token, db)
|
|
provider = next((p for p in providers if p.id == provider_id), None)
|
|
if not provider:
|
|
return None
|
|
api_key = llm_service.get_decrypted_api_key(provider.id)
|
|
if not api_key:
|
|
return None
|
|
|
|
planner = LLMClient(
|
|
provider_type=LLMProviderType(provider.provider_type),
|
|
api_key=api_key,
|
|
base_url=provider.base_url,
|
|
default_model=planner_model_override or provider.default_model,
|
|
)
|
|
|
|
system_instruction = (
|
|
"You are a helpful assistant that understands backend tools.\n"
|
|
"Choose exactly one operation from available_tools or return clarify.\n"
|
|
"Output strict JSON object:\n"
|
|
"{"
|
|
'"domain": string, '
|
|
'"operation": string, '
|
|
'"entities": object, '
|
|
'"confidence": number, '
|
|
'"risk_level": "safe"|"guarded"|"dangerous", '
|
|
'"requires_confirmation": boolean'
|
|
"}\n"
|
|
"Rules:\n"
|
|
"- Use only operation names from available_tools.\n"
|
|
'- If input is ambiguous, operation must be "clarify" with low confidence.\n'
|
|
' Include "clarification" field with a specific question in Russian '
|
|
"asking what exactly is missing or unclear.\n"
|
|
"- If dashboard is provided as name/slug (e.g., COVID), put it into entities.dashboard_ref.\n"
|
|
"- Keep entities minimal and factual.\n"
|
|
)
|
|
payload = {
|
|
"available_tools": tools,
|
|
"user_message": message,
|
|
"known_environments": [
|
|
{"id": e.id, "name": e.name} for e in config_manager.get_environments()
|
|
],
|
|
}
|
|
try:
|
|
response = await planner.get_json_completion(
|
|
[
|
|
{"role": "system", "content": system_instruction},
|
|
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
|
]
|
|
)
|
|
except Exception as exc:
|
|
import traceback
|
|
|
|
logger.warning(
|
|
f"[assistant.planner][fallback] LLM planner unavailable: {exc}\n{traceback.format_exc()}"
|
|
)
|
|
return None
|
|
if not isinstance(response, dict):
|
|
return None
|
|
|
|
operation = response.get("operation")
|
|
valid_ops = {tool["operation"] for tool in tools}
|
|
if operation == "clarify":
|
|
clarification = response.get("clarification", "")
|
|
return {
|
|
"domain": "unknown",
|
|
"operation": "clarify",
|
|
"entities": {},
|
|
"confidence": float(response.get("confidence", 0.3)),
|
|
"risk_level": "safe",
|
|
"requires_confirmation": False,
|
|
"clarification": clarification,
|
|
}
|
|
if operation not in valid_ops:
|
|
return None
|
|
|
|
by_operation = {tool["operation"]: tool for tool in tools}
|
|
selected = by_operation[operation]
|
|
intent = {
|
|
"domain": response.get("domain") or selected["domain"],
|
|
"operation": operation,
|
|
"entities": response.get("entities", {}),
|
|
"confidence": float(response.get("confidence", 0.75)),
|
|
"risk_level": response.get("risk_level") or selected["risk_level"],
|
|
"requires_confirmation": bool(
|
|
response.get("requires_confirmation", selected["requires_confirmation"])
|
|
),
|
|
}
|
|
intent = _coerce_intent_entities(intent)
|
|
|
|
defaults = selected.get("defaults") or {}
|
|
for key, value in defaults.items():
|
|
if value and not intent["entities"].get(key):
|
|
intent["entities"][key] = value
|
|
|
|
if operation in {"deploy_dashboard", "execute_migration"}:
|
|
env_token = intent["entities"].get("environment") or intent["entities"].get(
|
|
"target_env"
|
|
)
|
|
if _is_production_env(env_token, config_manager):
|
|
intent["risk_level"] = "dangerous"
|
|
intent["requires_confirmation"] = True
|
|
return intent
|
|
|
|
|
|
# #endregion _plan_intent_with_llm
|
|
|
|
|
|
# #region _authorize_intent [C:2] [TYPE Function]
|
|
# @BRIEF Validate user permissions for parsed intent before confirmation/dispatch.
|
|
# @PRE intent.operation is present for known assistant command domains.
|
|
# @POST Returns if authorized; raises HTTPException(403) when denied.
|
|
def _authorize_intent(intent: dict[str, Any], current_user: User):
|
|
operation = intent.get("operation")
|
|
checks = get_permission_checks().get(operation)
|
|
if checks:
|
|
_check_any_permission(current_user, checks)
|
|
|
|
|
|
# #endregion _authorize_intent
|
|
|
|
|
|
# #endregion AssistantLlmPlannerIntent
|