Files
microdao-daarion/tests/test_stepan_doc_focus.py
Apple 129e4ea1fc 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
2026-03-03 07:14:14 -08:00

430 lines
18 KiB
Python
Raw Permalink 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.
"""
Tests for Doc Focus Gate (v3.5):
- PROMPT A: doc_focus fields in session, is_doc_focus_active, TTL
- PROMPT B: _is_doc_question, context_mode arbitration
- PROMPT C: auto-clear on vision/URL domain
- PROMPT D: /doc on|off|status operator commands
- PROMPT 1: domain override — explicit doc-token beats vision
- PROMPT 2: TTL auto-expire (simulated)
- PROMPT 3: context bleed guard
"""
import sys
import os
import time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "gateway-bot"))
# ── PROMPT A: session_context ────────────────────────────────────────────────
def test_default_session_has_doc_focus():
from crews.agromatrix_crew.session_context import _default_session
s = _default_session()
assert "doc_focus" in s
assert s["doc_focus"] is False
assert "doc_focus_ts" in s
assert s["doc_focus_ts"] == 0.0
def test_is_doc_focus_active_false_by_default():
from crews.agromatrix_crew.session_context import _default_session, is_doc_focus_active
s = _default_session()
assert is_doc_focus_active(s) is False
def test_is_doc_focus_active_true_when_set():
from crews.agromatrix_crew.session_context import is_doc_focus_active
now = time.time()
s = {"doc_focus": True, "doc_focus_ts": now}
assert is_doc_focus_active(s, now) is True
def test_is_doc_focus_active_expired():
from crews.agromatrix_crew.session_context import is_doc_focus_active, DOC_FOCUS_TTL
old_ts = time.time() - DOC_FOCUS_TTL - 1 # протух
s = {"doc_focus": True, "doc_focus_ts": old_ts}
assert is_doc_focus_active(s) is False
def test_is_doc_focus_active_just_within_ttl():
from crews.agromatrix_crew.session_context import is_doc_focus_active, DOC_FOCUS_TTL
now = time.time()
recent_ts = now - DOC_FOCUS_TTL + 5 # 5 секунд до завершення
s = {"doc_focus": True, "doc_focus_ts": recent_ts}
assert is_doc_focus_active(s, now) is True
def test_update_session_doc_focus():
from crews.agromatrix_crew.session_context import update_session, load_session, clear_session
chat_id = "test_doc_focus_chat"
clear_session(chat_id)
now = time.time()
update_session(chat_id, "test", depth="deep", doc_focus=True, doc_focus_ts=now)
s = load_session(chat_id)
assert s["doc_focus"] is True
assert abs(s["doc_focus_ts"] - now) < 1.0
clear_session(chat_id)
def test_update_session_doc_focus_off():
from crews.agromatrix_crew.session_context import update_session, load_session, clear_session
chat_id = "test_doc_focus_off_chat"
clear_session(chat_id)
now = time.time()
# Спочатку вмикаємо
update_session(chat_id, "test", depth="deep", doc_focus=True, doc_focus_ts=now)
# Потім вимикаємо
update_session(chat_id, "test2", depth="deep", doc_focus=False, doc_focus_ts=0.0)
s = load_session(chat_id)
assert s["doc_focus"] is False
clear_session(chat_id)
def test_is_doc_focus_active_fail_safe():
from crews.agromatrix_crew.session_context import is_doc_focus_active
# Broken session
assert is_doc_focus_active(None) is False
assert is_doc_focus_active({}) is False
assert is_doc_focus_active({"doc_focus": True}) is False # немає ts → вважається протухлим
# ── PROMPT B: _is_doc_question ───────────────────────────────────────────────
def test_is_doc_question_explicit():
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("що у звіті?") is True
assert _is_doc_question("прибуток у документі") is True
assert _is_doc_question("відкрий звіт") is True
assert _is_doc_question("в цьому документі є дані?") is True
def test_is_doc_question_financial_with_doc_anchor():
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("прибуток зі звіту") is True
assert _is_doc_question("витрати у файлі xlsx") is True
assert _is_doc_question("скільки грн/га в документі") is True
def test_is_doc_question_url_returns_false():
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("https://www.cropscience.bayer.ua/Products") is False
assert _is_doc_question("вивчи каталог https://bayer.com") is False
def test_is_doc_question_vision_returns_false():
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("що з листям?") is False
assert _is_doc_question("плями на рослині — що це?") is False
assert _is_doc_question("шкідник на фото") is False
def test_is_doc_question_web_intent_returns_false():
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("вивчи каталог засобів захисту") is False
assert _is_doc_question("переглянь сторінку сайту") is False
def test_is_doc_question_general_false():
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("привіт") is False
assert _is_doc_question("що ти вмієш?") is False
assert _is_doc_question("план на тиждень") is False
# ── PROMPT B: _detect_domain ─────────────────────────────────────────────────
def test_detect_domain_vision():
from crews.agromatrix_crew.doc_focus import _detect_domain
assert _detect_domain("що з листям?") == "vision"
assert _detect_domain("плями на рослині") == "vision"
assert _detect_domain("хвороба кукурудзи") == "vision"
def test_detect_domain_web():
from crews.agromatrix_crew.doc_focus import _detect_domain
assert _detect_domain("https://bayer.com") == "web"
assert _detect_domain("вивчи каталог сайту") == "web"
def test_detect_domain_doc():
from crews.agromatrix_crew.doc_focus import _detect_domain
assert _detect_domain("що у звіті?") == "doc"
assert _detect_domain("прибуток у документі") == "doc"
def test_detect_domain_general():
from crews.agromatrix_crew.doc_focus import _detect_domain
assert _detect_domain("привіт") == "general"
assert _detect_domain("як справи?") == "general"
# ── PROMPT C: auto-clear logic ────────────────────────────────────────────────
def test_doc_focus_clear_on_vision():
"""Симулюємо логіку auto-clear: domain=vision → doc_focus=False."""
from crews.agromatrix_crew.doc_focus import _detect_domain
from crews.agromatrix_crew.session_context import is_doc_focus_active
now = time.time()
session = {"doc_focus": True, "doc_focus_ts": now}
text = "що з листям на фото?"
domain = _detect_domain(text)
assert domain == "vision"
# Логіка з run.py:
if domain in ("vision", "web"):
session["doc_focus"] = False
session["doc_focus_ts"] = 0.0
assert is_doc_focus_active(session, now) is False
def test_doc_focus_clear_on_url():
"""URL message → doc_focus скидається."""
from crews.agromatrix_crew.doc_focus import _detect_domain
from crews.agromatrix_crew.session_context import is_doc_focus_active
now = time.time()
session = {"doc_focus": True, "doc_focus_ts": now}
text = "ще будь ласка вивчи каталог засобів захисту рослин фірми BAYER https://www.cropscience.bayer.ua"
domain = _detect_domain(text)
assert domain == "web"
if domain in ("vision", "web"):
session["doc_focus"] = False
session["doc_focus_ts"] = 0.0
assert is_doc_focus_active(session, now) is False
def test_doc_focus_preserved_on_doc_question():
"""Doc питання → doc_focus не скидається."""
from crews.agromatrix_crew.doc_focus import _detect_domain, _is_doc_question
from crews.agromatrix_crew.session_context import is_doc_focus_active
now = time.time()
session = {"doc_focus": True, "doc_focus_ts": now}
text = "який прибуток у звіті?"
domain = _detect_domain(text)
assert domain == "doc"
# Vision/web auto-clear не спрацьовує
assert domain not in ("vision", "web")
# doc_focus залишається
assert is_doc_focus_active(session, now) is True
# ── PROMPT D: /doc commands ───────────────────────────────────────────────────
def test_doc_command_status_no_session():
from crews.agromatrix_crew.session_context import clear_session
from crews.agromatrix_crew.doc_focus import handle_doc_focus
clear_session("doc_test_chat_99")
result = handle_doc_focus("status", chat_id="doc_test_chat_99")
assert result is not None
msg = result.get("message", "") or str(result)
assert "doc_focus=off" in msg or "off" in msg
def test_doc_command_on():
from crews.agromatrix_crew.session_context import clear_session, load_session
from crews.agromatrix_crew.doc_focus import handle_doc_focus
chat_id = "doc_on_test_chat"
clear_session(chat_id)
result = handle_doc_focus("on", chat_id=chat_id)
assert result is not None
msg = result.get("message", "") or str(result)
assert "on" in msg
# Сесія має відображати зміну
from crews.agromatrix_crew.session_context import _STORE
s = _STORE.get(chat_id) or {}
assert s.get("doc_focus") is True
def test_doc_command_off():
from crews.agromatrix_crew.session_context import _STORE, clear_session
from crews.agromatrix_crew.doc_focus import handle_doc_focus
chat_id = "doc_off_test_chat"
_STORE[chat_id] = {"doc_focus": True, "doc_focus_ts": time.time(), "updated_at": time.time()}
result = handle_doc_focus("off", chat_id=chat_id)
assert result is not None
msg = result.get("message", "") or str(result)
assert "off" in msg
s = _STORE.get(chat_id) or {}
assert s.get("doc_focus") is False
clear_session(chat_id)
def test_doc_command_no_chat_id():
from crews.agromatrix_crew.doc_focus import handle_doc_focus
result = handle_doc_focus("on", chat_id=None)
msg = result.get("message", "") or str(result)
assert "chat_id" in msg.lower() or "required" in msg.lower()
def test_doc_command_status_shows_ttl():
from crews.agromatrix_crew.session_context import _STORE, clear_session
from crews.agromatrix_crew.doc_focus import handle_doc_focus
chat_id = "doc_status_ttl_test"
now = time.time()
_STORE[chat_id] = {"doc_focus": True, "doc_focus_ts": now, "active_doc_id": "abc123",
"doc_facts": {"profit_uah": 5000000}, "updated_at": now}
result = handle_doc_focus("status", chat_id=chat_id)
msg = result.get("message", "") or str(result)
assert "on" in msg
assert "ttl_left" in msg
assert "abc123" in msg
assert "profit_uah" in msg
clear_session(chat_id)
# ── PROMPT 1: domain override ────────────────────────────────────────────────
def test_detect_domain_explicit_doc_beats_vision():
"""Explicit doc-токен ('по звіту', 'у файлі') перемагає vision навіть якщо є рослини/листя."""
from crews.agromatrix_crew.doc_focus import _detect_domain
# Скрін таблиці + питання по звіту
assert _detect_domain("по звіту: що з листям?") == "doc"
assert _detect_domain("у файлі є дані по хворобі рослин?") == "doc"
assert _detect_domain("в документі — плями чи хвороба?") == "doc"
def test_detect_domain_empty_text_is_vision():
"""Порожній текст (caption відсутній) → vision (фото без caption)."""
from crews.agromatrix_crew.doc_focus import _detect_domain
assert _detect_domain("") == "vision"
assert _detect_domain(" ") == "vision"
def test_detect_domain_url_beats_explicit_doc():
"""URL завжди виграє навіть якщо є explicit doc-токен."""
from crews.agromatrix_crew.doc_focus import _detect_domain
assert _detect_domain("по звіту https://bayer.com/products") == "web"
def test_is_doc_question_explicit_doc_token_beats_vision():
"""_is_doc_question повертає True якщо explicit doc-токен, навіть якщо є vision-слова."""
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("по звіту є дані про шкідника?") is True
assert _is_doc_question("у файлі листя згадується?") is True
assert _is_doc_question("в документі хвороба кукурудзи?") is True
def test_is_doc_question_vision_only_without_doc_token():
"""Vision без explicit doc-токена → False."""
from crews.agromatrix_crew.doc_focus import _is_doc_question
assert _is_doc_question("що з листям на фото?") is False
assert _is_doc_question("є хвороба чи шкідник?") is False
# ── PROMPT 2: TTL auto-expire ────────────────────────────────────────────────
def test_ttl_auto_expire_logic():
"""Симулюємо: doc_focus=True але TTL протух → expire."""
from crews.agromatrix_crew.session_context import is_doc_focus_active, DOC_FOCUS_TTL
now = time.time()
old_ts = now - DOC_FOCUS_TTL - 60 # на хвилину старіше TTL
session = {
"doc_focus": True,
"doc_focus_ts": old_ts,
"active_doc_id": "some_doc",
}
# is_doc_focus_active має повернути False
assert is_doc_focus_active(session, now) is False
# Логіка expire з run.py:
expired_age = round(now - (session.get("doc_focus_ts") or 0.0))
if session.get("doc_focus") and not is_doc_focus_active(session, now):
session["doc_focus"] = False
session["doc_focus_ts"] = 0.0
assert session["doc_focus"] is False
assert session["doc_focus_ts"] == 0.0
assert expired_age > DOC_FOCUS_TTL
def test_ttl_not_expired_within_window():
"""doc_focus не скидається якщо TTL ще не минув."""
from crews.agromatrix_crew.session_context import is_doc_focus_active, DOC_FOCUS_TTL
now = time.time()
recent_ts = now - (DOC_FOCUS_TTL / 2) # половина TTL
session = {"doc_focus": True, "doc_focus_ts": recent_ts}
assert is_doc_focus_active(session, now) is True
# Expire logic не спрацьовує
if session.get("doc_focus") and not is_doc_focus_active(session, now):
session["doc_focus"] = False
assert session["doc_focus"] is True
# ── PROMPT 3: context bleed guard ────────────────────────────────────────────
def _apply_bleed_guard(styled_response: str, context_mode: str) -> str:
"""Копія логіки context bleed guard з run.py для тестів."""
import re as _re
if context_mode == "general":
_BLEED_RE = _re.compile(
r"у\s+(?:цьому|наданому|даному)\s+документі"
r"\s+(?:цьому|наданому|даному)\s+документі"
r"|у\s+(?:цьому\s+)?звіті|в\s+(?:цьому\s+)?звіті",
_re.IGNORECASE | _re.UNICODE,
)
if _BLEED_RE.search(styled_response):
return "Щоб відповісти точно, уточни: це питання про звіт чи про інше?"
return styled_response
def test_bleed_guard_replaces_doc_phrase_in_general_mode():
"""В general-mode фраза 'у цьому документі' заміняється на нейтральне питання."""
response = "У цьому документі немає інформації про каталог BAYER."
result = _apply_bleed_guard(response, "general")
assert "цьому документі" not in result
assert "уточни" in result
def test_bleed_guard_replaces_zvit_phrase():
"""В general-mode фраза 'у звіті' також блокується."""
response = "У звіті немає даних про засоби захисту."
result = _apply_bleed_guard(response, "general")
assert "уточни" in result
def test_bleed_guard_no_replacement_in_doc_mode():
"""В doc-mode doc-фрази дозволені."""
response = "У цьому документі прибуток: 5 972 016 грн."
result = _apply_bleed_guard(response, "doc")
assert result == response
def test_bleed_guard_no_replacement_without_doc_phrase():
"""Відповідь без doc-фраз не змінюється в general-mode."""
response = "Це рослина кукурудзи, виглядає здоровою."
result = _apply_bleed_guard(response, "general")
assert result == response
def test_bleed_guard_case_insensitive():
"""Блокування не залежить від регістру."""
response = "В Цьому Документі відсутня ця інформація."
result = _apply_bleed_guard(response, "general")
assert "уточни" in result
def test_bleed_guard_variant_phrases():
"""Різні варіанти фрази блокуються."""
phrases = [
"у наданому документі немає",
"В даному документі відсутні",
"у цьому звіті є лише",
"в цьому звіті міститься",
]
for p in phrases:
result = _apply_bleed_guard(p, "general")
assert "уточни" in result, f"Not blocked: {p!r}"