new feature: Multi-Intent
This commit is contained in:
@@ -40,6 +40,20 @@ def test_transactional_intent_is_primary_even_when_mentioned_second():
|
||||
assert [item.intent for item in plan.operations] == ["cancel", "invoice"]
|
||||
|
||||
|
||||
def test_sentence_boundary_does_not_leak_previous_intent_into_knowledge_query():
|
||||
planner = MultiIntentPlanner([
|
||||
IntentDefinition(name="invoice", agent="billing", priority=20, keywords=["fatura"]),
|
||||
IntentDefinition(name="knowledge", agent="support", priority=30, keywords=["tarifação"]),
|
||||
])
|
||||
plan = planner.plan(
|
||||
"quero ver minha fatura e o valor. depois gostaria de saber como funciona a tarifação do plano"
|
||||
)
|
||||
assert plan is not None
|
||||
knowledge = next(item for item in plan.operations if item.intent == "knowledge")
|
||||
assert knowledge.source_text == "depois gostaria de saber como funciona a tarifação do plano"
|
||||
assert not knowledge.source_text.startswith("valor")
|
||||
|
||||
|
||||
def test_does_not_plan_single_intent_or_invent_unknown_intent():
|
||||
assert _planner().plan("quero cancelar pedido") is None
|
||||
assert _planner().plan("quero cancelar pedido e saber o clima") is None
|
||||
|
||||
@@ -12,6 +12,8 @@ async def test_secondary_topic_does_not_inherit_primary_transaction_state():
|
||||
return {
|
||||
"answer": "Aqui está a sua segunda via.",
|
||||
"mcp_results": [{"tool_name": "consultar_faturas", "ok": True}],
|
||||
"rag": {"provider": "kbdb", "attempted": True, "status": "executed", "document_count": 1},
|
||||
"rag_context": "Documento recuperado da base de conhecimento.",
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -59,3 +61,5 @@ async def test_secondary_topic_does_not_inherit_primary_transaction_state():
|
||||
{"tool_name": "consultar_vas"},
|
||||
{"tool_name": "consultar_faturas", "ok": True},
|
||||
]
|
||||
assert result.rag_results[0]["metadata"]["provider"] == "kbdb"
|
||||
assert result.rag_results[0]["context"] == "Documento recuperado da base de conhecimento."
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from types import SimpleNamespace
|
||||
from contextlib import contextmanager
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -115,3 +116,68 @@ def test_kbdb_does_not_fallback_to_framework_adb_credentials():
|
||||
)
|
||||
with pytest.raises(RuntimeError, match="KBDB_DB_USER"):
|
||||
KbdbRagService(settings)
|
||||
|
||||
|
||||
def test_kbdb_discovers_number_flags_and_clob_output(monkeypatch):
|
||||
import sys
|
||||
from agent_framework.rag.kbdb_service import KbdbRagService
|
||||
|
||||
captured = {}
|
||||
|
||||
class Var:
|
||||
def __init__(self, value=None):
|
||||
self.value = value
|
||||
|
||||
def getvalue(self):
|
||||
return self.value
|
||||
|
||||
class Cursor:
|
||||
def execute(self, statement):
|
||||
captured["signature_query"] = statement
|
||||
|
||||
def fetchall(self):
|
||||
names = [
|
||||
("P_SEARCH_TYPE", "VARCHAR2"), ("P_QUERY", "VARCHAR2"),
|
||||
("P_TOP_K", "NUMBER"), ("P_NODE_EXPANSION", "NUMBER"),
|
||||
("P_NODE_MAX_RELATED", "NUMBER"), ("P_GRAPH_CROSS_REF", "NUMBER"),
|
||||
("P_MAX_CROSS_REF_HOPS", "NUMBER"), ("P_DOCUMENT_TYPE", "VARCHAR2"),
|
||||
("P_METADATA_JSON", "CLOB"), ("P_RESULT", "CLOB"),
|
||||
]
|
||||
return [
|
||||
(name, index, "OUT" if index == 10 else "IN", data_type, None)
|
||||
for index, (name, data_type) in enumerate(names, start=1)
|
||||
]
|
||||
|
||||
def var(self, data_type, size=None):
|
||||
captured["out_type"] = data_type
|
||||
return Var('{"units": [], "seeds": []}')
|
||||
|
||||
def callproc(self, name, binds):
|
||||
captured["procedure"] = name
|
||||
captured["binds"] = binds
|
||||
|
||||
class Connection:
|
||||
def cursor(self):
|
||||
return Cursor()
|
||||
|
||||
class FakeOracleDb:
|
||||
DB_TYPE_JSON = "JSON"
|
||||
DB_TYPE_CLOB = "CLOB"
|
||||
DB_TYPE_VARCHAR = "VARCHAR"
|
||||
|
||||
service = KbdbRagService(_settings())
|
||||
|
||||
@contextmanager
|
||||
def connect():
|
||||
yield Connection()
|
||||
|
||||
monkeypatch.setitem(sys.modules, "oracledb", FakeOracleDb)
|
||||
monkeypatch.setattr(service, "_connect", connect)
|
||||
|
||||
result = service._search_sync("como funciona?", 5)
|
||||
|
||||
assert result == {"units": [], "seeds": []}
|
||||
assert captured["procedure"] == "PKG_KB_SERVING.search_knowledge_base"
|
||||
assert captured["binds"][3] == 1
|
||||
assert captured["binds"][5] == 0
|
||||
assert captured["out_type"] == "CLOB"
|
||||
|
||||
@@ -13,7 +13,8 @@ def test_all_tuning_backend_graphs_support_pending_topics():
|
||||
assert '"pending_topics": drained.pending_topics' in source, path
|
||||
assert '"handled_topics": drained.handled_topics' in source, path
|
||||
assert '"mcp_results": drained.mcp_results' in source, path
|
||||
assert '"evidence": drained.mcp_results' in source, path
|
||||
assert '"rag_results": drained.rag_results' in source, path
|
||||
assert 'list(drained.mcp_results) + list(drained.rag_results)' in source, path
|
||||
|
||||
|
||||
def test_all_tuning_backend_states_expose_pending_topics():
|
||||
@@ -23,6 +24,7 @@ def test_all_tuning_backend_states_expose_pending_topics():
|
||||
source = path.read_text(encoding="utf-8")
|
||||
assert "pending_topics: list[dict[str, Any]]" in source, path
|
||||
assert "handled_topics: list[dict[str, Any]]" in source, path
|
||||
assert "rag_results: list[dict[str, Any]]" in source, path
|
||||
|
||||
|
||||
def test_canonical_templates_delegate_drain_to_framework_helper():
|
||||
@@ -36,7 +38,35 @@ def test_canonical_templates_delegate_drain_to_framework_helper():
|
||||
assert '"pending_topics": drained.pending_topics' in graph_source
|
||||
assert '"handled_topics": drained.handled_topics' in graph_source
|
||||
assert '"mcp_results": drained.mcp_results' in graph_source
|
||||
assert '"evidence": drained.mcp_results' in graph_source
|
||||
assert '"rag_results": drained.rag_results' in graph_source
|
||||
assert 'list(drained.mcp_results) + list(drained.rag_results)' in graph_source
|
||||
assert "MultiIntentPlanner.public_messages" not in graph_source
|
||||
assert "pending_topics: list[dict[str, Any]]" in state_source
|
||||
assert "handled_topics: list[dict[str, Any]]" in state_source
|
||||
assert "rag_results: list[dict[str, Any]]" in state_source
|
||||
|
||||
|
||||
def test_all_rag_agents_expose_metadata_and_context_as_state_evidence():
|
||||
roots = [ROOT / "templates", ROOT / "Tuning-Performance"]
|
||||
agents = sorted(
|
||||
path
|
||||
for root in roots
|
||||
for path in root.glob("**/app/agents/*.py")
|
||||
if '"rag": rag_metadata' in path.read_text(encoding="utf-8")
|
||||
)
|
||||
assert agents
|
||||
for path in agents:
|
||||
source = path.read_text(encoding="utf-8")
|
||||
assert '"rag_context": rag_context' in source, path
|
||||
assert '"rag_results": [{' in source, path
|
||||
|
||||
|
||||
def test_templates_and_tuning_expose_rag_results_in_api_and_telemetry():
|
||||
roots = [ROOT / "templates", ROOT / "Tuning-Performance"]
|
||||
mains = sorted(path for root in roots for path in root.glob("**/app/main.py"))
|
||||
graphs = sorted(path for root in roots for path in root.glob("**/app/workflows/agent_graph.py"))
|
||||
assert mains and graphs
|
||||
for path in mains:
|
||||
assert '"rag_results": result.get("rag_results")' in path.read_text(encoding="utf-8"), path
|
||||
for path in graphs:
|
||||
assert '"rag_results": state.get("rag_results", [])' in path.read_text(encoding="utf-8"), path
|
||||
|
||||
Reference in New Issue
Block a user