== User stories == US1: Контекст с дашборда/датасета → /agent с URL params US2: Guardrails card — env badge, 7 risk tones, countdown, permission_denied US3: Tools optimization — retry, timeout, summarise, RBAC + context affinity == Backend == - _context.py (NEW): UIContext validation (7 checks) - _tool_filter.py (NEW): RBAC + context affinity pipeline - _confirmation.py: build_confirmation_contract_v2, permission_denied_payload - tools.py: superset_list_databases, retry/summarise/timeout wrappers - app.py: _inject_uicontext, _inject_env_id_into_tools, database prefetch в runtime context - _persistence.py: prefetch_databases() - agent_superset_explore.py: GET /databases endpoint - _llm_async_http.py, _persistence.py: fix double /v1 в LLM URL (LM Studio Unexpected endpoint) == Frontend == - AgentChatModel.svelte.ts: 5 atoms, 3 actions, countdown, context - AgentChat.svelte: production banner, process steps, debug panel - ConfirmationCard.svelte: 7 risk tones, permission_denied, countdown - ToolCallCard.svelte: retrying/timeout/cancelled states - StreamProcessor.svelte.ts: tool_retry, timeout, permission_denied - TopNavbar: sparkles icon + Ассистент - sidebarNavigation: AI section - DashboardHeader, datasets/+page: contextual AI buttons - Icon: sparkles, brain, cpu icons - tailwind: assistant category colors - i18n: en/ru nav keys == Tests == - 159 backend agent tests (+16 US3: retry, timeout, summarise, contracts) - 2544 frontend tests (+11 model + component tests) - 15 JSON fixtures (10 API + 5 model) == Specs == - specs/035-agent-chat-context/: spec, UX, plan, tasks, research, data-model, contracts, quickstart, traceability, fixtures, checklists Closes #035
74 lines
2.9 KiB
Python
74 lines
2.9 KiB
Python
# #region Test.AgentChat.ToolSummarise [C:3] [TYPE Module] [SEMANTICS test,agent,tools,summarise]
|
|
# @BRIEF Contract tests for _summarise_response — structured truncation.
|
|
# @RELATION BINDS_TO -> [AgentChat.Tools.Summarise]
|
|
# @TEST_EDGE: json_array_50_items -> Large JSON arrays summarised as top-5 + count.
|
|
# @TEST_EDGE: short_text_passthrough -> Text ≤ limit returned unchanged.
|
|
# @TEST_EDGE: json_object_keys_sample -> Large JSON objects summarised as keys + sample.
|
|
# @TEST_EDGE: non_json_sentence_boundary -> Non-JSON text truncated at sentence boundary.
|
|
|
|
import json
|
|
|
|
from src.agent.tools import _summarise_response
|
|
|
|
|
|
# #region test_summarise_json_array_50_items [C:2] [TYPE Function]
|
|
# @BRIEF JSON array with 50 items → top-5 summary with remaining count.
|
|
def test_summarise_json_array_50_items():
|
|
"""Large JSON arrays summarise with top-5 items and remaining count."""
|
|
items = [{"id": i, "name": f"item-{i}"} for i in range(50)]
|
|
text = json.dumps(items)
|
|
|
|
summary = _summarise_response(text, limit=200)
|
|
|
|
assert summary.startswith("Found 50 items:")
|
|
assert "item-0" in summary
|
|
assert "item-4" in summary
|
|
assert "... and 45 more items." in summary
|
|
# #endregion test_summarise_json_array_50_items
|
|
|
|
|
|
# #region test_summarise_short_text_passthrough [C:2] [TYPE Function]
|
|
# @BRIEF Text ≤ limit is returned unchanged (no truncation, no JSON parse overhead visible).
|
|
def test_summarise_short_text_passthrough():
|
|
"""Text within limit is returned unchanged."""
|
|
text = "This is a short response."
|
|
|
|
result = _summarise_response(text, limit=100)
|
|
|
|
assert result == text
|
|
# #endregion test_summarise_short_text_passthrough
|
|
|
|
|
|
# #region test_summarise_json_object_keys_sample [C:2] [TYPE Function]
|
|
# @BRIEF Large JSON object → key list + sample values.
|
|
def test_summarise_json_object_keys_sample():
|
|
"""Large JSON objects are summarised with keys and sample values."""
|
|
data = {f"key_{i}": f"value_{i}" * 50 for i in range(20)}
|
|
text = json.dumps(data)
|
|
|
|
summary = _summarise_response(text, limit=100)
|
|
|
|
assert summary.startswith("Result keys: ")
|
|
assert "Sample: " in summary
|
|
# #endregion test_summarise_json_object_keys_sample
|
|
|
|
|
|
# #region test_summarise_non_json_sentence_boundary [C:2] [TYPE Function]
|
|
# @BRIEF Non-JSON long text truncated at last sentence boundary before limit.
|
|
def test_summarise_non_json_sentence_boundary():
|
|
"""Non-JSON text truncates at last sentence boundary with trailing ellipsis."""
|
|
text = ("This is sentence one. " * 100)
|
|
|
|
summary = _summarise_response(text, limit=500)
|
|
|
|
# Must end with ellipsis
|
|
assert summary.endswith("...")
|
|
# Must be shorter than or equal to limit + 3 (ellipsis)
|
|
assert len(summary) <= 500
|
|
# Must retain at least one sentence boundary before the ellipsis
|
|
assert ". " in summary[:-3]
|
|
# #endregion test_summarise_non_json_sentence_boundary
|
|
|
|
|
|
# #endregion Test.AgentChat.ToolSummarise
|