mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-09-07 10:13:46 +00:00
New features: Domain_Requested_LLM_Composition, Domain_Requested_RAG, Offline_Workflow_Regression, Pause_Resume_Workflow, Voice_Interruption_Replay, Workflow_Error_Recovery, Durable Idempotency, Workflow_Pause_Resume, Dynamic_Transaction_States, Post_Finalization_Replay, Retrieval_Tool_Guardrails
This commit is contained in:
@@ -11,6 +11,8 @@ from pydantic import BaseModel
|
||||
|
||||
from agent_framework.channels.base import ChannelResponse
|
||||
from agent_framework.channels.gateway import ChannelGateway
|
||||
from agent_framework.channels.interruption import classify_processing_interruption, evaluate_interruption
|
||||
from agent_framework.channels.transcription import fix_whole_utterance_transcription
|
||||
from agent_framework.config.agent_registry import AgentProfileRegistry
|
||||
from agent_framework.config.settings import settings
|
||||
from agent_framework.analytics.factory import create_analytics_publisher
|
||||
@@ -220,13 +222,91 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
"identity_missing": missing_identity_keys,
|
||||
"original_context": normalized_context,
|
||||
}
|
||||
await sessions.upsert(session)
|
||||
|
||||
fixed_message_text = fix_whole_utterance_transcription(msg.text)
|
||||
if fixed_message_text != msg.text:
|
||||
await telemetry.event(
|
||||
"channel.transcription.fixed",
|
||||
{
|
||||
"session_id": agent_session_id,
|
||||
"original_text": msg.text,
|
||||
"fixed_text": fixed_message_text,
|
||||
},
|
||||
)
|
||||
|
||||
interruption = evaluate_interruption(
|
||||
payload=payload,
|
||||
message_text=fixed_message_text,
|
||||
session_metadata=session.metadata,
|
||||
terminal_fallback_text=getattr(settings, "POST_FINALIZE_REPLAY_MESSAGE", ""),
|
||||
)
|
||||
if interruption.action == "classify":
|
||||
prior_history = await memory.list(agent_session_id)
|
||||
prior_user_text = ""
|
||||
for prior in reversed(prior_history):
|
||||
role = getattr(prior, "role", None)
|
||||
content = getattr(prior, "content", "")
|
||||
if str(role or "") == "user" and str(content or "").strip():
|
||||
prior_user_text = str(content).strip()
|
||||
break
|
||||
regenerate = await classify_processing_interruption(
|
||||
llm,
|
||||
original_agent=interruption.replay_text,
|
||||
original_client=prior_user_text,
|
||||
supplement_client=interruption.text,
|
||||
)
|
||||
await telemetry.event(
|
||||
"channel.processing_interruption.classified",
|
||||
{
|
||||
"session_id": agent_session_id,
|
||||
"regenerate": regenerate,
|
||||
"profile_name": "processing_interruption_classifier",
|
||||
},
|
||||
)
|
||||
if regenerate:
|
||||
interruption.action = "process"
|
||||
interruption.reason = "classifier_result_1"
|
||||
else:
|
||||
interruption.action = "replay"
|
||||
interruption.reason = "classifier_result_0"
|
||||
|
||||
if interruption.action == "replay":
|
||||
response = ChannelResponse(
|
||||
channel=msg.channel,
|
||||
session_id=agent_session_id,
|
||||
text=interruption.replay_text,
|
||||
metadata={
|
||||
"channel_id": msg.channel_id,
|
||||
"tenant_id": identity.tenant_id,
|
||||
"agent_id": identity.agent_id,
|
||||
"original_session_id": msg.session_id,
|
||||
"conversation_key": agent_session_id,
|
||||
"workflow_id": workflow_id,
|
||||
"message_id": message_id,
|
||||
"replay": True,
|
||||
"replay_reason": interruption.reason,
|
||||
"is_interruptible": interruption.is_interruptible,
|
||||
"framework_short_circuit": True,
|
||||
"terminal_status": interruption.terminal_status,
|
||||
"llm_called": False,
|
||||
"tools_called": False,
|
||||
"guardrails_called": False,
|
||||
},
|
||||
)
|
||||
rendered = await gateway.render(response)
|
||||
await telemetry.event("gateway.message.replayed", {"session_id": agent_session_id, "reason": interruption.reason})
|
||||
await sse_hub.emit(agent_session_id, "message.responded", rendered) if emit_sse else None
|
||||
return rendered
|
||||
|
||||
effective_text = interruption.text
|
||||
await sse_hub.emit(agent_session_id, "session.upserted", {"session_id": agent_session_id, "business_context": business_context.model_dump()}) if emit_sse else None
|
||||
|
||||
await memory.append(
|
||||
agent_session_id,
|
||||
ChatMessage(
|
||||
role="user",
|
||||
content=msg.text,
|
||||
content=effective_text,
|
||||
metadata={
|
||||
**normalized_context,
|
||||
"agent_id": identity.agent_id,
|
||||
@@ -247,7 +327,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
"payload": payload,
|
||||
}
|
||||
trace_context = {
|
||||
"text": msg.text,
|
||||
"text": effective_text,
|
||||
"channel": msg.channel,
|
||||
"channel_id": msg.channel_id,
|
||||
"tenant_id": identity.tenant_id,
|
||||
@@ -296,7 +376,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
"customer_key": business_context.customer_key,
|
||||
"user_id": session.user_id,
|
||||
"business_context": business_context.model_dump(),
|
||||
"user_text": msg.text,
|
||||
"user_text": effective_text,
|
||||
"history": history,
|
||||
"context": {
|
||||
**normalized_context,
|
||||
@@ -336,6 +416,20 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
),
|
||||
)
|
||||
|
||||
terminal_status = str(result.get("terminal_status") or "").strip()
|
||||
session_ended = bool(result.get("session_ended")) or bool(terminal_status)
|
||||
session.metadata = {
|
||||
**(session.metadata or {}),
|
||||
"last_assistant_text": answer,
|
||||
"last_assistant_is_interruptible": bool(result.get("is_interruptible", True)),
|
||||
"last_route": result.get("route"),
|
||||
"last_intent": result.get("intent"),
|
||||
"conversation_closed": session_ended,
|
||||
"terminal_status": terminal_status or ("resolvido" if session_ended else ""),
|
||||
"terminal_replay_text": answer if session_ended else "",
|
||||
}
|
||||
await sessions.upsert(session)
|
||||
|
||||
await telemetry.event(
|
||||
"gateway.message.responded",
|
||||
{
|
||||
@@ -462,7 +556,7 @@ async def debug_route(req: GatewayRequest):
|
||||
"session_id": msg.session_id or "debug-session",
|
||||
"conversation_key": identity.conversation_key(),
|
||||
"agent_profile": context["agent_profile"],
|
||||
"user_text": msg.text,
|
||||
"user_text": effective_text,
|
||||
"sanitized_input": msg.text,
|
||||
"history": [],
|
||||
"context": {**context, "session": context.get("session", {}), "channel": msg.channel, "business_context": business_context.model_dump()},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from agent_framework.checkpoints.langgraph_saver import create_langgraph_checkpointer
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from agent_framework.workflows import END, START, FrameworkStateGraph
|
||||
|
||||
from agent_framework.guardrails.pipeline import GuardrailPipeline
|
||||
from agent_framework.guardrails.output_supervisor import OutputSupervisor
|
||||
@@ -137,7 +137,7 @@ class AgentWorkflow:
|
||||
return _wrapped
|
||||
|
||||
def _build_graph(self):
|
||||
builder = StateGraph(AgentState)
|
||||
builder = FrameworkStateGraph(AgentState)
|
||||
builder.add_node("input_guardrails", self._node("input_guardrails", self.input_guardrails))
|
||||
builder.add_node("load_long_term_memory", self._node("load_long_term_memory", self.load_long_term_memory))
|
||||
builder.add_node("routing_decision", self._node("routing_decision", self.routing_decision))
|
||||
|
||||
@@ -14,38 +14,45 @@ CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
||||
# LLM - OCI Generative AI como provider principal
|
||||
###############################################################################
|
||||
# Opções: mock, oci_openai, oci_sdk, openai_compatible
|
||||
LLM_PROVIDER=oci_openai
|
||||
LLM_PROVIDER=oci_sdk
|
||||
LLM_TEMPERATURE=0.2
|
||||
LLM_MAX_TOKENS=2048
|
||||
LLM_TIMEOUT_SECONDS=120
|
||||
|
||||
# OCI OpenAI-compatible endpoint
|
||||
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1
|
||||
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com
|
||||
OCI_GENAI_MODEL=openai.gpt-4.1
|
||||
OCI_GENAI_API_KEY=sk-ph3FgX6ph3FgX6ph3FgX6ph3FgX6ph3FgX6ph3FgX6
|
||||
OCI_GENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS
|
||||
OCI_GENAI_PROJECT_OCID=
|
||||
|
||||
#OCI_GENAI_BASE_URL=https://pegruagntaiatenddev.pe.inference.generativeai.sa-saopaulo-1.oci.oraclecloud.com
|
||||
#OCI_GENAI_MODEL=openai.gpt-4.1
|
||||
#OCI_GENAI_API_KEY=
|
||||
#OCI_GENAI_PROJECT_OCID=
|
||||
|
||||
|
||||
# OCI_AUTH_MODE=config_file|instance_principal|resource_principal
|
||||
OCI_AUTH_MODE=config_file
|
||||
# OCI SDK / signer / profiles
|
||||
OCI_CONFIG_FILE=~/.oci/config
|
||||
OCI_PROFILE=DEFAULT
|
||||
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
OCI_PROFILE=LATINOAMERICA-Chicago
|
||||
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q
|
||||
OCI_REGION=us-chicago-1
|
||||
|
||||
###############################################################################
|
||||
# Persistência
|
||||
###############################################################################
|
||||
# Opções: memory, autonomous, mongodb
|
||||
SESSION_REPOSITORY_PROVIDER=sqlite
|
||||
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||
SQLITE_DB_PATH=./data/agent_framework.db
|
||||
SESSION_REPOSITORY_PROVIDER=autonomous
|
||||
MEMORY_REPOSITORY_PROVIDER=autonomous
|
||||
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
|
||||
|
||||
# Autonomous Database
|
||||
ADB_USER=admin
|
||||
ADB_PASSWORD=fjhsdf04954hf
|
||||
ADB_DSN=oradb23aidev_high
|
||||
ADB_WALLET_LOCATION=/ORACLE/DEFAULT/Wallet_ORADB23aiDev
|
||||
ADB_WALLET_PASSWORD=fjhsdf04954hf
|
||||
ADB_PASSWORD=Moniquinha19721972
|
||||
ADB_DSN=oradb23ai_high
|
||||
ADB_WALLET_LOCATION=/mnt/d/Dropbox/ORACLE/LatinoAmerica/Wallet_ORADB23ai
|
||||
ADB_WALLET_PASSWORD=Moniquinha1972
|
||||
ADB_TABLE_PREFIX=AGENTFW
|
||||
|
||||
# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente
|
||||
@@ -59,10 +66,10 @@ ENABLE_REDIS_CACHE=false
|
||||
###############################################################################
|
||||
# RAG / Vector / Graph
|
||||
###############################################################################
|
||||
VECTOR_STORE_PROVIDER=sqlite
|
||||
GRAPH_STORE_PROVIDER=sqlite
|
||||
VECTOR_STORE_PROVIDER=autonomous
|
||||
GRAPH_STORE_PROVIDER=autonomous
|
||||
RAG_TOP_K=5
|
||||
EMBEDDING_PROVIDER=mock
|
||||
EMBEDDING_PROVIDER=oci
|
||||
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
||||
RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
||||
|
||||
@@ -70,14 +77,21 @@ RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
||||
# Observabilidade
|
||||
###############################################################################
|
||||
ENABLE_LANGFUSE=true
|
||||
LANGFUSE_TRACE_MODE=compact # Opcional: verbose, compact
|
||||
LANGFUSE_PUBLIC_KEY=pk-lf-2f9da109-5b0f-4c78-b61d-9598ed787eba
|
||||
LANGFUSE_SECRET_KEY=sk-lf-a4cb0cdd-f2ea-4468-9911-cebeb91ba944
|
||||
# Opcional: verbose, compact
|
||||
LANGFUSE_TRACE_MODE=compact
|
||||
# Nome customizado do trace pai, ex.: backoffice.checklist.workflow ou backoffice.emulador.workflow
|
||||
LANGFUSE_COMPACT_VISIBLE_EVENT_PREFIXES=AGA.,NOC., IC.
|
||||
LANGFUSE_COMPACT_SUPPRESSED_PREFIXES=llm.chat_completion
|
||||
LANGFUSE_IGNORE_HEALTHCHECKS=true
|
||||
LANGFUSE_IGNORED_PATHS=/health,/ready,/metrics
|
||||
LANGFUSE_PUBLIC_KEY=pk-lf-4a1e3921-5158-4fd3-a16d-7a77549fb312
|
||||
LANGFUSE_SECRET_KEY=sk-lf-efc6fd59-c5ec-4858-b6ec-4aa129734915
|
||||
LANGFUSE_HOST=http://localhost:3005
|
||||
ENABLE_OTEL=false
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=
|
||||
OTEL_SERVICE_NAME=ai-agent-template
|
||||
ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true
|
||||
ENABLE_LANGFUSE_ANALYTICS_PUBLISHER=false
|
||||
|
||||
###############################################################################
|
||||
# Analytics / Observer corporativo
|
||||
@@ -85,7 +99,7 @@ ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true
|
||||
# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo.
|
||||
ENABLE_ANALYTICS=false
|
||||
# Providers aceitos: oci_streaming,pubsub,noop
|
||||
ANALYTICS_PROVIDERS=pubsub
|
||||
ANALYTICS_PROVIDERS=oci_streaming
|
||||
# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente.
|
||||
AGENT_PUBSUB_TOPIC=
|
||||
GCP_PUBSUB_TOPIC_PATH=
|
||||
@@ -122,6 +136,9 @@ PROMPT_POLICY_PATH=./config/prompt_policy.yaml
|
||||
# Gateway de canais
|
||||
###############################################################################
|
||||
DEFAULT_CHANNEL=web
|
||||
# embedded = backend may parse simple/native channel payloads.
|
||||
# external = backend only accepts GatewayRequest normalized by an external Channel Gateway.
|
||||
FRAMEWORK_CHANNEL_INPUT_MODE=embedded
|
||||
ENABLE_VOICE_ADAPTER=true
|
||||
ENABLE_WHATSAPP_ADAPTER=true
|
||||
ENABLE_TEXT_ADAPTER=true
|
||||
@@ -135,7 +152,9 @@ ROUTING_CONFIG_PATH=./config/routing.yaml
|
||||
# Em produção, costuma ser útil; em desenvolvimento, false evita custo e latência.
|
||||
ENABLE_LLM_ROUTER=true
|
||||
|
||||
# Continuidade semântica, handoff humano e encerramento global.
|
||||
# Semantic route stickiness (optional).
|
||||
# Uses a lightweight LLM profile to decide only CONTINUE vs ROUTE.
|
||||
# There are no regexes or deterministic language rules.
|
||||
ENABLE_ROUTE_STICKINESS=true
|
||||
ROUTE_STICKINESS_LLM_PROFILE=route_continuity
|
||||
ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90
|
||||
@@ -143,7 +162,6 @@ ROUTE_STICKINESS_HISTORY_TURNS=2
|
||||
ROUTE_STICKINESS_MAX_TOKENS=80
|
||||
HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa.
|
||||
END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato.
|
||||
SESSION_ALREADY_ENDED_MESSAGE=Este atendimento já foi encerrado. Inicie uma nova sessão para continuar.
|
||||
|
||||
###############################################################################
|
||||
# MCP / Tools
|
||||
@@ -151,14 +169,13 @@ SESSION_ALREADY_ENDED_MESSAGE=Este atendimento já foi encerrado. Inicie uma nov
|
||||
ENABLE_MCP_TOOLS=true
|
||||
MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml
|
||||
TOOLS_CONFIG_PATH=./config/tools.yaml
|
||||
TOOL_POLICIES_PATH=./config/tool_policies.yaml
|
||||
MCP_TOOL_TIMEOUT_SECONDS=30
|
||||
|
||||
# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes
|
||||
ROUTING_MODE=router
|
||||
|
||||
# Usage/cost accounting
|
||||
USAGE_REPOSITORY_PROVIDER=sqlite
|
||||
USAGE_REPOSITORY_PROVIDER=autonomous
|
||||
IDENTITY_CONFIG_PATH=./config/identity.yaml
|
||||
MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml
|
||||
|
||||
@@ -175,18 +192,6 @@ MEMORY_SUMMARY_USE_LLM=true
|
||||
MEMORY_INJECT_RECENT_MESSAGES=true
|
||||
MEMORY_INJECT_SUMMARY=true
|
||||
|
||||
###############################################################################
|
||||
# MCP Gateway
|
||||
###############################################################################
|
||||
# true = framework routes tool calls to the dedicated MCP Gateway.
|
||||
# false = framework calls MCP servers directly from mcp_servers.yaml.
|
||||
MCP_GATEWAY_ENABLED=true
|
||||
MCP_GATEWAY_URL=http://localhost:8300
|
||||
MCP_GATEWAY_TIMEOUT_SECONDS=60
|
||||
# MCP_GATEWAY_TOKEN=
|
||||
MCP_GATEWAY_AGENT_ID=telecom_contas
|
||||
MCP_GATEWAY_TENANT_ID=default
|
||||
|
||||
###############################################################################
|
||||
# LONG-TERM MEMORY
|
||||
###############################################################################
|
||||
|
||||
@@ -11,6 +11,8 @@ from pydantic import BaseModel
|
||||
|
||||
from agent_framework.channels.base import ChannelResponse
|
||||
from agent_framework.channels.gateway import ChannelGateway
|
||||
from agent_framework.channels.interruption import classify_processing_interruption, evaluate_interruption
|
||||
from agent_framework.channels.transcription import fix_whole_utterance_transcription
|
||||
from agent_framework.config.agent_registry import AgentProfileRegistry
|
||||
from agent_framework.config.settings import settings
|
||||
from agent_framework.analytics.factory import create_analytics_publisher
|
||||
@@ -220,13 +222,91 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
"identity_missing": missing_identity_keys,
|
||||
"original_context": normalized_context,
|
||||
}
|
||||
await sessions.upsert(session)
|
||||
|
||||
fixed_message_text = fix_whole_utterance_transcription(msg.text)
|
||||
if fixed_message_text != msg.text:
|
||||
await telemetry.event(
|
||||
"channel.transcription.fixed",
|
||||
{
|
||||
"session_id": agent_session_id,
|
||||
"original_text": msg.text,
|
||||
"fixed_text": fixed_message_text,
|
||||
},
|
||||
)
|
||||
|
||||
interruption = evaluate_interruption(
|
||||
payload=payload,
|
||||
message_text=fixed_message_text,
|
||||
session_metadata=session.metadata,
|
||||
terminal_fallback_text=getattr(settings, "POST_FINALIZE_REPLAY_MESSAGE", ""),
|
||||
)
|
||||
if interruption.action == "classify":
|
||||
prior_history = await memory.list(agent_session_id)
|
||||
prior_user_text = ""
|
||||
for prior in reversed(prior_history):
|
||||
role = getattr(prior, "role", None)
|
||||
content = getattr(prior, "content", "")
|
||||
if str(role or "") == "user" and str(content or "").strip():
|
||||
prior_user_text = str(content).strip()
|
||||
break
|
||||
regenerate = await classify_processing_interruption(
|
||||
llm,
|
||||
original_agent=interruption.replay_text,
|
||||
original_client=prior_user_text,
|
||||
supplement_client=interruption.text,
|
||||
)
|
||||
await telemetry.event(
|
||||
"channel.processing_interruption.classified",
|
||||
{
|
||||
"session_id": agent_session_id,
|
||||
"regenerate": regenerate,
|
||||
"profile_name": "processing_interruption_classifier",
|
||||
},
|
||||
)
|
||||
if regenerate:
|
||||
interruption.action = "process"
|
||||
interruption.reason = "classifier_result_1"
|
||||
else:
|
||||
interruption.action = "replay"
|
||||
interruption.reason = "classifier_result_0"
|
||||
|
||||
if interruption.action == "replay":
|
||||
response = ChannelResponse(
|
||||
channel=msg.channel,
|
||||
session_id=agent_session_id,
|
||||
text=interruption.replay_text,
|
||||
metadata={
|
||||
"channel_id": msg.channel_id,
|
||||
"tenant_id": identity.tenant_id,
|
||||
"agent_id": identity.agent_id,
|
||||
"original_session_id": msg.session_id,
|
||||
"conversation_key": agent_session_id,
|
||||
"workflow_id": workflow_id,
|
||||
"message_id": message_id,
|
||||
"replay": True,
|
||||
"replay_reason": interruption.reason,
|
||||
"is_interruptible": interruption.is_interruptible,
|
||||
"framework_short_circuit": True,
|
||||
"terminal_status": interruption.terminal_status,
|
||||
"llm_called": False,
|
||||
"tools_called": False,
|
||||
"guardrails_called": False,
|
||||
},
|
||||
)
|
||||
rendered = await gateway.render(response)
|
||||
await telemetry.event("gateway.message.replayed", {"session_id": agent_session_id, "reason": interruption.reason})
|
||||
await sse_hub.emit(agent_session_id, "message.responded", rendered) if emit_sse else None
|
||||
return rendered
|
||||
|
||||
effective_text = interruption.text
|
||||
await sse_hub.emit(agent_session_id, "session.upserted", {"session_id": agent_session_id, "business_context": business_context.model_dump()}) if emit_sse else None
|
||||
|
||||
await memory.append(
|
||||
agent_session_id,
|
||||
ChatMessage(
|
||||
role="user",
|
||||
content=msg.text,
|
||||
content=effective_text,
|
||||
metadata={
|
||||
**normalized_context,
|
||||
"agent_id": identity.agent_id,
|
||||
@@ -247,7 +327,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
"payload": payload,
|
||||
}
|
||||
trace_context = {
|
||||
"text": msg.text,
|
||||
"text": effective_text,
|
||||
"channel": msg.channel,
|
||||
"channel_id": msg.channel_id,
|
||||
"tenant_id": identity.tenant_id,
|
||||
@@ -291,7 +371,7 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
"conversation_key": agent_session_id,
|
||||
"workflow_id": workflow_id,
|
||||
"agent_profile": normalized_context["agent_profile"],
|
||||
"user_text": msg.text,
|
||||
"user_text": effective_text,
|
||||
"history": history,
|
||||
"context": {
|
||||
**normalized_context,
|
||||
@@ -331,6 +411,20 @@ async def _process_gateway_message(req: GatewayRequest, emit_sse: bool = False)
|
||||
),
|
||||
)
|
||||
|
||||
terminal_status = str(result.get("terminal_status") or "").strip()
|
||||
session_ended = bool(result.get("session_ended")) or bool(terminal_status)
|
||||
session.metadata = {
|
||||
**(session.metadata or {}),
|
||||
"last_assistant_text": answer,
|
||||
"last_assistant_is_interruptible": bool(result.get("is_interruptible", True)),
|
||||
"last_route": result.get("route"),
|
||||
"last_intent": result.get("intent"),
|
||||
"conversation_closed": session_ended,
|
||||
"terminal_status": terminal_status or ("resolvido" if session_ended else ""),
|
||||
"terminal_replay_text": answer if session_ended else "",
|
||||
}
|
||||
await sessions.upsert(session)
|
||||
|
||||
await telemetry.event(
|
||||
"gateway.message.responded",
|
||||
{
|
||||
@@ -442,7 +536,7 @@ async def debug_route(req: GatewayRequest):
|
||||
"session_id": msg.session_id or "debug-session",
|
||||
"conversation_key": identity.conversation_key(),
|
||||
"agent_profile": context["agent_profile"],
|
||||
"user_text": msg.text,
|
||||
"user_text": effective_text,
|
||||
"sanitized_input": msg.text,
|
||||
"history": [],
|
||||
"context": {**context, "session": context.get("session", {}), "channel": msg.channel, "business_context": business_context.model_dump()},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from agent_framework.checkpoints.langgraph_saver import create_langgraph_checkpointer
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
from agent_framework.workflows import END, START, FrameworkStateGraph
|
||||
|
||||
from agent_framework.guardrails.pipeline import GuardrailPipeline
|
||||
from agent_framework.guardrails.output_supervisor import OutputSupervisor
|
||||
@@ -137,7 +137,7 @@ class AgentWorkflow:
|
||||
return _wrapped
|
||||
|
||||
def _build_graph(self):
|
||||
builder = StateGraph(AgentState)
|
||||
builder = FrameworkStateGraph(AgentState)
|
||||
builder.add_node("input_guardrails", self._node("input_guardrails", self.input_guardrails))
|
||||
builder.add_node("routing_decision", self._node("routing_decision", self.routing_decision))
|
||||
builder.add_node("billing_agent", self._node("billing_agent", self.billing_agent))
|
||||
|
||||
Reference in New Issue
Block a user