feat(platform): add new services, tools, tests and crews modules

New router intelligence modules (26 files): alert_ingest/store, audit_store,
architecture_pressure, backlog_generator/store, cost_analyzer, data_governance,
dependency_scanner, drift_analyzer, incident_* (5 files), llm_enrichment,
platform_priority_digest, provider_budget, release_check_runner, risk_* (6 files),
signature_state_store, sofiia_auto_router, tool_governance

New services:
- sofiia-console: Dockerfile, adapters/, monitor/nodes/ops/voice modules, launchd, react static
- memory-service: integration_endpoints, integrations, voice_endpoints, static UI
- aurora-service: full app suite (analysis, job_store, orchestrator, reporting, schemas, subagents)
- sofiia-supervisor: new supervisor service
- aistalk-bridge-lite: Telegram bridge lite
- calendar-service: CalDAV calendar service with reminders
- mlx-stt-service / mlx-tts-service: Apple Silicon speech services
- binance-bot-monitor: market monitor service
- node-worker: STT/TTS memory providers

New tools (9): agent_email, browser_tool, contract_tool, observability_tool,
oncall_tool, pr_reviewer_tool, repo_tool, safe_code_executor, secure_vault

New crews: agromatrix_crew (10 modules: depth_classifier, doc_facts, doc_focus,
farm_state, light_reply, llm_factory, memory_manager, proactivity, reflection_engine,
session_context, style_adapter, telemetry)

Tests: 85+ test files for all new modules
Made-with: Cursor
This commit is contained in:
Apple
2026-03-03 07:14:14 -08:00
parent e9dedffa48
commit 129e4ea1fc
241 changed files with 69349 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
"""
Depth Classifier для Степана.
classify_depth(text, has_doc_context, last_topic, user_profile) → "light" | "deep"
Без залежності від crewai — чистий Python.
Fail-closed: помилка → "deep".
"""
from __future__ import annotations
import logging
import re
from typing import Literal
from crews.agromatrix_crew.telemetry import tlog
logger = logging.getLogger(__name__)
# ─── Patterns ────────────────────────────────────────────────────────────────
_DEEP_ACTION_RE = re.compile(
r'\b(зроби|зробити|перевір|перевірити|порахуй|порахувати|підготуй|підготувати'
r'|онови|оновити|створи|створити|запиши|записати|зафіксуй|зафіксувати'
r'|внеси|внести|проаналізуй|проаналізувати|порівняй|порівняти'
r'|розрахуй|розрахувати|сплануй|спланувати|покажи|показати'
r'|заплануй|запланувати|закрий|закрити|відкрий|відкрити)\b',
re.IGNORECASE | re.UNICODE,
)
_DEEP_URGENT_RE = re.compile(
r'\b(аварія|терміново|критично|тривога|невідкладно|alert|alarm|critical)\b',
re.IGNORECASE | re.UNICODE,
)
_DEEP_DATA_RE = re.compile(
r'\b(\d[\d.,]*)\s*(га|кг|л|т|мм|°c|°f|%|гектар|літр|тонн)',
re.IGNORECASE | re.UNICODE,
)
_LIGHT_GREET_RE = re.compile(
r'^(привіт|добрий\s+\w+|доброго\s+\w+|hello|hi|hey|ок|окей|добре|зрозумів|зрозуміла'
r'|дякую|дякуй|спасибі|чудово|супер|ясно|зрозуміло|вітаю|вітання)[\W]*$',
re.IGNORECASE | re.UNICODE,
)
_DEEP_INTENTS = frozenset({
'plan_week', 'plan_day', 'plan_vs_fact', 'show_critical_tomorrow', 'close_plan'
})
# ─── Intent detection (inline, no crewai dependency) ─────────────────────────
def _detect_intent(text: str) -> str:
t = text.lower()
if 'сплануй' in t and 'тиж' in t:
return 'plan_week'
if 'сплануй' in t:
return 'plan_day'
if 'критично' in t or 'на завтра' in t:
return 'show_critical_tomorrow'
if 'план/факт' in t or 'план факт' in t:
return 'plan_vs_fact'
if 'закрий план' in t:
return 'close_plan'
return 'general'
# ─── Public API ───────────────────────────────────────────────────────────────
def classify_depth(
text: str,
has_doc_context: bool = False,
last_topic: str | None = None,
user_profile: dict | None = None,
session: dict | None = None,
) -> Literal["light", "deep"]:
"""
Визначає глибину обробки запиту.
light — Степан відповідає сам, без запуску під-агентів
deep — повний orchestration flow з делегуванням
v3: session — SessionContext; якщо last_depth=="light" і короткий follow-up
без action verbs → stability_guard повертає "light" без подальших перевірок.
Правило fail-closed: при будь-якій помилці повертає "deep".
"""
try:
t = text.strip()
# ── Intent Stability Guard (v3) ────────────────────────────────────────
# Якщо попередня взаємодія була light і поточне повідомлення ≤6 слів
# без action verbs / urgent → утримуємо в light без зайвих перевірок.
if (
session
and session.get("last_depth") == "light"
and not _DEEP_ACTION_RE.search(t)
and not _DEEP_URGENT_RE.search(t)
):
word_count_guard = len(t.split())
if word_count_guard <= 6:
tlog(logger, "stability_guard_triggered", chat_id="n/a",
words=word_count_guard, last_depth="light")
return "light"
# Explicit greetings / social acks → always light
if _LIGHT_GREET_RE.match(t):
tlog(logger, "depth", depth="light", reason="greeting")
return "light"
word_count = len(t.split())
# Follow-up heuristic: ≤6 words + last_topic + no action verbs + no urgent → light
# Handles: "а на завтра?", "а по полю 12?", "а якщо дощ?" etc.
if (
word_count <= 6
and last_topic is not None
and not _DEEP_ACTION_RE.search(t)
and not _DEEP_URGENT_RE.search(t)
):
tlog(logger, "depth", depth="light", reason="short_followup_last_topic",
words=word_count, last_topic=last_topic)
return "light"
# Very short follow-ups without last_topic → light (≤4 words, no verbs)
if word_count <= 4 and not _DEEP_ACTION_RE.search(t) and not _DEEP_URGENT_RE.search(t):
tlog(logger, "depth", depth="light", reason="short_followup", words=word_count)
return "light"
# Active doc context → deep
if has_doc_context:
tlog(logger, "depth", depth="deep", reason="has_doc_context")
return "deep"
# Urgency keywords → always deep
if _DEEP_URGENT_RE.search(t):
tlog(logger, "depth", depth="deep", reason="urgent_keyword")
return "deep"
# Explicit action verbs → deep
if _DEEP_ACTION_RE.search(t):
tlog(logger, "depth", depth="deep", reason="action_verb")
return "deep"
# Numeric measurements → deep
if _DEEP_DATA_RE.search(t):
tlog(logger, "depth", depth="deep", reason="numeric_data")
return "deep"
# Intent-based deep trigger
detected = _detect_intent(t)
if detected in _DEEP_INTENTS:
tlog(logger, "depth", depth="deep", reason="intent", intent=detected)
return "deep"
tlog(logger, "depth", depth="light", reason="no_deep_signal")
return "light"
except Exception as exc:
logger.warning("classify_depth error, defaulting to deep: %s", exc)
return "deep"