This commit is contained in:
2026-06-13 09:45:18 -03:00
parent 89c23fb0ed
commit 471c0e21a9
18 changed files with 837 additions and 91 deletions

View File

@@ -16,8 +16,10 @@ from agent_framework.config.settings import settings
from agent_framework.analytics.factory import create_analytics_publisher
from agent_framework.observability.observer import AgentObserver
from src.compat.framework_observer import configure as configure_global_observer
from src.compat.framework_services import configure as configure_framework_services
from agent_framework.llm.providers import create_llm
from agent_framework.memory.message_history import create_memory
from agent_framework.memory.summary_memory import create_conversation_summary_memory
from agent_framework.mcp.tool_router import create_mcp_tool_router
from agent_framework.models.identity import AgentIdentity
from agent_framework.identity import IdentityResolver, BusinessContext
@@ -49,10 +51,11 @@ telemetry = Telemetry(settings)
usage_repository = create_usage_repository(settings)
llm = create_llm(settings, telemetry=telemetry, usage_repository=usage_repository)
memory = create_memory(settings)
summary_memory = create_conversation_summary_memory(settings, message_history=memory, llm=llm, telemetry=telemetry)
sessions = create_session_repository(settings)
checkpoints = create_checkpoint_repository(settings)
cache = create_cache(settings, telemetry=telemetry)
gateway = ChannelGateway()
gateway = ChannelGateway(input_mode=settings.FRAMEWORK_CHANNEL_INPUT_MODE)
analytics = create_analytics_publisher(settings)
observer = AgentObserver(analytics=analytics)
configure_global_observer({
@@ -61,10 +64,11 @@ configure_global_observer({
"topic_path": getattr(settings, "GCP_PUBSUB_TOPIC_PATH", None) or getattr(settings, "AGENT_PUBSUB_TOPIC", None),
})
tool_router = create_mcp_tool_router(settings, telemetry=telemetry)
configure_framework_services(tool_router=tool_router)
identity_resolver = IdentityResolver.from_yaml(settings.IDENTITY_CONFIG_PATH)
agent_profiles = AgentProfileRegistry(settings)
sse_hub = SSEHub(settings, telemetry=telemetry)
workflow = AgentWorkflow(llm, memory, telemetry, analytics, settings, observer=observer, tool_router=tool_router)
workflow = AgentWorkflow(llm, memory, telemetry, analytics, settings, observer=observer, tool_router=tool_router, summary_memory=summary_memory)
backoffice_runtime = BackofficeNativeRuntime(settings=settings, telemetry=telemetry, analytics=analytics, observer=observer)
logger.info("LLM provider carregado: %s", llm.__class__.__name__)
@@ -501,6 +505,9 @@ async def shutdown():
# Elas chamam o BackofficeNativeRuntime, que compila os workflows com o motor
# do framework e aplica guardrails, judges, supervisor, checkpoint e telemetry.
from src.api.schemas.anatel_schemas import TicketRequestEvent
from src.api.schemas.anatel_response_emulator_schemas import EmulatorGenerateRequest, EmulatorFinalizeRequest
import asyncio
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@@ -531,13 +538,15 @@ async def startup_backoffice_native_domain():
if getattr(original_settings, "ENABLE_OCI_STREAMING", False):
from src.infrastructure.streaming.producer import OciProducer
app.state.oci_producer = OciProducer(original_settings.OCI_RESPONSE_STREAM_OCID)
logger.info("OCI producer de domínio inicializado; consumer legado não é iniciado")
configure_framework_services(progress_producer=app.state.oci_producer)
logger.info("OCI producer de domínio inicializado via framework services; consumer legado não é iniciado")
else:
try:
from src.infrastructure.streaming.debug_producer import LocalDebugProducer
if getattr(original_settings, "DEBUG", False):
app.state.oci_producer = LocalDebugProducer()
logger.info("LocalDebugProducer de domínio ativo")
configure_framework_services(progress_producer=app.state.oci_producer)
logger.info("LocalDebugProducer de domínio ativo via framework services")
except Exception:
pass
except Exception:
@@ -621,37 +630,34 @@ async def _run_native_checklist(event, request: Request):
@app.post("/agent/process-ticket", status_code=status.HTTP_200_OK)
async def native_process_ticket(request: Request, event: "TicketRequestEvent"):
async def native_process_ticket(request: Request, event: TicketRequestEvent):
return await _run_native_checklist(event, request)
@app.post("/agent/execute", status_code=status.HTTP_200_OK)
async def native_agent_execute(request: Request, event: "TicketRequestEvent"):
async def native_agent_execute(request: Request, event: TicketRequestEvent):
return await _run_native_checklist(event, request)
@app.post("/agent/process-and-stream", status_code=status.HTTP_200_OK)
async def native_process_and_stream(request: Request, event: "TicketRequestEvent"):
async def native_process_and_stream(request: Request, event: TicketRequestEvent):
return await _run_native_checklist(event, request)
@app.post("/agent/search-tais-kb", status_code=status.HTTP_200_OK)
async def native_search_tais_kb(body: dict):
"""Adapter REST para TAIS KB usando o service/cliente de domínio, não grafo legado."""
try:
from src.components.clients.tais_kb_client import TaisKbClient
query = body.get("query") or body.get("text") or body.get("complaint_description") or ""
client = TaisKbClient()
if hasattr(client, "search"):
result = await client.search(query=query, **{k: v for k, v in body.items() if k not in {"query", "text", "complaint_description"}})
elif hasattr(client, "query"):
result = await client.query(query)
else:
raise AttributeError("TAIS KB client has no search/query method")
return {"framework_native": True, "result": result}
except Exception as exc:
await telemetry.event("backoffice.tais_kb.failed", {"error": str(exc)}, kind="tool")
return {"framework_native": True, "result": [], "error": str(exc)}
"""Adapter REST framework-native para TAIS KB via MCP Router."""
from src.compat.framework_services import call_mcp_tool
query = body.get("query") or body.get("text") or body.get("complaint_description") or ""
result = await call_mcp_tool(
"consultar_tais_kb",
{"query": query, "protocol_id": body.get("protocol_id") or body.get("complaintProtocol"), "customer_key": body.get("customer_key") or body.get("cpfCnpj")},
business_context={"customer_key": body.get("customer_key") or body.get("cpfCnpj"), "interaction_key": body.get("protocol_id") or body.get("complaintProtocol")},
original_context=body,
)
if not result.get("ok"):
await telemetry.event("backoffice.tais_kb.failed", {"error": result.get("error")}, kind="tool")
return {"framework_native": True, "result": result}
async def _run_native_emulator(request: Request, event):
@@ -687,7 +693,7 @@ async def _run_native_emulator(request: Request, event):
@app.post("/case/{transaction_id}/response-emulator/generate", status_code=status.HTTP_200_OK)
async def native_response_emulator_generate(transaction_id: str, request: Request, body: "EmulatorGenerateRequest"):
async def native_response_emulator_generate(transaction_id: str, request: Request, body: EmulatorGenerateRequest):
from src.api.schemas.anatel_response_emulator_schemas import ResponseEmulatorRequestEvent, OperatorFeedback
if body.transactionId != transaction_id:
raise HTTPException(status_code=400, detail="transactionId path/body mismatch")
@@ -711,7 +717,7 @@ async def native_response_emulator_generate(transaction_id: str, request: Reques
@app.post("/case/{transaction_id}/response-emulator/finalize", status_code=status.HTTP_200_OK)
async def native_response_emulator_finalize(transaction_id: str, request: Request, body: "EmulatorFinalizeRequest"):
async def native_response_emulator_finalize(transaction_id: str, request: Request, body: EmulatorFinalizeRequest):
from src.api.schemas.anatel_response_emulator_schemas import ResponseEmulatorRequestEvent
if body.transactionId != transaction_id:
raise HTTPException(status_code=400, detail="transactionId path/body mismatch")
@@ -820,7 +826,3 @@ async def debug_backoffice_parity():
},
}
# Late imports only for FastAPI annotation resolution. They are schemas, not
# workflow motors.
from src.api.schemas.anatel_schemas import TicketRequestEvent
from src.api.schemas.anatel_response_emulator_schemas import EmulatorGenerateRequest, EmulatorFinalizeRequest

View File

@@ -20,6 +20,7 @@ from app.agents.support_agent import SupportAgent
from app.agents.backoffice_agent import BackofficeAgent
from app.state import AgentState
from agent_framework.rag.rag_service import RagService
from agent_framework.rag.embedding_provider import create_embedding_provider
from agent_framework.cache.cache import create_cache
@@ -85,7 +86,7 @@ class AgentWorkflow:
Em ambos os modos, memória/checkpoint/session usam tenant_id:agent_id:session_id.
"""
def __init__(self, llm, memory, telemetry, analytics, settings, observer: AgentObserver | None = None, tool_router=None):
def __init__(self, llm, memory, telemetry, analytics, settings, observer: AgentObserver | None = None, tool_router=None, summary_memory=None):
self.llm = llm
self.memory = memory
self.telemetry = telemetry
@@ -93,7 +94,7 @@ class AgentWorkflow:
self.observer = observer or AgentObserver(analytics=analytics)
self.settings = settings
self.tool_router = tool_router
self.tool_router = tool_router
self.summary_memory = summary_memory
self.guardrails = GuardrailPipeline(
observer=self.observer,
enable_parallel=bool(getattr(settings, "ENABLE_PARALLEL_GUARDRAILS", True)),
@@ -113,6 +114,7 @@ class AgentWorkflow:
self.judge_telemetry = JudgeTelemetry(telemetry)
self.langgraph_telemetry = LangGraphDeepTelemetry(telemetry)
self.cache = create_cache(settings)
self.embedding_provider = create_embedding_provider(settings)
self.rag_service = RagService(settings, telemetry=telemetry)
self.router = EnterpriseRouter(settings, llm=llm, telemetry=telemetry)
agent_kwargs = {"telemetry": telemetry, "tool_router": getattr(self, "tool_router", None), "rag_service": self.rag_service, "cache": self.cache, "settings": settings, "observer": self.observer}

View File

@@ -382,8 +382,9 @@ class BackofficeNativeRuntime:
return state
@staticmethod
def _after_input_guardrails(state: AgentState) -> str:
return "blocked" if state.get("error", {}).get("type") == "InputGuardrailBlocked" else "continue"
def _after_input_guardrails(state):
error = state.get("error") or {}
return "blocked" if error.get("type") == "InputGuardrailBlocked" else "continue"
async def _framework_output_supervisor(self, state: AgentState) -> AgentState:
await self._safe_emit_grl(
@@ -544,7 +545,8 @@ class BackofficeNativeRuntime:
"guardrails_config_path": self.guardrails_config.get("_config_path"),
"active_input_rails": [r.get("code") for r in self.guardrails_config.get("input", []) if r.get("enabled", True)],
"active_output_rails": [r.get("code") for r in self.guardrails_config.get("output", []) if r.get("enabled", True)],
"_oci_producer": getattr(app_state, "oci_producer", None) if app_state is not None else None,
# Progress producer is kept in src.compat.framework_services, not in state,
# so checkpoints remain JSON-stable and integrity hashes do not change.
"_framework_ics_bridge_index": 0,
})
try: