From 6eee4514e825f6d8cba167962972ee15b9a8e6fd Mon Sep 17 00:00:00 2001 From: "cristiano.hoshikawa" Date: Fri, 18 Sep 2026 11:47:25 -0300 Subject: [PATCH] new feature: transaction interruption for same intent replacement --- .../en/agent_framework_oci_usage_spec_en.md | 1145 ++++++++++++++ ...agent_template_backend_specification_en.md | 1363 ++++++++++++++++ .../pt/agent_framework_oci_usage_spec_pt.md | 1134 ++++++++++++++ ...agent_template_backend_especificacao_pt.md | 1364 +++++++++++++++++ .../guardrails/dlex_sanitizer.py | 201 +++ .../lib/agent_framework/guardrails/rails.py | 91 +- .../enterprise_router.cpython-313.pyc | Bin 71875 -> 80714 bytes .../routing/enterprise_router.py | 410 ++++- .../agent_framework/runtime/agent_runtime.py | 30 +- .../src/agent_framework.egg-info/SOURCES.txt | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 400 -> 413 bytes .../__pycache__/extensions.cpython-313.pyc | Bin 2141 -> 2141 bytes .../__pycache__/idempotency.cpython-313.pyc | Bin 8901 -> 8914 bytes .../__pycache__/observer.cpython-313.pyc | Bin 17557 -> 17557 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 549 -> 549 bytes .../composite_publisher.cpython-313.pyc | Bin 2504 -> 2504 bytes .../__pycache__/event_builder.cpython-313.pyc | Bin 1262 -> 1262 bytes .../__pycache__/factory.cpython-313.pyc | Bin 3998 -> 3998 bytes .../__pycache__/publisher.cpython-313.pyc | Bin 1874 -> 1874 bytes .../tim_payload_mapper.cpython-313.pyc | Bin 9288 -> 9547 bytes .../__pycache__/tim_sequence.cpython-313.pyc | Bin 17563 -> 17563 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 516 -> 516 bytes .../__pycache__/kafka.cpython-313.pyc | Bin 1814 -> 1814 bytes .../__pycache__/langfuse.cpython-313.pyc | Bin 23174 -> 23174 bytes .../__pycache__/oci_streaming.cpython-313.pyc | Bin 1823 -> 1823 bytes .../__pycache__/pubsub.cpython-313.pyc | Bin 7442 -> 7442 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 405 -> 405 bytes .../usage_repository.cpython-313.pyc | Bin 15688 -> 15688 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 202 -> 215 bytes .../cache/__pycache__/cache.cpython-313.pyc | Bin 16512 -> 16525 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 218 -> 218 bytes .../__pycache__/adapters.cpython-313.pyc | Bin 4174 -> 4174 bytes .../channels/__pycache__/base.cpython-313.pyc | Bin 1798 -> 1798 bytes .../__pycache__/gateway.cpython-313.pyc | Bin 5773 -> 5773 bytes .../__pycache__/interruption.cpython-313.pyc | Bin 6458 -> 6458 bytes .../__pycache__/transcription.cpython-313.pyc | Bin 1431 -> 1431 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 925 -> 925 bytes .../checkpoint_repository.cpython-313.pyc | Bin 27165 -> 27165 bytes .../langgraph_saver.cpython-313.pyc | Bin 33267 -> 33267 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 203 -> 216 bytes .../agent_registry.cpython-313.pyc | Bin 6949 -> 6949 bytes .../__pycache__/settings.cpython-313.pyc | Bin 13937 -> 13950 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 295 -> 308 bytes .../mcp_gateway_client.cpython-313.pyc | Bin 3321 -> 3334 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1503 -> 1503 bytes .../__pycache__/base.cpython-313.pyc | Bin 1377 -> 1377 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 9162 -> 9162 bytes .../__pycache__/custom_rails.cpython-313.pyc | Bin 4406 -> 4406 bytes .../dlex_sanitizer.cpython-313.pyc | Bin 0 -> 8489 bytes .../framework_llm_client.cpython-313.pyc | Bin 19675 -> 19675 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 5925 -> 5925 bytes .../output_supervisor.cpython-313.pyc | Bin 24308 -> 24308 bytes .../parallel_executor.cpython-313.pyc | Bin 21864 -> 21864 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 10891 -> 10891 bytes .../__pycache__/rail_action.cpython-313.pyc | Bin 656 -> 656 bytes .../__pycache__/rail_decision.cpython-313.pyc | Bin 1607 -> 1607 bytes .../__pycache__/rail_result.cpython-313.pyc | Bin 1006 -> 1006 bytes .../__pycache__/rails.cpython-313.pyc | Bin 43326 -> 45179 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 2996 -> 2996 bytes .../__pycache__/_compat.cpython-313.pyc | Bin 2447 -> 2447 bytes .../contestation_validation.cpython-313.pyc | Bin 2179 -> 2179 bytes .../__pycache__/contracts.cpython-313.pyc | Bin 6903 -> 6903 bytes .../__pycache__/input_size.cpython-313.pyc | Bin 3651 -> 3651 bytes .../__pycache__/llm_adapter.cpython-313.pyc | Bin 3801 -> 3801 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 7456 -> 7456 bytes .../__pycache__/llm_rails.cpython-313.pyc | Bin 8896 -> 8896 bytes .../output_sanitization.cpython-313.pyc | Bin 13912 -> 13912 bytes .../__pycache__/pipeline.cpython-313.pyc | Bin 12663 -> 12663 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 475 -> 475 bytes .../__pycache__/_context.cpython-313.pyc | Bin 5873 -> 5873 bytes .../ausencia_oferta_proativa.cpython-313.pyc | Bin 12207 -> 12207 bytes .../__pycache__/coerencia.cpython-313.pyc | Bin 4159 -> 4159 bytes .../__pycache__/dlex_in.cpython-313.pyc | Bin 1306 -> 1306 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 1817 -> 1817 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 17654 -> 17654 bytes .../__pycache__/fraseologia.cpython-313.pyc | Bin 7635 -> 7635 bytes .../__pycache__/out_of_scope.cpython-313.pyc | Bin 17495 -> 17495 bytes .../prompts/__pycache__/pinj.cpython-313.pyc | Bin 8426 -> 8426 bytes .../__pycache__/ragsec.cpython-313.pyc | Bin 1122 -> 1122 bytes .../__pycache__/revprec.cpython-313.pyc | Bin 3152 -> 3152 bytes .../prompts/__pycache__/tox.cpython-313.pyc | Bin 646 -> 646 bytes .../toxicidade_output.cpython-313.pyc | Bin 930 -> 930 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1665 -> 1665 bytes .../rails/__pycache__/alcada.cpython-313.pyc | Bin 5001 -> 5001 bytes .../rails/__pycache__/anatel.cpython-313.pyc | Bin 9150 -> 9150 bytes .../__pycache__/confirmation.cpython-313.pyc | Bin 11044 -> 11044 bytes .../rails/__pycache__/dlex_in.cpython-313.pyc | Bin 3281 -> 3281 bytes .../__pycache__/dlex_out.cpython-313.pyc | Bin 3333 -> 3333 bytes .../rails/__pycache__/ragsec.cpython-313.pyc | Bin 5464 -> 5464 bytes .../rails/__pycache__/revprec.cpython-313.pyc | Bin 5472 -> 5472 bytes .../rails/__pycache__/tox.cpython-313.pyc | Bin 8295 -> 8295 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 535 -> 535 bytes .../rules/__pycache__/alcada.cpython-313.pyc | Bin 1883 -> 1883 bytes .../__pycache__/pinj_patterns.cpython-313.pyc | Bin 3732 -> 3732 bytes .../__pycache__/tox_blocklist.cpython-313.pyc | Bin 1508 -> 1508 bytes .../guardrails/dlex_sanitizer.py | 201 +++ .../src/agent_framework/guardrails/rails.py | 56 +- .../__pycache__/__init__.cpython-313.pyc | Bin 397 -> 410 bytes .../__pycache__/mcp_mapper.cpython-313.pyc | Bin 4681 -> 4694 bytes .../__pycache__/models.cpython-313.pyc | Bin 3133 -> 3146 bytes .../__pycache__/resolver.cpython-313.pyc | Bin 4702 -> 4715 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 620 -> 620 bytes .../judges/__pycache__/judge.cpython-313.pyc | Bin 43986 -> 43986 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 227 -> 227 bytes .../__pycache__/llm_client.cpython-313.pyc | Bin 5406 -> 5406 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 235 -> 235 bytes .../prompts/__pycache__/aluc.cpython-313.pyc | Bin 6531 -> 6531 bytes .../prompts/__pycache__/csi.cpython-313.pyc | Bin 1334 -> 1334 bytes .../__pycache__/fallback.cpython-313.pyc | Bin 8037 -> 8037 bytes .../prompts/__pycache__/rqlt.cpython-313.pyc | Bin 1167 -> 1167 bytes .../prompts/__pycache__/vctn.cpython-313.pyc | Bin 628 -> 628 bytes .../llm/__pycache__/__init__.cpython-313.pyc | Bin 465 -> 478 bytes .../llm/__pycache__/base.cpython-313.pyc | Bin 1612 -> 1625 bytes .../profile_resolver.cpython-313.pyc | Bin 9324 -> 9324 bytes .../llm/__pycache__/providers.cpython-313.pyc | Bin 46980 -> 46980 bytes .../structured_output.cpython-313.pyc | Bin 7448 -> 7461 bytes .../llm/__pycache__/types.cpython-313.pyc | Bin 1263 -> 1276 bytes .../mcp/__pycache__/__init__.cpython-313.pyc | Bin 462 -> 475 bytes .../mcp/__pycache__/client.cpython-313.pyc | Bin 18415 -> 18428 bytes .../mcp/__pycache__/models.cpython-313.pyc | Bin 2125 -> 2138 bytes .../mcp/__pycache__/registry.cpython-313.pyc | Bin 4729 -> 4742 bytes .../__pycache__/tool_policy.cpython-313.pyc | Bin 5674 -> 5687 bytes .../__pycache__/tool_router.cpython-313.pyc | Bin 14178 -> 14191 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1124 -> 1137 bytes .../long_term_extractor.cpython-313.pyc | Bin 2515 -> 2515 bytes .../long_term_memory.cpython-313.pyc | Bin 6841 -> 6841 bytes .../long_term_models.cpython-313.pyc | Bin 1750 -> 1750 bytes .../long_term_store.cpython-313.pyc | Bin 29188 -> 29188 bytes .../message_history.cpython-313.pyc | Bin 8348 -> 8361 bytes .../summary_memory.cpython-313.pyc | Bin 12499 -> 12512 bytes .../__pycache__/summary_store.cpython-313.pyc | Bin 9509 -> 9522 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 203 -> 216 bytes .../__pycache__/identity.cpython-313.pyc | Bin 3467 -> 3467 bytes .../__pycache__/session.cpython-313.pyc | Bin 2697 -> 2710 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 1396 -> 1396 bytes .../__pycache__/code_mapper.cpython-313.pyc | Bin 21449 -> 21449 bytes .../__pycache__/context.cpython-313.pyc | Bin 7388 -> 7388 bytes .../__pycache__/event_bus.cpython-313.pyc | Bin 3634 -> 3634 bytes .../guardrail_events.cpython-313.pyc | Bin 2052 -> 2052 bytes .../__pycache__/ic_events.cpython-313.pyc | Bin 627 -> 627 bytes .../__pycache__/judge_events.cpython-313.pyc | Bin 1412 -> 1412 bytes .../langgraph_telemetry.cpython-313.pyc | Bin 4965 -> 4965 bytes .../__pycache__/llm_advisors.cpython-313.pyc | Bin 2939 -> 2939 bytes .../__pycache__/noc_contract.cpython-313.pyc | Bin 5076 -> 5076 bytes .../__pycache__/noc_otel.cpython-313.pyc | Bin 6323 -> 6323 bytes .../__pycache__/observer.cpython-313.pyc | Bin 5106 -> 5106 bytes .../__pycache__/otel.cpython-313.pyc | Bin 3413 -> 3413 bytes .../streaming_events.cpython-313.pyc | Bin 1836 -> 1836 bytes .../__pycache__/telemetry.cpython-313.pyc | Bin 52429 -> 52429 bytes .../__pycache__/token_cost.cpython-313.pyc | Bin 8692 -> 8692 bytes .../workflow_events.cpython-313.pyc | Bin 3331 -> 3331 bytes .../oci/__pycache__/__init__.cpython-313.pyc | Bin 213 -> 213 bytes .../oci/__pycache__/auth.cpython-313.pyc | Bin 3245 -> 3245 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 208 -> 221 bytes .../__pycache__/oracle_store.cpython-313.pyc | Bin 45933 -> 45933 bytes .../__pycache__/sqlite_store.cpython-313.pyc | Bin 15007 -> 15020 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 456 -> 456 bytes .../__pycache__/renderers.cpython-313.pyc | Bin 4319 -> 4319 bytes .../rag/__pycache__/__init__.cpython-313.pyc | Bin 711 -> 711 bytes .../embedding_provider.cpython-313.pyc | Bin 6518 -> 6518 bytes .../__pycache__/graph_store.cpython-313.pyc | Bin 5529 -> 5529 bytes .../rag/__pycache__/ingest.cpython-313.pyc | Bin 12582 -> 12582 bytes .../__pycache__/kbdb_service.cpython-313.pyc | Bin 10013 -> 15501 bytes .../__pycache__/rag_service.cpython-313.pyc | Bin 10899 -> 10899 bytes .../__pycache__/vector_store.cpython-313.pyc | Bin 18657 -> 18657 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 222 -> 222 bytes .../session_repository.cpython-313.pyc | Bin 8591 -> 8591 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 636 -> 649 bytes .../__pycache__/config_loader.cpython-313.pyc | Bin 2678 -> 2691 bytes .../__pycache__/continuity.cpython-313.pyc | Bin 15922 -> 15935 bytes .../enterprise_router.cpython-313.pyc | Bin 74929 -> 82180 bytes .../__pycache__/models.cpython-313.pyc | Bin 2435 -> 2448 bytes .../__pycache__/multi_intent.cpython-313.pyc | Bin 20255 -> 20268 bytes .../pending_topics.cpython-313.pyc | Bin 16121 -> 16134 bytes .../routing/enterprise_router.py | 417 +++-- .../__pycache__/__init__.cpython-313.pyc | Bin 337 -> 350 bytes .../__pycache__/agent_runtime.cpython-313.pyc | Bin 206049 -> 206215 bytes .../transaction_context.cpython-313.pyc | Bin 0 -> 8445 bytes .../transaction_parameters.cpython-313.pyc | Bin 19572 -> 19585 bytes .../agent_framework/runtime/agent_runtime.py | 30 +- .../sse/__pycache__/__init__.cpython-313.pyc | Bin 213 -> 213 bytes .../sse/__pycache__/events.cpython-313.pyc | Bin 9589 -> 9589 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 220 -> 220 bytes .../__pycache__/supervisor.cpython-313.pyc | Bin 4945 -> 5405 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 867 -> 880 bytes .../__pycache__/graph.cpython-313.pyc | Bin 1404 -> 1417 bytes .../input_contract.cpython-313.pyc | Bin 8779 -> 8792 bytes .../__pycache__/models.cpython-313.pyc | Bin 7658 -> 7671 bytes .../__pycache__/registry.cpython-313.pyc | Bin 2350 -> 2363 bytes .../__pycache__/repository.cpython-313.pyc | Bin 2703 -> 2716 bytes .../__pycache__/runtime.cpython-313.pyc | Bin 36141 -> 36154 bytes .../__pycache__/tool_executor.cpython-313.pyc | Bin 1955 -> 1968 bytes .../conftest.cpython-313-pytest-9.0.2.pyc | Bin 796 -> 801 bytes ...saction_shift.cpython-313-pytest-9.0.2.pyc | Bin 28008 -> 28012 bytes ...ral_candidate.cpython-313-pytest-9.0.2.pyc | Bin 41067 -> 64263 bytes ...lm_precedence.cpython-313-pytest-9.0.2.pyc | Bin 57836 -> 58415 bytes ...ession_matrix.cpython-313-pytest-9.0.2.pyc | Bin 52053 -> 52059 bytes ..._interruption.cpython-313-pytest-9.0.2.pyc | Bin 28279 -> 28284 bytes ...est_same_intent_transaction_replacement.py | 164 ++ ...it_abandonment_and_structural_candidate.py | 180 ++- ...st_transaction_parameter_llm_precedence.py | 18 +- ...ics_isolation.cpython-313-pytest-9.0.2.pyc | Bin 33547 -> 33551 bytes 202 files changed, 6594 insertions(+), 211 deletions(-) create mode 100644 agent_framework_oci/deploy/docs/en/agent_framework_oci_usage_spec_en.md create mode 100644 agent_framework_oci/deploy/docs/en/agent_template_backend_specification_en.md create mode 100644 agent_framework_oci/deploy/docs/pt/agent_framework_oci_usage_spec_pt.md create mode 100644 agent_framework_oci/deploy/docs/pt/agent_template_backend_especificacao_pt.md create mode 100644 agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py create mode 100644 agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/dlex_sanitizer.cpython-313.pyc create mode 100644 agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/dlex_sanitizer.py create mode 100644 agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_context.cpython-313.pyc create mode 100644 agent_framework_oci/tests/test_same_intent_transaction_replacement.py diff --git a/agent_framework_oci/deploy/docs/en/agent_framework_oci_usage_spec_en.md b/agent_framework_oci/deploy/docs/en/agent_framework_oci_usage_spec_en.md new file mode 100644 index 00000000..caf82051 --- /dev/null +++ b/agent_framework_oci/deploy/docs/en/agent_framework_oci_usage_spec_en.md @@ -0,0 +1,1145 @@ +# `agent_framework_oci` Usage Specification + +## 1. Purpose + +This specification defines the standard for developing, packaging, and deploying agent projects based on `agent_framework_oci`. + +The primary goal is to establish a clear separation between: + +- the **shared enterprise framework**, responsible for generic platform capabilities; +- the **agent projects**, responsible for domain-specific rules, configuration, workflows, and implementations. + +The `agent_framework` must not be embedded or duplicated inside each agent project. + +The library must be made available separately and consumed by agent projects through standard Python imports. + +--- + +## 2. Framework source + +In the official `agent_framework_oci` repository, the shared framework implementation is located at: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +This directory is the distributable Python package for the framework. + +Its structure includes, among other components: + +```text +agent_framework/ +├── pyproject.toml +├── src/ +│ └── agent_framework/ +│ ├── analytics/ +│ ├── cache/ +│ ├── channels/ +│ ├── guardrails/ +│ ├── memory/ +│ ├── observability/ +│ ├── routing/ +│ ├── supervisor/ +│ ├── workflows/ +│ └── ... +``` + +The `pyproject.toml` defines the package: + +```toml +[project] +name = "agent-framework" +version = "0.1.0" +``` + +and uses: + +```toml +[tool.setuptools.packages.find] +where = ["src"] +``` + +Therefore: + +```text +agent_framework_oci/libs/agent_framework +``` + +must be treated as an independent, reusable Python library. + +--- + +## 3. Required directory structure for agent projects + +When using the framework outside the main repository, the directory: + +```text +agent_framework_oci/libs/agent_framework +``` + +must be made available together with the agent project. + +The agent project and the framework must reside **at the same directory level**. + +Example: + +```text +workspace/ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +└── backoffice/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +Another example: + +```text +workspace/ +├── agent_framework/ +│ +└── agent_contas/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +The architectural rule is: + +```text +/ +├── agent_framework/ +└── / +``` + +and not: + +```text +/ +└── agent_framework/ +``` + +The agent project must not contain a private copy of the framework. + +--- + +## 4. Responsibilities + +### 4.1 `agent_framework` + +The `agent_framework` contains generic and reusable platform capabilities such as: + +- routing; +- supervisor; +- multi-agent; +- workflows; +- conversational states; +- parameter collection; +- pause/resume; +- pending actions; +- multi-intent; +- intent shift; +- route stickiness; +- deterministic transactions; +- guardrails; +- judges; +- output supervisor; +- RAG; +- memory; +- cache; +- observability; +- telemetry; +- MCP integration; +- persistence; +- analytics; +- OCI provider integration; +- resilience mechanisms; +- idempotency; +- error handling. + +These components belong to the framework and must not be copied into consumer projects. + +### 4.2 Agent project + +Each agent project contains only domain-specific implementation and configuration. + +Example: + +```text +backoffice/ +├── app/ +│ ├── agents/ +│ ├── workflows/ +│ ├── observability/ +│ ├── workflow_actions/ +│ ├── presentation/ +│ ├── state.py +│ └── main.py +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── prompt_policy.yaml +│ ├── identity.yaml +│ └── mcp_parameter_mapping.yaml +│ +├── data/ +├── requirements.txt +├── Dockerfile +└── .env +``` + +The project may use any public framework capability through standard imports. + +Example: + +```python +from agent_framework.routing import ... +``` + +or: + +```python +from agent_framework.supervisor import ... +``` + +or: + +```python +from agent_framework.observability import ... +``` + +The project code must not depend on internal repository paths from `agent_framework_oci`. + +--- + +## 5. Local development installation + +Given: + +```text +workspace/ +├── agent_framework/ +└── backoffice/ +``` + +the recommended setup is: + +```bash +cd workspace + +python -m venv .venv + +source .venv/bin/activate +``` + +On Windows: + +```powershell +.venv\Scripts\activate +``` + +Install the framework: + +```bash +pip install -e ./agent_framework +``` + +Install project dependencies: + +```bash +pip install -r ./backoffice/requirements.txt +``` + +Editable mode: + +```bash +pip install -e +``` + +is recommended for development because the Python environment references the framework source directly. + +Framework changes then become immediately available to the agent project without a complete package reinstall. + +--- + +## 6. Mandatory validation + +After installing the framework, the following command must execute successfully: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +Imports used by the application should also be validated: + +```bash +python -c "from agent_framework import *" +``` + +or with specific imports required by the agent implementation. + +An error such as: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indicates that the framework was not made available or installed correctly. + +--- + +## 7. CI/CD model + +A CI/CD pipeline should create a workspace containing both components. + +Example: + +```text +build/ +├── agent_framework/ +└── backoffice/ +``` + +The framework may be obtained directly from: + +```text +agent_framework_oci/libs/agent_framework +``` + +and copied to: + +```text +build/agent_framework +``` + +The agent project should be copied to: + +```text +build/backoffice +``` + +The Docker build must use: + +```text +build/ +``` + +as the build context. + +Example: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +This is important. + +The build context must be: + +```text +. +``` + +representing the directory that contains both: + +```text +agent_framework/ +backoffice/ +``` + +--- + +## 8. Standard Dockerfile + +An agent project should use a Dockerfile similar to the following: + +```dockerfile +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PIP_NO_CACHE_DIR=1 + +WORKDIR /workspace + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* + +# Shared framework +COPY agent_framework /workspace/agent_framework + +# Agent-specific project +COPY backoffice /workspace/backoffice + +# Install enterprise framework first +RUN pip install --upgrade pip \ + && pip install /workspace/agent_framework + +# Install agent-specific dependencies +RUN pip install -r /workspace/backoffice/requirements.txt + +WORKDIR /workspace/backoffice + +EXPOSE 8000 + +CMD [ + "uvicorn", + "app.main:app", + "--host", + "0.0.0.0", + "--port", + "8000" +] +``` + +For development, the following may optionally be used: + +```dockerfile +RUN pip install -e /workspace/agent_framework +``` + +For immutable production images, the recommended form is: + +```dockerfile +RUN pip install /workspace/agent_framework +``` + +This freezes the framework version used by the image at build time. + +--- + +## 9. Container directory structure + +The container should conceptually preserve the same separation used during development: + +```text +/workspace/ +├── agent_framework/ +└── backoffice/ +``` + +The application runs from: + +```text +/workspace/backoffice +``` + +The framework is installed in the Python environment and may be imported normally: + +```python +import agent_framework +``` + +There should be no need to use: + +```python +sys.path.append(...) +``` + +or dynamically modify `PYTHONPATH` inside the application code. + +--- + +## 10. Framework versioning + +The framework version used by the agent must be determined at build time. + +At minimum, the following should be recorded: + +```text +AGENT_FRAMEWORK_VERSION +AGENT_APPLICATION_VERSION +BUILD_ID +GIT_COMMIT +``` + +Example: + +```yaml +env: + - name: AGENT_FRAMEWORK_VERSION + value: "1.5.0" + + - name: AGENT_APPLICATION_VERSION + value: "2.1.4" +``` + +This makes it possible to identify the exact: + +```text +Framework + Agent +``` + +combination running in each environment. + +--- + +## 11. Image publication + +After the build: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +the image should be published to OCI Container Registry — OCIR. + +Example: + +```bash +docker tag \ + backoffice:1.0.0 \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +Then: + +```bash +docker push \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +--- + +## 12. Deployment on OCI OKE + +Each agent project should have its own Kubernetes `Deployment`. + +The framework does not need to run as an independent Pod. + +`agent_framework` is a library and is included in the agent project image during the build process. + +Architecture: + +```text + OCI OKE + | + v + Kubernetes Service + | + +------------+------------+ + | | | + v v v + Agent Pod Agent Pod Agent Pod + | | | + +------------+------------+ + | + agent_framework + installed in image +``` + +The framework code is shared by source and version, but is installed into each agent image. + +--- + +## 13. Base OKE YAML manifest + +The following manifest can be used as a base for deploying a project such as `backoffice`. + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: agent-platform + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: backoffice-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENT_FRAMEWORK_VERSION: "1.0.0" + + AGENT_ID: "backoffice" + AGENT_APPLICATION_VERSION: "1.0.0" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + ENABLE_LANGFUSE: "true" + ENABLE_OTEL: "true" + ENABLE_ANALYTICS: "true" + +--- +apiVersion: v1 +kind: Secret +metadata: + name: backoffice-secrets + namespace: agent-platform +type: Opaque +stringData: + OCI_COMPARTMENT_ID: "" + OCI_GENAI_API_KEY: "" + LANGFUSE_PUBLIC_KEY: "" + LANGFUSE_SECRET_KEY: "" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backoffice + namespace: agent-platform + labels: + app: backoffice + +spec: + replicas: 3 + + selector: + matchLabels: + app: backoffice + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: backoffice + + spec: + containers: + - name: backoffice + image: gru.ocir.io//agents/backoffice:1.0.0 + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8000 + protocol: TCP + + envFrom: + - configMapRef: + name: backoffice-config + - secretRef: + name: backoffice-secrets + + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + + securityContext: + allowPrivilegeEscalation: false + +--- +apiVersion: v1 +kind: Service +metadata: + name: backoffice + namespace: agent-platform + +spec: + type: ClusterIP + + selector: + app: backoffice + + ports: + - name: http + protocol: TCP + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: backoffice + namespace: agent-platform + +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: backoffice + + minReplicas: 3 + maxReplicas: 10 + + behavior: + scaleUp: + stabilizationWindowSeconds: 0 + + policies: + - type: Percent + value: 100 + periodSeconds: 60 + + - type: Pods + value: 4 + periodSeconds: 60 + + selectPolicy: Max + + scaleDown: + stabilizationWindowSeconds: 300 + + policies: + - type: Percent + value: 25 + periodSeconds: 60 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 75 + +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: backoffice + namespace: agent-platform + +spec: + minAvailable: 2 + + selector: + matchLabels: + app: backoffice +``` + +--- + +## 14. Applying the manifest + +The manifest may be stored as: + +```text +deploy/oke/backoffice.yaml +``` + +and applied with: + +```bash +kubectl apply -f deploy/oke/backoffice.yaml +``` + +Validation: + +```bash +kubectl -n agent-platform get pods +kubectl -n agent-platform get deployment +kubectl -n agent-platform get svc +kubectl -n agent-platform get hpa +``` + +To track rollout status: + +```bash +kubectl -n agent-platform rollout status deployment/backoffice +``` + +--- + +## 15. Health check + +Every agent project deployed to OKE should provide: + +```http +GET /health +``` + +returning: + +```text +200 OK +``` + +while the application is ready to receive requests. + +This endpoint is used by: + +```text +startupProbe +readinessProbe +livenessProbe +``` + +--- + +## 16. Persistence and multiple replicas + +OKE applications must assume that multiple Pods may run simultaneously. + +Conversational and transactional data that must survive across requests must therefore not depend exclusively on the container filesystem. + +Local SQLite is not recommended as the definitive persistence layer for multi-replica OKE environments. + +Production environments should use external providers for: + +- sessions; +- checkpoints; +- memory; +- cache; +- idempotency; +- locks; +- analytics; +- transactional state. + +Depending on the framework configuration, examples include: + +```text +Oracle Autonomous Database +MongoDB +Redis +OCI Streaming +OCI Object Storage +``` + +--- + +## 17. Framework upgrades + +An agent project must not receive framework changes by copying individual framework source files into the project. + +Upgrades should be performed by replacing the available version of: + +```text +agent_framework/ +``` + +and rebuilding the image. + +Process: + +```text +New agent_framework version + | + v +CI/CD workspace update + | + v +Agent image build + | + v +Tests + | + v +OCIR push + | + v +OKE Rolling Update +``` + +This maintains a clear separation between: + +```text +product/framework +``` + +and: + +```text +agent implementation +``` + +--- + +## 18. Compatibility rule + +Each project release should explicitly declare the framework version against which it was tested. + +Example: + +```text +Backoffice 3.2.0 +Agent Framework 1.8.0 +``` + +A new framework version should not be promoted directly to production without executing regression tests for the consuming agent project. + +Recommended pipeline: + +```text +Framework + | + v +Build Agent + | + v +Unit Tests + | + v +Integration Tests + | + v +Workflow Regression + | + v +Smoke Test + | + v +Staging + | + v +Production +``` + +--- + +## 19. Multiple projects using the same framework + +The structure may support several projects simultaneously: + +```text +workspace/ +├── agent_framework/ +├── backoffice/ +├── contas/ +├── ofertas/ +├── atendimento/ +└── suporte/ +``` + +All of them use: + +```text +agent_framework/ +``` + +as the common platform source. + +Each application, however, generates its own image: + +```text +backoffice:3.0.0 +contas:5.2.0 +ofertas:1.4.0 +atendimento:2.6.0 +``` + +All images may have been built with: + +```text +agent-framework:1.8.0 +``` + +This allows projects to evolve independently without forking the framework. + +--- + +## 20. Architectural principle + +The relationship between the components should remain: + +```text + agent_framework + | + +---------------+---------------+ + | | | + v v v + Backoffice Contas Ofertas + | | | + v v v + OCI image OCI image OCI image + | | | + +---------------+---------------+ + | + v + OKE +``` + +`agent_framework` is the common technology platform. + +Agent projects are consumers of that platform. + +Therefore: + +**the agent project depends on the framework; the framework must not depend on the agent project.** + +--- + +## 21. Mandatory requirements + +To be considered compatible with `agent_framework_oci`, a project must satisfy the following requirements: + +1. `agent_framework` must be obtained from: + +```text +agent_framework_oci/libs/agent_framework +``` + +2. The framework must be made available separately from the agent project. + +3. The distribution structure must be: + +```text +/ +├── agent_framework/ +└── / +``` + +4. The framework must not be duplicated inside the agent project. + +5. The library must be installed using the standard Python packaging mechanism: + +```bash +pip install ./agent_framework +``` + +or, for development: + +```bash +pip install -e ./agent_framework +``` + +6. Imports must use: + +```python +import agent_framework +``` + +and never physical repository paths. + +7. The Docker build context must have access to both: + +```text +agent_framework/ +``` + +and: + +```text +/ +``` + +8. Each image must contain a known framework version. + +9. OKE deployments must expose health probes. + +10. Scalable applications must externalize shared state. + +11. Secrets must not be stored in source code or committed deployment manifests. + +12. Each project must have its own Deployment and Service. + +13. Horizontal scaling must occur at the agent application layer, not through a centralized executable `agent_framework` instance. + +--- + +## 22. Recommended final structure + +The reference structure for development and CI/CD is: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +├── backoffice/ +│ ├── app/ +│ ├── config/ +│ ├── data/ +│ ├── requirements.txt +│ ├── Dockerfile +│ └── .env.example +│ +└── deploy/ + └── oke/ + └── backoffice.yaml +``` + +For new projects, only the project-specific directory changes: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ +├── new_agent/ +│ +└── deploy/ + └── oke/ + └── new_agent.yaml +``` + +This should be the standard distribution and deployment model for projects built on `agent_framework_oci`. diff --git a/agent_framework_oci/deploy/docs/en/agent_template_backend_specification_en.md b/agent_framework_oci/deploy/docs/en/agent_template_backend_specification_en.md new file mode 100644 index 00000000..23390d1f --- /dev/null +++ b/agent_framework_oci/deploy/docs/en/agent_template_backend_specification_en.md @@ -0,0 +1,1363 @@ +# `agent_template_backend` Usage Specification + +## Complementary document to the general `agent_framework_oci` architecture + +> **Scope of this document** +> +> This document is specific to the `agent_template_backend` distributed with `agent_framework_oci`. +> +> It **does not replace** the general architectural specification for using `agent_framework_oci` in production projects. The general specification remains the normative reference for production project structure, packaging, versioning, CI/CD, state externalization, security, scalability, and OKE deployment. +> +> This document explains how the existing `agent_template_backend` should be used as a **reference template**, development environment, starting point for new agents, and source for derived production projects. + +--- + +## 1. Purpose + +`agent_template_backend` is the agent backend template provided by the `agent_framework_oci` repository. + +Its purpose is to provide a functional starting implementation showing how an agent project consumes `agent_framework`, organizes configuration, exposes APIs, uses workflows, integrates MCP, applies guardrails and judges, and consumes the other enterprise capabilities of the framework. + +The template should be understood as: + +- an implementation reference; +- a starting point for new projects; +- a development and validation environment; +- an integration example for `agent_framework`; +- a deployment example; +- a source for independent derived projects. + +The template **must not be interpreted as an exception** to the architectural rules defined by the general production specification. + +--- + +## 2. Relationship with the general architectural specification + +There are two documentation levels. + +### 2.1. General architectural specification + +The general specification defines the standard for production projects derived from the framework: + +```text +/ +├── agent_framework/ +└── / +``` + +It defines, among other items: + +- separation between framework and project; +- `agent_framework` installation; +- versioning; +- build context; +- Docker; +- CI/CD; +- OKE deployment; +- scalability; +- state externalization; +- secret management; +- compatibility between framework and agent versions. + +### 2.2. This specification + +This document describes how the template delivered in the repository fits into that model. + +Whenever a demonstration setting in the template differs from a rule in the general architectural specification, **the general architectural specification takes precedence for production environments**. + +--- + +## 3. Template location in the repository + +In the source repository, the template is located at: + +```text +agent_framework_oci/ +└── templates/ + └── agent_template_backend/ +``` + +The shared framework is located at: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +Therefore, inside the development repository, the structure is: + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +├── mcp/ +├── deploy/ +└── ... +``` + +This is the **source structure of the `agent_framework_oci` product**. + +It does not change the production architecture rule. + +--- + +## 4. Template distribution structure + +When `agent_template_backend` is extracted from the repository for independent use, isolated build, or creation of a derived project, the workspace should materialize: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +The content of: + +```text +agent_framework_oci/libs/agent_framework/ +``` + +should be made available as: + +```text +workspace/agent_framework/ +``` + +and the content of: + +```text +agent_framework_oci/templates/agent_template_backend/ +``` + +as: + +```text +workspace/agent_template_backend/ +``` + +This structure is compatible with the Dockerfile currently delivered with the template. + +--- + +## 5. Current `agent_template_backend` structure + +The main template structure is: + +```text +agent_template_backend/ +├── app/ +│ ├── main.py +│ ├── state.py +│ ├── mcp_gateway_client_factory.py +│ └── ... +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── identity.yaml +│ ├── prompt_policy.yaml +│ ├── mcp_parameter_mapping.yaml +│ ├── mcp_servers.yaml +│ └── mcp_servers.docker.yaml +│ +├── workflows/ +│ ├── devolucao_pedido.active.yaml +│ └── devolucao_pedido.v1.yaml +│ +├── data/ +├── docs/ +├── scripts/ +├── llm_profiles.yaml +├── requirements.txt +├── Dockerfile +├── .env.example +├── README.md +└── README_ENTERPRISE_TEMPLATE.md +``` + +The template contains enough configuration to demonstrate enterprise framework capabilities without copying those capabilities into the project. + +--- + +## 6. Dependency on `agent_framework` + +`agent_template_backend` is a framework consumer. + +The dependency must remain one-way: + +```text +agent_template_backend + | + v +agent_framework +``` + +The inverse must not occur. + +`agent_framework` must not depend on: + +```text +templates/agent_template_backend +``` + +or on template-specific files. + +--- + +## 7. Local installation from the source repository + +For development directly inside the repository: + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +the framework may be installed in editable mode. + +Example, from `templates/agent_template_backend`: + +```bash +pip install -e ../../libs/agent_framework +pip install -r requirements.txt +``` + +Or, from the repository root: + +```bash +pip install -e ./libs/agent_framework +pip install -r ./templates/agent_template_backend/requirements.txt +``` + +Editable mode is appropriate for product development because changes made under `libs/agent_framework` are immediately consumed by the template. + +--- + +## 8. Installation in an independent workspace + +When the template is distributed according to the general architecture: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +use: + +```bash +pip install -e ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +For a production build: + +```bash +pip install ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +--- + +## 9. Installation validation + +The installation must allow: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +and, from the project: + +```bash +python -c "import app.main" +``` + +An error such as: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indicates that the framework was not made available or installed correctly. + +--- + +## 10. Backend execution + +The template backend exposes its FastAPI application on: + +```text +8000 +``` + +Local execution: + +```bash +uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +``` + +Health check: + +```bash +curl http://localhost:8000/health +``` + +Other useful checks: + +```bash +curl http://localhost:8000/agents +curl http://localhost:8000/debug/mcp/tools +``` + +Port `8000` belongs to the agent backend and must not be confused with MCP Server or gateway ports. + +--- + +## 11. Agent configuration + +Template-specific behavior is primarily defined by files external to the framework. + +### `config/agents.yaml` + +Defines available agents and their profiles. + +### `config/routing.yaml` + +Defines intents, routes, and routing behavior. + +### `config/tools.yaml` + +Defines project tools and their MCP bindings. + +### `config/tool_policies.yaml` + +Defines execution, confirmation, control, and transactional requirements for tools. + +### `config/guardrails.yaml` + +Configures project guardrails. + +### `config/judges.yaml` + +Configures judges. + +### `config/identity.yaml` + +Defines identity and business keys. + +### `config/mcp_parameter_mapping.yaml` + +Defines parameter mapping and extraction for MCP tools. + +### `config/mcp_servers.yaml` + +Defines MCP Servers used in local execution. + +### `config/mcp_servers.docker.yaml` + +Defines endpoints suitable for Docker/container environments. + +### `llm_profiles.yaml` + +Defines LLM profiles and providers used by the project. + +This model preserves the separation between: + +```text +generic engine = agent_framework +``` + +and: + +```text +domain rules = agent_template_backend/config + app +``` + +--- + +## 12. Example MCP Servers + +The repository includes development MCP Servers under: + +```text +agent_framework_oci/mcp/servers/ +├── telecom_mcp_server/ +├── retail_mcp_server/ +└── mock_telecom_mcp/ +``` + +The main examples use: + +```text +Telecom MCP : 8100 +Retail MCP : 8200 +``` + +The local template configuration references: + +```yaml +servers: + telecom: + endpoint: http://localhost:8100/mcp + + retail: + endpoint: http://localhost:8200/mcp +``` + +These MCP Servers are separate components from the agent backend. + +--- + +## 13. Starting MCP Servers in development + +The repository provides: + +```text +scripts/run_mcp_servers.sh +``` + +which starts the example MCP Servers. + +Local architecture: + +```text + +------------------+ + | Telecom MCP | + | :8100 | + +---------+--------+ + | + | ++----------------------+ | +| agent_template |---------+ +| backend :8000 | ++----------------------+---------+ + | + | + +---------v--------+ + | Retail MCP | + | :8200 | + +------------------+ +``` + +This script is a development convenience. + +In production, MCP Servers should be deployed according to the target architecture and should not depend on shell processes started inside the backend Pod. + +--- + +## 14. MCP Gateway + +`agent_framework_oci` also contains: + +```text +apps/mcp_gateway/ +``` + +and OKE configuration for the MCP Gateway. + +The default repository example uses: + +```text +8300 +``` + +The template supports: + +```env +MCP_GATEWAY_ENABLED=true +MCP_GATEWAY_URL=http://localhost:8300 +``` + +or, in OKE: + +```text +http://mcp-gateway.agent-platform.svc.cluster.local:8300 +``` + +The MCP Gateway is a platform component and is not physically part of the `agent_template_backend` directory. + +--- + +## 15. Agent Gateway, Channel Gateway, and Frontend + +The repository also contains: + +```text +apps/ +├── agent_gateway/ +├── channel_gateway/ +├── mcp_gateway/ +└── agent_frontend/ +``` + +These should be treated as platform or channel components. + +`agent_template_backend` can operate directly on port `8000` without all of them being required for a simple local test. + +In a complete enterprise topology, the flow may be: + +```text +Client / Frontend + | + v +Channel Gateway + | + v +Agent Gateway + | + v +agent_template_backend + | + v +MCP Gateway + | + v +MCP Servers +``` + +The actual composition must follow the requirements of the target environment. + +--- + +## 16. Development frontend + +The reference frontend is located at: + +```text +agent_framework_oci/apps/agent_frontend/ +``` + +and points by default to: + +```text +http://localhost:8000 +``` + +The frontend should not be moved into `agent_template_backend` merely to satisfy a project structure. + +It is a separate component. + +A production project may: + +- use this frontend; +- use another frontend; +- integrate Web/WhatsApp/Voice; +- expose APIs only; +- use Agent Gateway/Channel Gateway. + +--- + +## 17. Template Dockerfile + +The Dockerfile at: + +```text +templates/agent_template_backend/Dockerfile +``` + +expects a build context containing both: + +```text +agent_framework/ +agent_template_backend/ +``` + +Structure: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +Build: + +```bash +docker build \ + -t agent-template-backend:local \ + -f agent_template_backend/Dockerfile \ + . +``` + +This is aligned with the general architectural specification. + +--- + +## 18. Existing OKE Dockerfile in the repository + +The repository also provides: + +```text +deploy/oke/dockerfiles/Dockerfile.agent-template-backend +``` + +This Dockerfile runs with the `agent_framework_oci` repository root as context and copies: + +```text +libs/agent_framework -> /opt/agent_framework +templates/agent_template_backend -> /app +``` + +There are therefore two valid build contexts for different purposes. + +### Independent development/distribution + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +### Build directly from the monorepo + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +Both materialize the same logical dependency: + +```text +agent_template_backend -> agent_framework +``` + +--- + +## 19. Deriving a production project + +`agent_template_backend` should be used as the source for a new project. + +Example: + +```text +agent_template_backend + | + | derive + v +agent_backoffice +``` + +After derivation, the recommended production structure is: + +```text +workspace/ +├── agent_framework/ +└── agent_backoffice/ +``` + +There is no requirement to preserve the name: + +```text +agent_template_backend +``` + +in production. + +The derived project then owns: + +- its own version lifecycle; +- its own Docker image; +- its own configuration; +- its own agents; +- its own workflows; +- its own policies; +- its own pipeline. + +The `agent_framework` library remains shared. + +--- + +## 20. What should and should not be copied + +### Should be derived into the project + +As required by the domain: + +```text +app/ +config/ +workflows/ +data/ +scripts/ +requirements.txt +Dockerfile +.env.example +``` + +### Should not be copied into the project + +```text +libs/agent_framework +``` + +The framework must remain separately distributed and installed. + +It is also not necessary to automatically copy: + +```text +apps/agent_gateway +apps/channel_gateway +apps/mcp_gateway +apps/agent_frontend +mcp/servers +``` + +Those components should only be included when required by the target topology. + +--- + +## 21. OKE: relationship with the general architecture + +The repository contains a reference OKE implementation under: + +```text +deploy/oke/ +``` + +including: + +```text +deploy/oke/k8s/base/ +├── 00-namespace.yaml +├── 01-configmap.yaml +├── 02-secret-template.yaml +├── 03-agent-template-backend.yaml +├── 04-agent-gateway.yaml +├── 05-channel-gateway.yaml +├── 06-mcp-gateway.yaml +├── 07-frontend.yaml +└── kustomization.yaml +``` + +This implementation demonstrates how the components can be materialized in Kubernetes. + +Production projects must continue to follow the rules defined by the general architectural specification. + +--- + +## 22. Important note about demonstration OKE settings + +The reference configuration currently present in the repository uses values such as: + +```text +SESSION_REPOSITORY_PROVIDER=sqlite +MEMORY_REPOSITORY_PROVIDER=sqlite +CHECKPOINT_REPOSITORY_PROVIDER=sqlite +USAGE_REPOSITORY_PROVIDER=sqlite +VECTOR_STORE_PROVIDER=sqlite +GRAPH_STORE_PROVIDER=sqlite +``` + +These values are useful for demonstrations and controlled environments. + +**They must not be interpreted as the recommended architecture for multi-replica production environments.** + +In production, the general architectural specification takes precedence: shared state that must survive across Pods should use appropriate external providers. + +--- + +## 23. Base OKE deployment for `agent_template_backend` + +The following example represents the template backend specifically. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + replicas: 3 + + selector: + matchLabels: + app: agent-template-backend + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: agent-template-backend + + spec: + containers: + - name: agent-template-backend + image: .ocir.io//agents/agent-template-backend: + + ports: + - name: http + containerPort: 8000 + + envFrom: + - configMapRef: + name: agent-template-backend-config + - secretRef: + name: agent-template-backend-secrets + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 20 + failureThreshold: 3 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + +--- +apiVersion: v1 +kind: Service +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + type: ClusterIP + + selector: + app: agent-template-backend + + ports: + - name: http + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: agent-template-backend + + minReplicas: 3 + maxReplicas: 10 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +``` + +Sizing must be adjusted using load test results for the actual project. + +--- + +## 24. Template-specific base ConfigMap + +Example: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: agent-template-backend-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_PARALLEL_GUARDRAILS: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + FRAMEWORK_CHANNEL_INPUT_MODE: "embedded" +``` + +Persistence, LLM, memory, cache, observability, and RAG providers should be configured according to the production environment and the general architectural specification. + +--- + +## 25. Complete reference OKE topology + +A complete installation may use: + +```text + +----------------------+ + | Agent Frontend | + +----------+-----------+ + | + v + +----------------------+ + | Channel Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Backend | + | :8000 | + +----------+-----------+ + | + v + +----------------------+ + | MCP Gateway | + | :8300 | + +----------+-----------+ + | + +-------------+-------------+ + | | + v v + +------------------+ +------------------+ + | Telecom MCP | | Retail MCP | + | :8100 | | :8200 | + +------------------+ +------------------+ +``` + +This is a reference topology, not a mandatory topology for every project. + +--- + +## 26. MCP Servers on OKE + +MCP Servers should be deployed as independent components when used in production. + +Conceptual example: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + replicas: 2 + selector: + matchLabels: + app: telecom-mcp + template: + metadata: + labels: + app: telecom-mcp + spec: + containers: + - name: telecom-mcp + image: .ocir.io//mcp/telecom: + ports: + - containerPort: 8100 + +--- +apiVersion: v1 +kind: Service +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + selector: + app: telecom-mcp + ports: + - port: 8100 + targetPort: 8100 +``` + +The same approach may be used for other MCP Servers. + +--- + +## 27. Health, readiness, and startup + +The backend should maintain: + +```text +GET /health +``` + +as the minimum probe endpoint. + +Recommended: + +- `startupProbe` for initialization; +- `readinessProbe` for traffic admission; +- `livenessProbe` to detect an unhealthy process. + +An application should not rely only on `livenessProbe`. + +--- + +## 28. State and scalability + +When using: + +```yaml +replicas: 3 +``` + +there is no guarantee that consecutive requests will be handled by the same Pod. + +Therefore, in production: + +```text +session +checkpoint +memory +idempotency +transaction state +shared cache +``` + +must be assessed for externalization. + +A SQLite database stored under: + +```text +/data +``` + +inside an `emptyDir` volume is local to a Pod and is not shared storage. + +--- + +## 29. Local versus container MCP configuration + +The template separates: + +```text +config/mcp_servers.yaml +``` + +from: + +```text +config/mcp_servers.docker.yaml +``` + +This is intentional. + +Local: + +```yaml +endpoint: http://localhost:8100/mcp +``` + +Docker/Kubernetes: + +```yaml +endpoint: http://telecom-mcp:8100/mcp +``` + +The endpoint must reflect the DNS visible from the backend process. + +--- + +## 30. `.env.example` + +The file: + +```text +templates/agent_template_backend/.env.example +``` + +is a configuration reference. + +It must not be used as a place to store real credentials in source control. + +In production: + +- non-sensitive configuration may come from ConfigMaps; +- secrets should come from Kubernetes Secrets, OCI Vault, or an equivalent enterprise solution; +- credentials must not be committed. + +--- + +## 31. Using the template for new projects + +Recommended flow: + +```text +agent_framework_oci + | + +--> libs/agent_framework + | + +--> templates/agent_template_backend + | + v + new agent project +``` + +Steps: + +1. select the `agent_framework` version; +2. derive `agent_template_backend`; +3. rename the project; +4. adjust `agents.yaml`; +5. adjust `routing.yaml`; +6. adjust tools and tool policies; +7. create or modify workflows; +8. configure identity; +9. configure MCP; +10. configure guardrails and judges; +11. configure persistence and memory; +12. test locally; +13. run regression tests; +14. build the image; +15. deploy to staging; +16. promote to production according to the enterprise pipeline. + +--- + +## 32. The template must not create a framework fork + +Generic and reusable changes should be evaluated for implementation under: + +```text +libs/agent_framework +``` + +Domain-specific changes should remain in the derived project. + +Example: + +```text +"new billing-specific rule" + -> agent project + +"new generic pause/resume mechanism" + -> agent_framework +``` + +This principle reduces divergence between projects. + +--- + +## 33. Version compatibility + +A derived project should record: + +```text +AGENT_APPLICATION_VERSION +AGENT_FRAMEWORK_VERSION +GIT_COMMIT +BUILD_ID +``` + +Example: + +```text +agent-backoffice: 3.2.0 +agent-framework: 1.8.0 +``` + +A framework upgrade should trigger the project's regression suite. + +--- + +## 34. Role of `Tuning-Performance` + +The repository contains scenarios and variants under: + +```text +Tuning-Performance/ +``` + +These materials are references for features, tuning scenarios, regression, and implementation examples. + +They do not change the basic architectural relationship: + +```text +agent project -> agent_framework +``` + +A production project should incorporate only the configurations or implementations required and validated for its use case. + +--- + +## 35. Role of the template's internal documentation + +The directory: + +```text +templates/agent_template_backend/docs/ +``` + +contains detailed documentation for template-specific functionality, including topics such as: + +- memory; +- observability; +- guardrails; +- IC/NOC/GRL; +- transactions; +- routing; +- channel input; +- output supervisor. + +This document does not replace those functional manuals. + +It defines the **usage, distribution, and deployment model for the template**. + +--- + +## 36. Mandatory requirements for correct usage + +1. The template must consume `agent_framework` through Python imports. + +2. The framework must remain under: + +```text +agent_framework_oci/libs/agent_framework +``` + +as its source location in the monorepo. + +3. The template must remain under: + +```text +agent_framework_oci/templates/agent_template_backend +``` + +as its source location in the monorepo. + +4. In an independent distribution, both should be materialized at the same level: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +5. A derived production project must follow the general architectural specification. + +6. The framework must not be duplicated inside the project. + +7. MCP Servers must not be confused with the backend. + +8. The MCP Gateway is not part of the template directory. + +9. The frontend is not part of the template directory. + +10. SQLite settings present in examples must not be treated as the production default for multi-Pod environments. + +11. The backend must expose a health check. + +12. Real secrets must not be stored in `.env.example` or committed manifests. + +13. The framework version used by the project must be traceable. + +14. Generic changes should preferably be implemented in the framework, avoiding project-specific framework forks. + +--- + +## 37. Final reference structure + +### Inside the `agent_framework_oci` product + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +│ ├── agent_gateway/ +│ ├── channel_gateway/ +│ ├── mcp_gateway/ +│ └── agent_frontend/ +│ +├── mcp/ +│ └── servers/ +│ +└── deploy/ + └── oke/ +``` + +### Derived production project + +```text +workspace/ +├── agent_framework/ +│ +└── agent_backoffice/ + ├── app/ + ├── config/ + ├── workflows/ + ├── data/ + ├── scripts/ + ├── requirements.txt + ├── Dockerfile + └── .env.example +``` + +--- + +## 38. Final rule + +`agent_template_backend` is a **reference implementation of the standard**, not the framework itself. + +The correct relationship is: + +```text +agent_framework + ^ + | +agent_template_backend + | + v +derived projects +``` + +For development inside the monorepo, the original `libs/` and `templates/` paths may be used. + +For distribution and production, derived projects must follow the **General `agent_framework_oci` Usage Specification**, keeping the agent project and `agent_framework` as separate, independently versioned components. diff --git a/agent_framework_oci/deploy/docs/pt/agent_framework_oci_usage_spec_pt.md b/agent_framework_oci/deploy/docs/pt/agent_framework_oci_usage_spec_pt.md new file mode 100644 index 00000000..303b146e --- /dev/null +++ b/agent_framework_oci/deploy/docs/pt/agent_framework_oci_usage_spec_pt.md @@ -0,0 +1,1134 @@ +# Especificação de Utilização do `agent_framework_oci` + +## 1. Objetivo + +Esta especificação define o padrão para desenvolvimento, empacotamento e deployment de projetos de agentes baseados no `agent_framework_oci`. + +O objetivo principal é estabelecer uma separação clara entre: + +- o **framework corporativo compartilhado**, responsável pelas capacidades genéricas da plataforma; +- os **projetos de agentes**, responsáveis pelas regras, configurações, workflows e implementações específicas de cada domínio. + +O `agent_framework` não deve ser incorporado ou duplicado dentro de cada projeto de agente. + +A biblioteca deve ser disponibilizada separadamente e utilizada pelos projetos por meio de imports Python. + +--- + +## 2. Origem do framework + +No repositório oficial `agent_framework_oci`, a implementação compartilhada encontra-se em: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +Essa pasta corresponde ao pacote Python distribuível do framework. + +Sua estrutura contém, entre outros componentes: + +```text +agent_framework/ +├── pyproject.toml +├── src/ +│ └── agent_framework/ +│ ├── analytics/ +│ ├── cache/ +│ ├── channels/ +│ ├── guardrails/ +│ ├── memory/ +│ ├── observability/ +│ ├── routing/ +│ ├── supervisor/ +│ ├── workflows/ +│ └── ... +``` + +O `pyproject.toml` define o pacote: + +```toml +[project] +name = "agent-framework" +version = "0.1.0" +``` + +e utiliza: + +```toml +[tool.setuptools.packages.find] +where = ["src"] +``` + +Portanto, a pasta: + +```text +agent_framework_oci/libs/agent_framework +``` + +deve ser tratada como uma biblioteca Python independente e reutilizável. + +--- + +## 3. Estrutura obrigatória para projetos de agentes + +Para utilização do framework fora do repositório principal, a pasta: + +```text +agent_framework_oci/libs/agent_framework +``` + +deve ser disponibilizada juntamente com o projeto de agente. + +O projeto de agente e o framework devem ficar **no mesmo nível de diretório**. + +Exemplo: + +```text +workspace/ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +└── backoffice/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +Para outro agente: + +```text +workspace/ +├── agent_framework/ +│ +└── agent_contas/ + ├── app/ + ├── config/ + ├── data/ + ├── requirements.txt + ├── Dockerfile + └── .env +``` + +A regra arquitetural é: + +```text +/ +├── agent_framework/ +└── / +``` + +e não: + +```text +/ +└── agent_framework/ +``` + +O projeto do agente não deve possuir uma cópia privada do framework. + +--- + +## 4. Responsabilidades + +### 4.1 `agent_framework` + +O `agent_framework` contém capacidades genéricas e reutilizáveis da plataforma, como: + +- routing; +- supervisor; +- multi-agent; +- workflows; +- estados conversacionais; +- coleta de parâmetros; +- pause/resume; +- pending actions; +- multi-intent; +- intent shift; +- route stickiness; +- transações determinísticas; +- guardrails; +- judges; +- output supervisor; +- RAG; +- memória; +- cache; +- observabilidade; +- telemetria; +- integração MCP; +- persistência; +- analytics; +- integração com provedores OCI; +- mecanismos de resiliência; +- idempotência; +- tratamento de erros. + +Esses componentes pertencem ao framework e não devem ser copiados para os projetos consumidores. + +### 4.2 Projeto do agente + +Cada projeto de agente contém somente a implementação e configuração específicas do domínio. + +Exemplo: + +```text +backoffice/ +├── app/ +│ ├── agents/ +│ ├── workflows/ +│ ├── observability/ +│ ├── workflow_actions/ +│ ├── presentation/ +│ ├── state.py +│ └── main.py +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── prompt_policy.yaml +│ ├── identity.yaml +│ └── mcp_parameter_mapping.yaml +│ +├── data/ +├── requirements.txt +├── Dockerfile +└── .env +``` + +O projeto pode utilizar qualquer funcionalidade pública do framework por meio de imports. + +Exemplo: + +```python +from agent_framework.routing import ... +``` + +ou: + +```python +from agent_framework.supervisor import ... +``` + +ou: + +```python +from agent_framework.observability import ... +``` + +O código do projeto não deve depender de caminhos internos do repositório `agent_framework_oci`. + +--- + +## 5. Instalação local para desenvolvimento + +Considerando: + +```text +workspace/ +├── agent_framework/ +└── backoffice/ +``` + +a instalação recomendada é: + +```bash +cd workspace + +python -m venv .venv + +source .venv/bin/activate +``` + +Em Windows: + +```powershell +.venv\Scripts\activate +``` + +Instalar o framework: + +```bash +pip install -e ./agent_framework +``` + +Instalar as dependências do projeto: + +```bash +pip install -r ./backoffice/requirements.txt +``` + +O modo: + +```bash +pip install -e +``` + +é recomendado para desenvolvimento porque mantém o código do framework referenciado diretamente pelo ambiente Python. + +Alterações no framework tornam-se imediatamente disponíveis para o projeto sem reinstalação completa do pacote. + +--- + +## 6. Validação obrigatória + +Após instalar o framework, o seguinte comando deve executar sem erro: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +Também deve ser possível testar imports utilizados pelo projeto: + +```bash +python -c "from agent_framework import *" +``` + +ou imports específicos conforme a implementação do agente. + +Uma falha como: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indica que o framework não foi disponibilizado ou instalado corretamente. + +--- + +## 7. Modelo para CI/CD + +Em um pipeline de CI/CD, deve ser criado um workspace contendo os dois componentes. + +Exemplo: + +```text +build/ +├── agent_framework/ +└── backoffice/ +``` + +O framework pode ser obtido diretamente do repositório `agent_framework_oci`: + +```text +agent_framework_oci/libs/agent_framework +``` + +e copiado para: + +```text +build/agent_framework +``` + +O projeto específico do agente deve ser copiado para: + +```text +build/backoffice +``` + +O build da imagem Docker deve utilizar: + +```text +build/ +``` + +como contexto. + +Exemplo: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +Esse detalhe é importante. + +O contexto deve ser: + +```text +. +``` + +representando o diretório que contém simultaneamente: + +```text +agent_framework/ +backoffice/ +``` + +--- + +## 8. Dockerfile padrão + +Um projeto de agente deve adotar como base um Dockerfile semelhante ao seguinte: + +```dockerfile +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PIP_NO_CACHE_DIR=1 + +WORKDIR /workspace + +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* + +# Framework compartilhado +COPY agent_framework /workspace/agent_framework + +# Projeto específico do agente +COPY backoffice /workspace/backoffice + +# Instala primeiro o framework corporativo +RUN pip install --upgrade pip \ + && pip install /workspace/agent_framework + +# Instala as dependências específicas do agente +RUN pip install -r /workspace/backoffice/requirements.txt + +WORKDIR /workspace/backoffice + +EXPOSE 8000 + +CMD [ + "uvicorn", + "app.main:app", + "--host", + "0.0.0.0", + "--port", + "8000" +] +``` + +Para ambiente de desenvolvimento, opcionalmente pode ser utilizado: + +```dockerfile +RUN pip install -e /workspace/agent_framework +``` + +Para imagens imutáveis de produção, recomenda-se: + +```dockerfile +RUN pip install /workspace/agent_framework +``` + +Dessa forma a versão do framework utilizada pela imagem fica congelada no momento do build. + +--- + +## 9. Estrutura dentro do container + +O container deve manter conceitualmente a mesma separação utilizada no desenvolvimento: + +```text +/workspace/ +├── agent_framework/ +└── backoffice/ +``` + +A aplicação será executada a partir de: + +```text +/workspace/backoffice +``` + +O framework estará instalado no ambiente Python e poderá ser importado normalmente: + +```python +import agent_framework +``` + +Não deve ser necessário utilizar: + +```python +sys.path.append(...) +``` + +nem alterar dinamicamente o `PYTHONPATH` no código da aplicação. + +--- + +## 10. Versionamento do framework + +A versão do framework utilizada pelo agente deve ser determinada durante o build. + +É recomendado registrar, no mínimo: + +```text +AGENT_FRAMEWORK_VERSION +AGENT_APPLICATION_VERSION +BUILD_ID +GIT_COMMIT +``` + +Exemplo: + +```yaml +env: + - name: AGENT_FRAMEWORK_VERSION + value: "1.5.0" + + - name: AGENT_APPLICATION_VERSION + value: "2.1.4" +``` + +Isso permite identificar exatamente qual combinação: + +```text +Framework + Agente +``` + +está executando em cada ambiente. + +--- + +## 11. Publicação da imagem + +Após o build: + +```bash +docker build \ + -f backoffice/Dockerfile \ + -t backoffice:1.0.0 \ + . +``` + +a imagem deve ser publicada no OCI Container Registry — OCIR. + +Exemplo: + +```bash +docker tag \ + backoffice:1.0.0 \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +Depois: + +```bash +docker push \ + gru.ocir.io//agents/backoffice:1.0.0 +``` + +--- + +## 12. Deployment no OCI OKE + +Cada projeto de agente deve possuir seu próprio Kubernetes `Deployment`. + +O framework não precisa ser executado como Pod independente. + +O `agent_framework` é uma biblioteca e estará incorporado à imagem do projeto do agente durante o build. + +A arquitetura é: + +```text + OCI OKE + | + v + Kubernetes Service + | + +------------+------------+ + | | | + v v v + Agent Pod Agent Pod Agent Pod + | | | + +------------+------------+ + | + agent_framework + instalado em cada imagem +``` + +O código do framework é compartilhado em termos de origem e versionamento, mas estará instalado dentro de cada imagem de agente. + +--- + +## 13. Manifesto YAML base para OKE + +O manifesto abaixo pode ser utilizado como base para deployment de um projeto como `backoffice`. + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: agent-platform + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: backoffice-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENT_FRAMEWORK_VERSION: "1.0.0" + + AGENT_ID: "backoffice" + AGENT_APPLICATION_VERSION: "1.0.0" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + ENABLE_LANGFUSE: "true" + ENABLE_OTEL: "true" + ENABLE_ANALYTICS: "true" + +--- +apiVersion: v1 +kind: Secret +metadata: + name: backoffice-secrets + namespace: agent-platform +type: Opaque +stringData: + OCI_COMPARTMENT_ID: "" + OCI_GENAI_API_KEY: "" + LANGFUSE_PUBLIC_KEY: "" + LANGFUSE_SECRET_KEY: "" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backoffice + namespace: agent-platform + labels: + app: backoffice +spec: + replicas: 3 + + selector: + matchLabels: + app: backoffice + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: backoffice + + spec: + containers: + - name: backoffice + image: gru.ocir.io//agents/backoffice:1.0.0 + imagePullPolicy: IfNotPresent + + ports: + - name: http + containerPort: 8000 + protocol: TCP + + envFrom: + - configMapRef: + name: backoffice-config + - secretRef: + name: backoffice-secrets + + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 20 + timeoutSeconds: 5 + failureThreshold: 3 + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + + securityContext: + allowPrivilegeEscalation: false + +--- +apiVersion: v1 +kind: Service +metadata: + name: backoffice + namespace: agent-platform +spec: + type: ClusterIP + selector: + app: backoffice + ports: + - name: http + protocol: TCP + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: backoffice + namespace: agent-platform +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: backoffice + + minReplicas: 3 + maxReplicas: 10 + + behavior: + scaleUp: + stabilizationWindowSeconds: 0 + policies: + - type: Percent + value: 100 + periodSeconds: 60 + - type: Pods + value: 4 + periodSeconds: 60 + selectPolicy: Max + + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Percent + value: 25 + periodSeconds: 60 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 75 + +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: backoffice + namespace: agent-platform +spec: + minAvailable: 2 + selector: + matchLabels: + app: backoffice +``` + +--- + +## 14. Aplicação do manifesto + +O manifesto pode ser armazenado como: + +```text +deploy/oke/backoffice.yaml +``` + +e aplicado com: + +```bash +kubectl apply -f deploy/oke/backoffice.yaml +``` + +Validação: + +```bash +kubectl -n agent-platform get pods +kubectl -n agent-platform get deployment +kubectl -n agent-platform get svc +kubectl -n agent-platform get hpa +``` + +Para acompanhar o rollout: + +```bash +kubectl -n agent-platform rollout status deployment/backoffice +``` + +--- + +## 15. Health check + +Todo projeto de agente destinado ao OKE deve disponibilizar: + +```http +GET /health +``` + +com retorno: + +```text +200 OK +``` + +enquanto estiver apto a receber novas requisições. + +Esse endpoint é utilizado pelas probes: + +```text +startupProbe +readinessProbe +livenessProbe +``` + +--- + +## 16. Persistência e múltiplas réplicas + +Aplicações em OKE devem considerar que múltiplos Pods podem executar simultaneamente. + +Portanto, dados conversacionais e transacionais que precisem sobreviver entre requisições não devem depender exclusivamente do filesystem do container. + +Não é recomendado utilizar SQLite local como repositório definitivo para ambientes OKE com múltiplas réplicas. + +Para produção devem ser utilizados providers externos adequados para: + +- sessions; +- checkpoints; +- memória; +- cache; +- idempotência; +- locks; +- analytics; +- estado transacional. + +Dependendo da configuração do framework, podem ser utilizados, por exemplo: + +```text +Oracle Autonomous Database +MongoDB +Redis +OCI Streaming +OCI Object Storage +``` + +--- + +## 17. Atualização do framework + +Um projeto de agente não deve receber alterações no framework copiando arquivos individualmente para seu código. + +A atualização deve acontecer substituindo a versão disponibilizada de: + +```text +agent_framework/ +``` + +e reconstruindo a imagem. + +Processo: + +```text +Nova versão agent_framework + | + v +Atualização do workspace CI/CD + | + v +Build da imagem do agente + | + v +Testes + | + v +Push OCIR + | + v +Rolling Update OKE +``` + +Esse modelo mantém clara a separação entre: + +```text +produto/framework +``` + +e: + +```text +implementação do agente +``` + +--- + +## 18. Regra de compatibilidade + +Cada release de projeto deverá declarar explicitamente a versão do framework contra a qual foi testada. + +Exemplo: + +```text +Backoffice 3.2.0 +Agent Framework 1.8.0 +``` + +Uma nova versão do framework não deve ser promovida diretamente para produção sem que sejam executados os testes de regressão do projeto consumidor. + +Pipeline recomendado: + +```text +Framework + | + v +Build Agent + | + v +Unit Tests + | + v +Integration Tests + | + v +Workflow Regression + | + v +Smoke Test + | + v +Homologação + | + v +Produção +``` + +--- + +## 19. Vários projetos utilizando o mesmo framework + +A estrutura pode atender simultaneamente vários projetos: + +```text +workspace/ +├── agent_framework/ +├── backoffice/ +├── contas/ +├── ofertas/ +├── atendimento/ +└── suporte/ +``` + +Todos utilizam: + +```text +agent_framework/ +``` + +como origem comum da plataforma. + +Cada aplicação, entretanto, gera sua própria imagem: + +```text +backoffice:3.0.0 +contas:5.2.0 +ofertas:1.4.0 +atendimento:2.6.0 +``` + +Todas podem ter sido construídas com: + +```text +agent-framework:1.8.0 +``` + +Isso permite evoluir projetos de forma independente sem realizar forks do framework. + +--- + +## 20. Princípio arquitetural + +A relação entre os componentes deve permanecer: + +```text + agent_framework + | + +---------------+---------------+ + | | | + v v v + Backoffice Contas Ofertas + | | | + v v v + imagem OCI imagem OCI imagem OCI + | | | + +---------------+---------------+ + | + v + OKE +``` + +O `agent_framework` é a plataforma tecnológica comum. + +Os projetos de agentes são consumidores dessa plataforma. + +Consequentemente: + +**o projeto do agente depende do framework; o framework não deve depender do projeto do agente.** + +--- + +## 21. Requisitos obrigatórios + +Para que um projeto seja considerado compatível com o `agent_framework_oci`, devem ser atendidos os seguintes requisitos: + +1. `agent_framework` deve ser obtido de: + +```text +agent_framework_oci/libs/agent_framework +``` + +2. O framework deve ser disponibilizado separadamente do projeto do agente. + +3. A estrutura de distribuição deve ser: + +```text +/ +├── agent_framework/ +└── / +``` + +4. O framework não deve ser duplicado dentro do projeto. + +5. A biblioteca deve ser instalada pelo mecanismo padrão do Python: + +```bash +pip install ./agent_framework +``` + +ou, em desenvolvimento: + +```bash +pip install -e ./agent_framework +``` + +6. Imports devem utilizar: + +```python +import agent_framework +``` + +e nunca caminhos físicos do repositório. + +7. O contexto Docker deve permitir acesso simultâneo a: + +```text +agent_framework/ +``` + +e: + +```text +/ +``` + +8. Cada imagem deve conter uma versão conhecida do framework. + +9. O deployment OKE deve possuir probes de saúde. + +10. Aplicações escaláveis devem externalizar estado compartilhado. + +11. Secrets não devem ser mantidos no código nem no manifesto versionado. + +12. Cada projeto deve possuir Deployment e Service próprios. + +13. A escala horizontal deve acontecer no projeto do agente, e não através de uma instância central executável do `agent_framework`. + +--- + +## 22. Estrutura recomendada final + +A estrutura de referência para desenvolvimento e CI/CD é: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ ├── pyproject.toml +│ └── src/ +│ └── agent_framework/ +│ +├── backoffice/ +│ ├── app/ +│ ├── config/ +│ ├── data/ +│ ├── requirements.txt +│ ├── Dockerfile +│ └── .env.example +│ +└── deploy/ + └── oke/ + └── backoffice.yaml +``` + +Para novos projetos, somente a pasta específica muda: + +```text +agent-runtime/ +│ +├── agent_framework/ +│ +├── novo_agente/ +│ +└── deploy/ + └── oke/ + └── novo_agente.yaml +``` + +Esse deve ser o padrão de distribuição e deployment dos projetos construídos sobre o `agent_framework_oci`. diff --git a/agent_framework_oci/deploy/docs/pt/agent_template_backend_especificacao_pt.md b/agent_framework_oci/deploy/docs/pt/agent_template_backend_especificacao_pt.md new file mode 100644 index 00000000..652dec68 --- /dev/null +++ b/agent_framework_oci/deploy/docs/pt/agent_template_backend_especificacao_pt.md @@ -0,0 +1,1364 @@ +# Especificação de Uso do `agent_template_backend` + +## Documento complementar à arquitetura geral do `agent_framework_oci` + +> **Escopo deste documento** +> +> Este documento é específico para o `agent_template_backend` distribuído com o `agent_framework_oci`. +> +> Ele **não substitui** a especificação arquitetural geral de utilização do `agent_framework_oci` para projetos de produção. A especificação geral continua sendo a referência normativa para estrutura de projetos produtivos, empacotamento, versionamento, CI/CD, externalização de estado, segurança, escalabilidade e deployment em OKE. +> +> Este documento explica como o `agent_template_backend` existente no repositório deve ser utilizado como **template de referência**, ambiente de desenvolvimento, base para novos agentes e origem para projetos produtivos derivados. + +--- + +## 1. Objetivo + +O `agent_template_backend` é o template de backend de agentes fornecido pelo repositório `agent_framework_oci`. + +Sua finalidade é oferecer uma implementação inicial funcional que demonstre como um projeto de agente deve consumir o `agent_framework`, organizar configurações, expor APIs, utilizar workflows, integrar MCP, aplicar guardrails e judges e utilizar os demais serviços corporativos do framework. + +O template deve ser entendido como: + +- referência de implementação; +- ponto de partida para novos projetos; +- ambiente de desenvolvimento e validação; +- exemplo de integração com o `agent_framework`; +- exemplo de deployment; +- base para geração de projetos independentes. + +O template **não deve ser interpretado como uma exceção** às regras arquiteturais definidas na especificação geral de produção. + +--- + +## 2. Relação com a especificação arquitetural geral + +Existem dois níveis de documentação. + +### 2.1. Especificação arquitetural geral + +A especificação geral define o padrão para projetos produtivos derivados do framework: + +```text +/ +├── agent_framework/ +└── / +``` + +Ela define, entre outros pontos: + +- separação entre framework e projeto; +- instalação do `agent_framework`; +- versionamento; +- contexto de build; +- Docker; +- CI/CD; +- deployment em OKE; +- escalabilidade; +- externalização de estado; +- gerenciamento de secrets; +- compatibilidade entre versão do framework e versão do agente. + +### 2.2. Esta especificação + +Este documento descreve como o template entregue no repositório se encaixa nesse modelo. + +Quando existir diferença entre uma configuração demonstrativa do template e uma regra da especificação arquitetural geral, **a especificação arquitetural geral prevalece para ambientes de produção**. + +--- + +## 3. Localização do template no repositório + +No repositório fonte, o template está localizado em: + +```text +agent_framework_oci/ +└── templates/ + └── agent_template_backend/ +``` + +O framework compartilhado está localizado em: + +```text +agent_framework_oci/ +└── libs/ + └── agent_framework/ +``` + +Portanto, dentro do repositório de desenvolvimento, a estrutura é: + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +├── mcp/ +├── deploy/ +└── ... +``` + +Essa organização é a **estrutura fonte do produto `agent_framework_oci`**. + +Ela não altera a regra da especificação arquitetural de produção. + +--- + +## 4. Estrutura de distribuição do template + +Quando o `agent_template_backend` for extraído do repositório para uso independente, build isolado ou criação de um projeto derivado, o workspace deve materializar: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +O conteúdo de: + +```text +agent_framework_oci/libs/agent_framework/ +``` + +deve ser disponibilizado como: + +```text +workspace/agent_framework/ +``` + +e o conteúdo de: + +```text +agent_framework_oci/templates/agent_template_backend/ +``` + +como: + +```text +workspace/agent_template_backend/ +``` + +Essa estrutura é compatível com o Dockerfile existente no template. + +--- + +## 5. Estrutura atual do `agent_template_backend` + +A estrutura principal do template é: + +```text +agent_template_backend/ +├── app/ +│ ├── main.py +│ ├── state.py +│ ├── mcp_gateway_client_factory.py +│ └── ... +│ +├── config/ +│ ├── agents.yaml +│ ├── routing.yaml +│ ├── tools.yaml +│ ├── tool_policies.yaml +│ ├── guardrails.yaml +│ ├── judges.yaml +│ ├── identity.yaml +│ ├── prompt_policy.yaml +│ ├── mcp_parameter_mapping.yaml +│ ├── mcp_servers.yaml +│ └── mcp_servers.docker.yaml +│ +├── workflows/ +│ ├── devolucao_pedido.active.yaml +│ └── devolucao_pedido.v1.yaml +│ +├── data/ +├── docs/ +├── scripts/ +├── llm_profiles.yaml +├── requirements.txt +├── Dockerfile +├── .env.example +├── README.md +└── README_ENTERPRISE_TEMPLATE.md +``` + +O template contém configuração suficiente para demonstrar as capacidades corporativas do framework sem que essas capacidades precisem ser copiadas para o projeto. + +--- + +## 6. Dependência do `agent_framework` + +O `agent_template_backend` é um consumidor do framework. + +A dependência deve permanecer unidirecional: + +```text +agent_template_backend + | + v +agent_framework +``` + +O inverso não deve ocorrer. + +O `agent_framework` não deve depender de: + +```text +templates/agent_template_backend +``` + +nem de arquivos específicos do template. + +--- + +## 7. Instalação local no repositório fonte + +Para desenvolvimento diretamente dentro do repositório: + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +o framework pode ser instalado em modo editável. + +Exemplo, a partir de `templates/agent_template_backend`: + +```bash +pip install -e ../../libs/agent_framework +pip install -r requirements.txt +``` + +Ou, a partir do root: + +```bash +pip install -e ./libs/agent_framework +pip install -r ./templates/agent_template_backend/requirements.txt +``` + +O modo editável é adequado para desenvolvimento do produto, pois alterações realizadas em `libs/agent_framework` são imediatamente utilizadas pelo template. + +--- + +## 8. Instalação em workspace independente + +Quando o template for disponibilizado segundo a estrutura arquitetural geral: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +utilize: + +```bash +pip install -e ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +Para um build de produção: + +```bash +pip install ./agent_framework +pip install -r ./agent_template_backend/requirements.txt +``` + +--- + +## 9. Validação de instalação + +A instalação deve permitir: + +```bash +python -c "import agent_framework; print(agent_framework.__file__)" +``` + +e, a partir do projeto: + +```bash +python -c "import app.main" +``` + +Uma falha: + +```text +ModuleNotFoundError: No module named 'agent_framework' +``` + +indica erro na disponibilização ou instalação da biblioteca. + +--- + +## 10. Execução do backend + +O backend do template expõe a aplicação FastAPI em: + +```text +8000 +``` + +Execução local: + +```bash +uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +``` + +Health check: + +```bash +curl http://localhost:8000/health +``` + +Outras verificações úteis: + +```bash +curl http://localhost:8000/agents +curl http://localhost:8000/debug/mcp/tools +``` + +A porta `8000` pertence ao backend do agente e não deve ser confundida com as portas dos MCP Servers ou gateways. + +--- + +## 11. Configuração do agente + +Os comportamentos específicos do template são definidos principalmente por arquivos externos ao framework. + +### `config/agents.yaml` + +Define os agentes disponíveis e seus perfis. + +### `config/routing.yaml` + +Define intents, rotas e comportamento de roteamento. + +### `config/tools.yaml` + +Define as tools conhecidas pelo projeto e seus vínculos com MCP. + +### `config/tool_policies.yaml` + +Define políticas de execução, confirmação, controle e requisitos transacionais das tools. + +### `config/guardrails.yaml` + +Configura guardrails aplicados pelo projeto. + +### `config/judges.yaml` + +Configura judges. + +### `config/identity.yaml` + +Define identidade e business keys. + +### `config/mcp_parameter_mapping.yaml` + +Define mapeamento e extração de parâmetros destinados às tools MCP. + +### `config/mcp_servers.yaml` + +Define MCP Servers utilizados em execução local. + +### `config/mcp_servers.docker.yaml` + +Define endpoints adequados ao ambiente Docker. + +### `llm_profiles.yaml` + +Define perfis e providers LLM utilizados pelo projeto. + +Esse modelo preserva a separação entre: + +```text +motor genérico = agent_framework +``` + +e: + +```text +regras do domínio = agent_template_backend/config + app +``` + +--- + +## 12. MCP Servers de exemplo + +O repositório inclui MCP Servers de desenvolvimento em: + +```text +agent_framework_oci/mcp/servers/ +├── telecom_mcp_server/ +├── retail_mcp_server/ +└── mock_telecom_mcp/ +``` + +Os exemplos principais utilizam: + +```text +Telecom MCP : 8100 +Retail MCP : 8200 +``` + +A configuração local do template referencia: + +```yaml +servers: + telecom: + endpoint: http://localhost:8100/mcp + + retail: + endpoint: http://localhost:8200/mcp +``` + +Esses MCP Servers são componentes separados do backend do agente. + +--- + +## 13. Inicialização dos MCP Servers no ambiente de desenvolvimento + +O repositório fornece: + +```text +scripts/run_mcp_servers.sh +``` + +que inicia os MCP Servers de exemplo. + +Arquitetura local: + +```text + +------------------+ + | Telecom MCP | + | :8100 | + +---------+--------+ + | + | ++----------------------+ | +| agent_template |---------+ +| backend :8000 | ++----------------------+---------+ + | + | + +---------v--------+ + | Retail MCP | + | :8200 | + +------------------+ +``` + +Esse script é uma conveniência de desenvolvimento. + +Em produção, a implantação dos MCP Servers deve respeitar a arquitetura produtiva do projeto e não depender de processos iniciados por shell dentro do Pod do backend. + +--- + +## 14. MCP Gateway + +O `agent_framework_oci` também contém: + +```text +apps/mcp_gateway/ +``` + +e configuração OKE para o MCP Gateway. + +A porta padrão apresentada no repositório é: + +```text +8300 +``` + +O template possui suporte a: + +```env +MCP_GATEWAY_ENABLED=true +MCP_GATEWAY_URL=http://localhost:8300 +``` + +ou, em OKE: + +```text +http://mcp-gateway.agent-platform.svc.cluster.local:8300 +``` + +O MCP Gateway é um componente da plataforma e não faz parte fisicamente da pasta `agent_template_backend`. + +--- + +## 15. Agent Gateway, Channel Gateway e Frontend + +O repositório também contém: + +```text +apps/ +├── agent_gateway/ +├── channel_gateway/ +├── mcp_gateway/ +└── agent_frontend/ +``` + +Esses componentes devem ser entendidos como componentes de plataforma ou canais adicionais. + +O `agent_template_backend` pode operar diretamente na porta `8000` sem que todos esses componentes sejam obrigatórios para um teste local simples. + +Em uma topologia corporativa completa, o fluxo pode ser: + +```text +Client / Frontend + | + v +Channel Gateway + | + v +Agent Gateway + | + v +agent_template_backend + | + v +MCP Gateway + | + v +MCP Servers +``` + +A composição concreta deve seguir os requisitos do ambiente de destino. + +--- + +## 16. Frontend de desenvolvimento + +O frontend de referência está em: + +```text +agent_framework_oci/apps/agent_frontend/ +``` + +e aponta, por padrão, para: + +```text +http://localhost:8000 +``` + +O frontend não deve ser movido para dentro do `agent_template_backend` apenas para satisfazer uma estrutura de projeto. + +Ele é um componente separado. + +Um projeto produtivo poderá: + +- utilizar esse frontend; +- utilizar outro frontend; +- integrar Web/WhatsApp/Voice; +- utilizar apenas APIs; +- utilizar Agent Gateway/Channel Gateway. + +--- + +## 17. Dockerfile do template + +O Dockerfile existente em: + +```text +templates/agent_template_backend/Dockerfile +``` + +pressupõe um contexto que contenha simultaneamente: + +```text +agent_framework/ +agent_template_backend/ +``` + +Estrutura: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +Build: + +```bash +docker build \ + -t agent-template-backend:local \ + -f agent_template_backend/Dockerfile \ + . +``` + +Essa forma está alinhada à especificação arquitetural geral. + +--- + +## 18. Dockerfile OKE existente no repositório + +O repositório também fornece: + +```text +deploy/oke/dockerfiles/Dockerfile.agent-template-backend +``` + +Esse Dockerfile é executado com o root de `agent_framework_oci` como contexto e copia: + +```text +libs/agent_framework -> /opt/agent_framework +templates/agent_template_backend -> /app +``` + +Portanto existem dois contextos válidos, para objetivos diferentes. + +### Desenvolvimento/distribuição independente + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +### Build a partir do monorepo + +```text +agent_framework_oci/ +├── libs/agent_framework/ +└── templates/agent_template_backend/ +``` + +Ambos materializam a mesma dependência lógica: + +```text +agent_template_backend -> agent_framework +``` + +--- + +## 19. Derivação de um projeto produtivo + +O `agent_template_backend` deve servir como origem para um novo projeto. + +Exemplo: + +```text +agent_template_backend + | + | derivação + v +agent_backoffice +``` + +Após a derivação, a estrutura produtiva recomendada é: + +```text +workspace/ +├── agent_framework/ +└── agent_backoffice/ +``` + +Não é necessário manter o nome: + +```text +agent_template_backend +``` + +em produção. + +O projeto derivado passa a ter: + +- ciclo de versão próprio; +- Docker image própria; +- configuração própria; +- agentes próprios; +- workflows próprios; +- políticas próprias; +- pipeline próprio. + +A biblioteca `agent_framework` permanece compartilhada. + +--- + +## 20. O que deve e o que não deve ser copiado + +### Deve ser derivado para o projeto + +Conforme a necessidade do domínio: + +```text +app/ +config/ +workflows/ +data/ +scripts/ +requirements.txt +Dockerfile +.env.example +``` + +### Não deve ser copiado para dentro do projeto + +```text +libs/agent_framework +``` + +O framework deve continuar sendo distribuído e instalado separadamente. + +Também não é necessário copiar automaticamente: + +```text +apps/agent_gateway +apps/channel_gateway +apps/mcp_gateway +apps/agent_frontend +mcp/servers +``` + +Esses componentes devem ser incluídos na solução apenas quando fizerem parte da topologia necessária. + +--- + +## 21. OKE: relação com a arquitetura geral + +O repositório contém uma implementação OKE de referência em: + +```text +deploy/oke/ +``` + +Incluindo: + +```text +deploy/oke/k8s/base/ +├── 00-namespace.yaml +├── 01-configmap.yaml +├── 02-secret-template.yaml +├── 03-agent-template-backend.yaml +├── 04-agent-gateway.yaml +├── 05-channel-gateway.yaml +├── 06-mcp-gateway.yaml +├── 07-frontend.yaml +└── kustomization.yaml +``` + +Essa implementação demonstra como os componentes podem ser materializados no Kubernetes. + +Para projetos produtivos, devem ser mantidas as regras da especificação arquitetural geral. + +--- + +## 22. Atenção às configurações demonstrativas do OKE + +A configuração de referência presente no repositório utiliza, entre outros valores: + +```text +SESSION_REPOSITORY_PROVIDER=sqlite +MEMORY_REPOSITORY_PROVIDER=sqlite +CHECKPOINT_REPOSITORY_PROVIDER=sqlite +USAGE_REPOSITORY_PROVIDER=sqlite +VECTOR_STORE_PROVIDER=sqlite +GRAPH_STORE_PROVIDER=sqlite +``` + +Esses valores são úteis para demonstração e ambientes controlados. + +**Eles não devem ser interpretados como recomendação arquitetural para produção com múltiplas réplicas.** + +Em produção, a especificação arquitetural geral prevalece: estados compartilhados que precisem sobreviver entre Pods devem utilizar providers externos apropriados. + +--- + +## 23. Deployment base do `agent_template_backend` no OKE + +O exemplo abaixo representa especificamente o backend do template. + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + replicas: 3 + + selector: + matchLabels: + app: agent-template-backend + + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + + template: + metadata: + labels: + app: agent-template-backend + + spec: + containers: + - name: agent-template-backend + image: .ocir.io//agents/agent-template-backend: + + ports: + - name: http + containerPort: 8000 + + envFrom: + - configMapRef: + name: agent-template-backend-config + - secretRef: + name: agent-template-backend-secrets + + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + failureThreshold: 24 + + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 10 + failureThreshold: 6 + + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 20 + failureThreshold: 3 + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2000m" + memory: "4Gi" + +--- +apiVersion: v1 +kind: Service +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + type: ClusterIP + + selector: + app: agent-template-backend + + ports: + - name: http + port: 8000 + targetPort: http + +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: agent-template-backend + namespace: agent-platform +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: agent-template-backend + + minReplicas: 3 + maxReplicas: 10 + + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +``` + +O dimensionamento deve ser ajustado com base nos testes de carga do projeto real. + +--- + +## 24. ConfigMap base específico do template + +Exemplo: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: agent-template-backend-config + namespace: agent-platform +data: + APP_ENV: "oke" + LOG_LEVEL: "INFO" + + AGENTS_CONFIG_PATH: "./config/agents.yaml" + ROUTING_CONFIG_PATH: "./config/routing.yaml" + GUARDRAILS_CONFIG_PATH: "./config/guardrails.yaml" + JUDGES_CONFIG_PATH: "./config/judges.yaml" + PROMPT_POLICY_PATH: "./config/prompt_policy.yaml" + IDENTITY_CONFIG_PATH: "./config/identity.yaml" + MCP_PARAMETER_MAPPING_PATH: "./config/mcp_parameter_mapping.yaml" + + ENABLE_INPUT_GUARDRAILS: "true" + ENABLE_OUTPUT_GUARDRAILS: "true" + ENABLE_JUDGES: "true" + ENABLE_SUPERVISOR: "true" + ENABLE_OUTPUT_SUPERVISOR: "true" + ENABLE_PARALLEL_GUARDRAILS: "true" + ENABLE_MCP_TOOLS: "true" + + MCP_GATEWAY_ENABLED: "true" + MCP_GATEWAY_URL: "http://mcp-gateway.agent-platform.svc.cluster.local:8300" + + FRAMEWORK_CHANNEL_INPUT_MODE: "embedded" +``` + +Providers de persistência, LLM, memória, cache, observabilidade e RAG devem ser configurados de acordo com o ambiente de produção e com a especificação arquitetural geral. + +--- + +## 25. Topologia OKE completa de referência + +Uma instalação completa pode utilizar: + +```text + +----------------------+ + | Agent Frontend | + +----------+-----------+ + | + v + +----------------------+ + | Channel Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Gateway | + +----------+-----------+ + | + v + +----------------------+ + | Agent Backend | + | :8000 | + +----------+-----------+ + | + v + +----------------------+ + | MCP Gateway | + | :8300 | + +----------+-----------+ + | + +-------------+-------------+ + | | + v v + +------------------+ +------------------+ + | Telecom MCP | | Retail MCP | + | :8100 | | :8200 | + +------------------+ +------------------+ +``` + +Essa é uma topologia de referência, não uma obrigação para todos os projetos. + +--- + +## 26. MCP Servers em OKE + +Os MCP Servers devem ser implantados como componentes próprios quando utilizados em produção. + +Exemplo conceitual: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + replicas: 2 + selector: + matchLabels: + app: telecom-mcp + template: + metadata: + labels: + app: telecom-mcp + spec: + containers: + - name: telecom-mcp + image: .ocir.io//mcp/telecom: + ports: + - containerPort: 8100 + +--- +apiVersion: v1 +kind: Service +metadata: + name: telecom-mcp + namespace: agent-platform +spec: + selector: + app: telecom-mcp + ports: + - port: 8100 + targetPort: 8100 +``` + +A mesma abordagem pode ser utilizada para outros MCP Servers. + +--- + +## 27. Health, readiness e startup + +O backend deve manter: + +```text +GET /health +``` + +como endpoint mínimo para probes. + +Recomenda-se utilizar: + +- `startupProbe` para inicialização; +- `readinessProbe` para admissão de tráfego; +- `livenessProbe` para detectar processo não saudável. + +Uma aplicação não deve depender apenas de `livenessProbe`. + +--- + +## 28. Estado e escalabilidade + +Ao utilizar: + +```yaml +replicas: 3 +``` + +não existe garantia de que requisições consecutivas sejam atendidas pelo mesmo Pod. + +Portanto, em produção: + +```text +session +checkpoint +memory +idempotency +transaction state +cache compartilhado +``` + +devem ser analisados quanto à necessidade de externalização. + +Um banco SQLite armazenado em: + +```text +/data +``` + +dentro de `emptyDir` é local ao Pod e não representa armazenamento compartilhado. + +--- + +## 29. Configuração de MCP local versus container + +O template separa: + +```text +config/mcp_servers.yaml +``` + +e: + +```text +config/mcp_servers.docker.yaml +``` + +Isso é intencional. + +Local: + +```yaml +endpoint: http://localhost:8100/mcp +``` + +Docker/Kubernetes: + +```yaml +endpoint: http://telecom-mcp:8100/mcp +``` + +O endpoint deve refletir o DNS visível a partir do processo do backend. + +--- + +## 30. `.env.example` + +O arquivo: + +```text +templates/agent_template_backend/.env.example +``` + +é uma referência de configuração. + +Ele não deve ser utilizado como mecanismo para armazenar credenciais reais no repositório. + +Em produção: + +- configurações não sensíveis podem vir de ConfigMap; +- secrets devem vir de Kubernetes Secret, OCI Vault ou solução corporativa equivalente; +- credenciais não devem ser commitadas. + +--- + +## 31. Uso do template em novos projetos + +Fluxo recomendado: + +```text +agent_framework_oci + | + +--> libs/agent_framework + | + +--> templates/agent_template_backend + | + v + novo projeto de agente +``` + +Passos: + +1. selecionar a versão do `agent_framework`; +2. derivar o `agent_template_backend`; +3. renomear o projeto; +4. ajustar `agents.yaml`; +5. ajustar `routing.yaml`; +6. ajustar tools e tool policies; +7. criar/ajustar workflows; +8. configurar identidade; +9. configurar MCP; +10. configurar guardrails e judges; +11. configurar persistência e memória; +12. testar localmente; +13. executar regressão; +14. gerar imagem; +15. implantar em ambiente de homologação; +16. promover para produção conforme pipeline corporativo. + +--- + +## 32. O template não deve criar fork do framework + +Alterações genéricas e reutilizáveis devem ser avaliadas para implementação em: + +```text +libs/agent_framework +``` + +Alterações específicas de domínio devem permanecer no projeto derivado. + +Exemplo: + +```text +"nova regra específica de cobrança" + -> projeto do agente + +"novo mecanismo genérico de pause/resume" + -> agent_framework +``` + +Esse princípio reduz divergência entre projetos. + +--- + +## 33. Compatibilidade de versões + +Um projeto derivado deve registrar: + +```text +AGENT_APPLICATION_VERSION +AGENT_FRAMEWORK_VERSION +GIT_COMMIT +BUILD_ID +``` + +Exemplo: + +```text +agent-backoffice: 3.2.0 +agent-framework: 1.8.0 +``` + +A atualização do framework deve acionar os testes de regressão do projeto. + +--- + +## 34. Papel da pasta `Tuning-Performance` + +O repositório possui cenários e variantes em: + +```text +Tuning-Performance/ +``` + +Esses materiais servem como referência para features, cenários de tuning, regressão e exemplos de implementação. + +Eles não alteram a estrutura arquitetural básica: + +```text +agent project -> agent_framework +``` + +Um projeto produtivo deve incorporar apenas as configurações ou implementações necessárias e validadas para seu caso. + +--- + +## 35. Papel da documentação interna do template + +A pasta: + +```text +templates/agent_template_backend/docs/ +``` + +contém documentação detalhada sobre funcionalidades específicas do template, incluindo temas como: + +- memória; +- observabilidade; +- guardrails; +- IC/NOC/GRL; +- transações; +- routing; +- channel input; +- output supervisor. + +Este documento não substitui esses manuais funcionais. + +Ele define o **modelo de utilização, distribuição e deployment do template**. + +--- + +## 36. Requisitos obrigatórios para uso correto + +1. O template deve consumir `agent_framework` por import Python. + +2. O framework deve continuar em: + +```text +agent_framework_oci/libs/agent_framework +``` + +como origem no monorepo. + +3. O template deve continuar em: + +```text +agent_framework_oci/templates/agent_template_backend +``` + +como origem no monorepo. + +4. Em uma distribuição independente, ambos devem ser materializados no mesmo nível: + +```text +workspace/ +├── agent_framework/ +└── agent_template_backend/ +``` + +5. Um projeto produtivo derivado deve respeitar a especificação arquitetural geral. + +6. O framework não deve ser duplicado dentro do projeto. + +7. MCP Servers não devem ser confundidos com o backend. + +8. O MCP Gateway não faz parte da pasta do template. + +9. O frontend não faz parte da pasta do template. + +10. Configurações SQLite existentes nos exemplos não devem ser consideradas padrão produtivo para ambientes multi-Pod. + +11. O backend deve expor health check. + +12. Secrets reais não devem ser armazenados em `.env.example` ou manifestos versionados. + +13. A versão do framework utilizada pelo projeto deve ser rastreável. + +14. Mudanças genéricas devem preferencialmente ser implementadas no framework, evitando forks por projeto. + +--- + +## 37. Estrutura de referência final + +### Dentro do produto `agent_framework_oci` + +```text +agent_framework_oci/ +├── libs/ +│ └── agent_framework/ +│ +├── templates/ +│ └── agent_template_backend/ +│ +├── apps/ +│ ├── agent_gateway/ +│ ├── channel_gateway/ +│ ├── mcp_gateway/ +│ └── agent_frontend/ +│ +├── mcp/ +│ └── servers/ +│ +└── deploy/ + └── oke/ +``` + +### Projeto derivado para produção + +```text +workspace/ +├── agent_framework/ +│ +└── agent_backoffice/ + ├── app/ + ├── config/ + ├── workflows/ + ├── data/ + ├── scripts/ + ├── requirements.txt + ├── Dockerfile + └── .env.example +``` + +--- + +## 38. Regra final + +O `agent_template_backend` é uma **implementação de referência do padrão**, não o próprio framework. + +A relação correta é: + +```text +agent_framework + ^ + | +agent_template_backend + | + v +projetos derivados +``` + +Para desenvolvimento dentro do monorepo, os caminhos originais em `libs/` e `templates/` podem ser utilizados. + +Para distribuição e produção, os projetos derivados devem seguir a **Especificação Geral de Utilização do `agent_framework_oci`**, mantendo o projeto de agente e o `agent_framework` como componentes separados e versionáveis. + diff --git a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py new file mode 100644 index 00000000..613167e8 --- /dev/null +++ b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/dlex_sanitizer.py @@ -0,0 +1,201 @@ +"""Deterministic, framework-owned sanitization for DLEX_OUT. + +The DLEX classifier decides whether a candidate response contains information +that must not be exposed. When the finding is recoverable, this module masks +only the sensitive values so the response can be revalidated and preserved. + +Rules are intentionally generic and live in the framework, never in domain +agents. Deployments may extend the built-ins without changing agent code via: + +* ``GUARDRAIL_DLEX_OUT_EXTRA_MASKS_JSON`` (JSON list of rule objects), or +* the DLEX_OUT rail policy key ``sanitize_patterns`` in guardrails.yaml. + +Each extension rule accepts ``name``, ``pattern`` and ``replacement``. Patterns +are regular expressions. Raw matched values are never returned in findings. +""" +from __future__ import annotations + +from dataclasses import dataclass +import json +import os +import re +from typing import Any, Iterable + + +@dataclass(frozen=True, slots=True) +class DlexMaskRule: + name: str + pattern: str + replacement: str + flags: int = re.IGNORECASE + + +# Conservative defaults: values that are broadly unsafe to echo verbatim and +# can be redacted without changing the semantic meaning of an answer. +_BUILTIN_RULES: tuple[DlexMaskRule, ...] = ( + DlexMaskRule( + "payment_barcode", + # Brazilian boleto/payment representations normally contain 44, 47 or + # 48 digits and may be formatted with dots/spaces. Keep the match on the + # numeric token only so surrounding prose is preserved. + r"(? DlexMaskRule | None: + if not isinstance(raw, dict): + return None + name = str(raw.get("name") or "custom").strip() + pattern = str(raw.get("pattern") or "").strip() + replacement = str(raw.get("replacement") or "[DADO_PROTEGIDO]") + if not pattern: + return None + try: + re.compile(pattern) + except re.error: + return None + return DlexMaskRule(name=name, pattern=pattern, replacement=replacement, flags=0) + + +def _extra_rules_from_env() -> list[DlexMaskRule]: + raw = os.getenv("GUARDRAIL_DLEX_OUT_EXTRA_MASKS_JSON", "").strip() + if not raw: + return [] + try: + data = json.loads(raw) + except Exception: + return [] + if isinstance(data, dict): + data = [data] + return [rule for item in (data or []) if (rule := _parse_rule(item)) is not None] + + +def _extra_rules_from_policy(policy: dict[str, Any] | None) -> list[DlexMaskRule]: + data = dict(policy or {}).get("sanitize_patterns") or [] + if isinstance(data, dict): + data = [data] + return [rule for item in data if (rule := _parse_rule(item)) is not None] + + +def _mask_payment_sequences(text: str) -> tuple[str, int]: + """Mask 44/47/48-digit payment tokens without consuming surrounding prose. + + Regex alone is awkward because formatted boletos have variable punctuation. + We scan numeric/punctuation runs, count digits, and only redact the run when + it has a canonical payment digit count. + """ + token_re = re.compile(r"(? str: + nonlocal count + token = match.group(0) + digits = re.sub(r"\D", "", token) + if len(digits) in {44, 47, 48}: + count += 1 + return "[CODIGO_DE_PAGAMENTO_OCULTADO]" + return token + + return token_re.sub(repl, text), count + + +def sanitize_dlex_output( + text: str, + *, + policy: dict[str, Any] | None = None, + exclude_values: Iterable[str] | None = None, +) -> tuple[str, list[dict[str, Any]]]: + """Return ``(sanitized_text, findings)`` without exposing raw matches. + + ``exclude_values`` contains explicitly authorized values (for example a + workflow-generated protocol). They are temporarily protected so generic + masking never alters them. + """ + original = text or "" + sanitized = original + findings: list[dict[str, Any]] = [] + + protected: dict[str, str] = {} + for index, value in enumerate(exclude_values or []): + value = str(value or "").strip() + if not value or value not in sanitized: + continue + marker = f"__DLEX_AUTHORIZED_{index}__" + sanitized = sanitized.replace(value, marker) + protected[marker] = value + + # Payment sequences use digit-count validation rather than a plain rule. + sanitized, payment_count = _mask_payment_sequences(sanitized) + if payment_count: + findings.append({"type": "payment_barcode", "action": "redact", "count": payment_count}) + + rules = [r for r in _BUILTIN_RULES if r.name != "payment_barcode"] + rules.extend(_extra_rules_from_env()) + rules.extend(_extra_rules_from_policy(policy)) + + for rule in rules: + try: + regex = re.compile(rule.pattern, rule.flags) + except re.error: + continue + sanitized, count = regex.subn(rule.replacement, sanitized) + if count: + findings.append({"type": rule.name, "action": "redact", "count": count}) + + for marker, value in protected.items(): + sanitized = sanitized.replace(marker, value) + + return sanitized, findings + + +__all__ = ["DlexMaskRule", "sanitize_dlex_output"] diff --git a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py index 18b3fe9c..8ae0a4ec 100644 --- a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py +++ b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/guardrails/rails.py @@ -25,6 +25,7 @@ from .calibrated.output_sanitization import mascarar_pii_output, sanitizar_toxic from .calibrated.rules.pinj_patterns import _PINJ_PATTERNS, is_obvious_injection from .calibrated.rules.tox_blocklist import _EXPLICIT_TERMS, _THREAT_PATTERNS, is_obvious_toxic from .framework_llm_client import classify_with_framework_llm +from .dlex_sanitizer import sanitize_dlex_output from agent_framework.workflows.input_contract import has_meaningful_unmatched_policy, has_semantic_classifier @@ -323,10 +324,28 @@ class CoherenceRail(Guardrail): "data": out, }, ) - out = await classify_with_framework_llm( - _llm(ctx), "COER", {"text": text or "", "context": ctx}, - profile_name="guardrail", component_name="guardrail.coer", generation_name="guardrail.coer", - ) + try: + out = await classify_with_framework_llm( + _llm(ctx), "COER", {"text": text or "", "context": ctx}, + profile_name="guardrail", component_name="guardrail.coer", generation_name="guardrail.coer", + ) + except Exception as exc: + # COER is a conversational-coherence rail, not a security boundary. + # If its semantic classifier/provider is unavailable, the graph must + # continue and let the normal router/workflow clarification logic own + # the turn instead of surfacing an infrastructure exception. + return RailDecision( + code=self.code, + allowed=True, + reason="Classificador de coerência indisponível; continuidade delegada ao runtime", + sanitized_text=text, + metadata={ + "mechanism": "infrastructure_fail_open", + "calibrated": True, + "error_type": type(exc).__name__, + "error": str(exc)[:500], + }, + ) return RailDecision( code=self.code, allowed=bool(out.get("allowed", True)), reason=str(out.get("reason") or out.get("label") or "COER avaliado"), @@ -344,6 +363,9 @@ class LoopRail(Guardrail): "COLLECTING_PARAMETERS", "AWAITING_CONFIRMATION", "EXECUTING", "PAUSED", "WAITING_INPUT", } + # A queued multi-intent transaction may be promoted immediately after + # the previous one completed. Prefer the newest live MCP evidence over + # a stale top-level COMPLETED status left by checkpoint merge order. for result in reversed(list(ctx.get("mcp_results") or [])): if isinstance(result, dict): result_status = str(result.get("transaction_status") or "").strip().upper() @@ -359,6 +381,11 @@ class LoopRail(Guardrail): async def evaluate(self, text: str, context: dict[str, Any]) -> RailDecision: ctx = _ctx(context) transaction_status = self._transaction_status(ctx) + + # Parameter values and short confirmations may legitimately repeat in + # the same session. VLOOP must not consume input owned by the active + # transaction contract; extraction/confirmation remains responsible + # for deciding whether the value is valid. if transaction_status in {"COLLECTING_PARAMETERS", "AWAITING_CONFIRMATION"}: return RailDecision( code=self.code, @@ -373,6 +400,7 @@ class LoopRail(Guardrail): "calibrated": True, }, ) + normalized = _lower(text).strip() history = [_lower(h).strip() for h in ctx.get("history_texts", [])[-6:]] repeated = history.count(normalized) >= 2 if normalized else False @@ -805,11 +833,64 @@ class DataLeakageOutputRail(Guardrail): } protocol_authorization_verified = True + # DLEX_OUT is permissive for recoverable leakage: when classification + # rejects the candidate, mask only generic sensitive values and re-run + # the same classifier. The agent/domain remains unaware of this detail. + # If sanitization cannot change the candidate, or the sanitized version + # is still unsafe, the original blocking behavior is preserved. + dlex_sanitized_text = None + dlex_findings: list[dict[str, Any]] = [] + sanitization_recheck = None + original_dlex_result = dict(out) + if not bool(out.get("allowed", True)): + policy = dict(getattr(self, "_guardrail_policy", {}) or {}) + candidate_sanitized, dlex_findings = sanitize_dlex_output( + original_text, + policy=policy, + exclude_values=matched_expected_protocols, + ) + if candidate_sanitized != original_text: + sanitized_classifier_text = candidate_sanitized + sanitized_classifier_ctx: dict[str, Any] = ctx + if matched_expected_protocols: + sanitized_classifier_text = _mask_authorized_protocol_values( + sanitized_classifier_text, matched_expected_protocols + ) + sanitized_classifier_ctx = _mask_authorized_protocol_values( + ctx, matched_expected_protocols + ) + sanitization_recheck = await classify_with_framework_llm( + _llm(ctx), + "DLEX_OUT", + {"text": sanitized_classifier_text, "context": sanitized_classifier_ctx}, + profile_name="grl", + component_name="guardrail.dlex_out.sanitization_recheck", + generation_name="guardrail.dlex_out.sanitization_recheck", + ) + if bool(sanitization_recheck.get("allowed", True)): + dlex_sanitized_text = candidate_sanitized + out = { + "allowed": True, + "label": "SANITIZED", + "reason": "DLEX_OUT sanitizado e revalidado com segurança", + "original_reason": original_dlex_result.get("reason"), + } + metadata = { "mechanism": "llm_rail", "data": out, "calibrated": True, } + if dlex_findings: + metadata.update( + { + "action": "sanitize" if dlex_sanitized_text is not None else "block", + "sanitized": dlex_sanitized_text is not None, + "findings": dlex_findings, + "original_decision": original_dlex_result, + "sanitization_recheck": sanitization_recheck, + } + ) if matched_expected_protocols: metadata.update( { @@ -823,7 +904,7 @@ class DataLeakageOutputRail(Guardrail): code=self.code, allowed=bool(out.get("allowed", True)), reason=str(out.get("reason") or out.get("label") or "DLEX_OUT avaliado"), - sanitized_text=text, + sanitized_text=dlex_sanitized_text, metadata=metadata, ) diff --git a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc index 96d1c56cb74b9ea0d2f467930552a0b72de179b5..8dbf921583f5c8d58a29450ab541cde440422065 100644 GIT binary patch delta 14251 zcmbVy3wTqZP$yZ$Ic1u@h~ew;-K;6x!0TvTKCkBu zE|f`;$n%Z7kx+mZYS3Z{Zz9-&78-sjZ>AiyP}{^?K31#i&*lsGLKotj`Jx1ug_e4# zvxbwE+%1&`ei>g(*&2B zl_{!Jtrv{NuL_iGN_IEQufCOJSPCR-JViu(F9_q;0(%s&r$u=6Afd!#Y2FVMDu(IH zT`&epaU=8|JI|Ium8XPX)x3>i)*|oYfGupPT6|i zR*P06z@8V1kpmwdEzEM*NxhlWD{Q=t-{2Afg_%&`*q&m!r*z2sH;U`xH{L2v3F17q zg<;r(-#BYZbfLry!re{mCpuB)DH~i(J5ffIw5(5Cj*iQwO|b4!Ic2n4Wu#mAWp-v$ zXNIJXw4}nnB&h%|+HXRXhX#1Sn5`va=54>#29{4Zd4dNuugGD?7tb0KUHp_L<(~4v zM3Zu&Ix5F6?s@C4oBp|l8M98~L7F zr6vcLC|^?f7rZwy1*!MSJjp4T;j7foEJ;7hm}~zZqhwC(6xR%sWGMG3mL-0dU{X$3 zhZK`?YBTDKS><$fpmtqQJ;SDDVADRB8i}8W;kWQiL-IousPa?|n!(yt-79hKgahvg zW~<3u4OH`611_li9I3>LiE^mQ&l;elYssQ!s!n-n%0s=KblBG6z;DfV?^24aPsvTE zgW!HeWNq9C65CBRo|-`xXlp>^HV|1ocnGlS?jGFUrO|DNPZSeal;Pluy0;MV;&fV1 zC<_ZbE`A$b2FQn?4?58gB{mOm?CvELxjXpJG$DS#CH5@p-k#*qPioLk{QhtY3n~qe zV%&T{juzwhdy4U~4l{mZ(t+y8_Z;Th@<+ddwB(BpDXlTpd+HYYe+MYOc+gE?cRk^h zTTj0`%jwiuoN#` z)pS!Dn($8enw{c!_SW{!o=*4Lfw*Gjrmn8lD-(#UeW6@75e$wE1ia(EH)zw=GzB5WFS)#J`Pf>8SovL5WGVZKsewV-#c+IZYAXvDe@to0LuvD6jR

ZRE*(>;V`47b9z+0FI?e|ZN#+`V@8a?h3$#T;A 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 delta 7969 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 diff --git a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py index cdabbd9d..f6ec8a73 100644 --- a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py +++ b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/routing/enterprise_router.py @@ -630,47 +630,144 @@ class EnterpriseRouter: if state_decision: tx_status = str(state.get("transaction_status") or "").strip().upper() - # Explicit abandonment must be evaluated before confirmation - # consumption. Otherwise an utterance such as - # "não quero cancelar; quero apenas consultar meus serviços" can be - # trapped by the confirmation state and never reach ABANDON. A plain - # yes/no still keeps deterministic confirmation precedence because it - # does not pass the abandonment gate. - if tx_status == "AWAITING_CONFIRMATION": - if self._looks_like_explicit_abandonment(str(text)): + active_tx = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} + legacy_tx = state.get("pending_tool_call") or state.get("selected_tool_call") or {} + state_lock_name = str( + state_decision.next_state + or current_state + or state_decision.intent.removeprefix("state:") + or "" + ).strip().upper() + transactional_state_lock = state_lock_name.startswith( + ("WAITING_", "COLLECTING_", "AWAITING_") + ) + + # Rich transactional latches remain the primary source of truth. + # The domain state lock is only a recovery fallback for checkpoints + # where those latches were not restored. This prevents a generic + # COLLECTING_* state from stealing a legitimate parameter answer. + rich_transaction_context = bool( + active_tx.get("tool_name") + or (isinstance(legacy_tx, dict) and legacy_tx.get("tool_name")) + or tx_status in {"COLLECTING_PARAMETERS", "AWAITING_CONFIRMATION"} + ) + fallback_state_lock_context = bool( + transactional_state_lock and not rich_transaction_context + ) + has_transaction_context = bool( + rich_transaction_context or fallback_state_lock_context + ) + + # Some hosts restore the domain state but omit transaction_status. + # Infer only the generic phase needed by the precedence logic; do not + # replace richer transaction data when it exists. + effective_tx_status = tx_status + if not effective_tx_status: + if state_lock_name.startswith("COLLECTING_"): + effective_tx_status = "COLLECTING_PARAMETERS" + elif state_lock_name.startswith("AWAITING_") or "CONFIRMATION" in state_lock_name: + effective_tx_status = "AWAITING_CONFIRMATION" + + precedence_state = state + if effective_tx_status and effective_tx_status != tx_status: + precedence_state = dict(state) + precedence_state["transaction_status"] = effective_tx_status + + # Transactional interruption precedence is semantic, not lexical. + # + # COLLECTING_PARAMETERS is special: the pending parameter gets the + # first opportunity to consume the turn. If the input is pertinent + # to the parameter, extraction returns values and the transaction + # continues. Only when the input is not pertinent do we ask the + # semantic interruption classifier whether the user abandoned the + # active transaction. This preserves inputs such as + # ``motivo é que desisti`` as a legitimate ``reason`` value while + # still allowing ``não quero mais devolver; quero ver meus serviços`` + # to abandon the current operation and start a new objective. + if effective_tx_status == "COLLECTING_PARAMETERS": + if rich_transaction_context: + relevance = await self._classify_transaction_parameter_relevance( + precedence_state, text=str(text) + ) + if relevance is True: + consumed = await self._transaction_parameter_precedence( + precedence_state, text=str(text), state_decision=state_decision + ) + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), + "transaction_parameter_relevant": True, + } + await self._emit(consumed, state) + return consumed + + # Pertinent to the requested parameter, but extraction did + # not produce a structured value. Keep transaction + # ownership and let the agent reprompt/clarify; do not + # reinterpret the same input as ABANDON or SHIFT. + state_decision.metadata = { + **(state_decision.metadata or {}), + "transaction_parameter_relevant": True, + "transaction_parameter_extraction_empty": True, + } + await self._emit(state_decision, state) + return state_decision + + if relevance is None: + # Compatibility/fail-safe when the relevance classifier is + # unavailable: retain the previous extractor-first behavior. + consumed = await self._transaction_parameter_precedence( + precedence_state, text=str(text), state_decision=state_decision + ) + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), + "transaction_parameter_relevance": "unknown", + } + await self._emit(consumed, state) + return consumed + + if has_transaction_context: abandonment = await self._transaction_state_interruption_candidate( - state, + precedence_state, text=str(text), state_decision=state_decision, abandon_only=True, ) if abandonment is not None: + abandonment.metadata = { + **(abandonment.metadata or {}), + "transaction_parameter_relevant": False if rich_transaction_context else None, + } await self._emit(abandonment, state) return abandonment - consumed = await self._transaction_parameter_precedence( - state, text=str(text), state_decision=state_decision + # In confirmation/waiting phases there is no pending free-form + # parameter whose value can be confused with abandonment. Evaluate + # ABANDON semantically before confirmation/state-lock consumption. + # Plain confirmations (e.g. yes/no) are classified as CONTINUE by + # this probe and then follow the normal confirmation path below. + elif has_transaction_context: + abandonment = await self._transaction_state_interruption_candidate( + precedence_state, + text=str(text), + state_decision=state_decision, + abandon_only=True, ) - if consumed is not None: - await self._emit(consumed, state) - return consumed + if abandonment is not None: + await self._emit(abandonment, state) + return abandonment - # Transaction parameter precedence is absolute while collecting: - # first let the active transaction try to consume the current turn. - # Only when NO pending parameter can be extracted do we ask the - # semantic classifier whether the user changed goals. This prevents - # value/name/reference answers (for example "a de 14,99") from being - # stolen by a semantically plausible but incompatible intent. - if tx_status == "COLLECTING_PARAMETERS": + if effective_tx_status == "AWAITING_CONFIRMATION": consumed = await self._transaction_parameter_precedence( - state, text=str(text), state_decision=state_decision + precedence_state, text=str(text), state_decision=state_decision ) if consumed is not None: await self._emit(consumed, state) return consumed interruption = await self._transaction_state_interruption_candidate( - state, text=str(text), state_decision=state_decision + precedence_state, text=str(text), state_decision=state_decision ) if interruption is not None: await self._emit(interruption, state) @@ -701,34 +798,61 @@ class EnterpriseRouter: method="state", next_state=tx_status, ) - if tx_status == "AWAITING_CONFIRMATION": - if self._looks_like_explicit_abandonment(str(text)): - abandonment = await self._transaction_state_interruption_candidate( - state, - text=str(text), - state_decision=synthetic, - abandon_only=True, - ) - if abandonment is not None: - abandonment.metadata = { - **(abandonment.metadata or {}), - "transaction_state_recovered": True, - } - await self._emit(abandonment, state) - return abandonment - - consumed = await self._transaction_parameter_precedence( - state, text=str(text), state_decision=synthetic - ) - if consumed is not None: - consumed.metadata = { - **(consumed.metadata or {}), - "transaction_state_recovered": True, - } - await self._emit(consumed, state) - return consumed - if tx_status == "COLLECTING_PARAMETERS": + relevance = await self._classify_transaction_parameter_relevance( + state, text=str(text) + ) + if relevance is True: + consumed = await self._transaction_parameter_precedence( + state, text=str(text), state_decision=synthetic + ) + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), + "transaction_state_recovered": True, + "transaction_parameter_relevant": True, + } + await self._emit(consumed, state) + return consumed + synthetic.metadata = { + **(synthetic.metadata or {}), + "transaction_state_recovered": True, + "transaction_parameter_relevant": True, + "transaction_parameter_extraction_empty": True, + } + await self._emit(synthetic, state) + return synthetic + + if relevance is None: + consumed = await self._transaction_parameter_precedence( + state, text=str(text), state_decision=synthetic + ) + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), + "transaction_state_recovered": True, + "transaction_parameter_relevance": "unknown", + } + await self._emit(consumed, state) + return consumed + + # No parameter was consumed (or this is confirmation): now decide + # semantically whether the user abandoned the active transaction. + abandonment = await self._transaction_state_interruption_candidate( + state, + text=str(text), + state_decision=synthetic, + abandon_only=True, + ) + if abandonment is not None: + abandonment.metadata = { + **(abandonment.metadata or {}), + "transaction_state_recovered": True, + } + await self._emit(abandonment, state) + return abandonment + + if tx_status == "AWAITING_CONFIRMATION": consumed = await self._transaction_parameter_precedence( state, text=str(text), state_decision=synthetic ) @@ -867,6 +991,82 @@ class EnterpriseRouter: return decision + async def _classify_transaction_parameter_relevance( + self, state: dict[str, Any], *, text: str + ) -> bool | None: + """Semantically decide whether the turn answers a pending parameter. + + This is intentionally separate from extraction. A parameter extractor + may be able to manufacture a syntactically valid value from unrelated + text; relevance answers the higher-level question first: does the user + appear to be responding to what the active transaction asked for? + + Returns True/False when classification succeeds and None when the + classifier is unavailable or its output cannot be trusted. + """ + if not (self.enable_llm_router and self.llm is not None): + return None + missing = [str(name) for name in (state.get("missing_parameters") or []) if str(name).strip()] + if not missing: + return None + active = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} + tool_name = str(active.get("tool_name") or "").strip() + if not tool_name: + return None + payload = { + "message": str(text or ""), + "transaction": { + "intent": active.get("started_from_intent") or state.get("intent"), + "tool_name": tool_name, + "tool_description": active.get("tool_description"), + "missing_parameters": missing, + "parameter_schema": active.get("parameter_schema") or {}, + "known_arguments": active.get("arguments") or {}, + "relevant_conversation_context": ( + active.get("parameter_conversational_context") + or self._collect_transaction_parameter_context(state=state, current_text=str(text)) + ), + }, + } + system = ( + "Você decide somente se a mensagem do usuário é uma resposta pertinente ao(s) " + "parâmetro(s) que a transação ativa está pedindo. Avalie significado e contexto, " + "não palavras isoladas. Considere RELEVANT quando a mensagem fornece, esclarece, " + "corrige ou referencia plausivelmente o dado solicitado, mesmo em linguagem livre. " + "Considere NOT_RELEVANT quando a mensagem abandona a ação, inicia outro objetivo, " + "faz uma pergunta diferente ou não responde ao dado pedido. Não extraia valores e " + "não decida abandono/intent; classifique apenas pertinência. Retorne somente JSON " + "válido: {\"relevance\":\"RELEVANT|NOT_RELEVANT\",\"confidence\":0.0,\"reason\":\"...\"}." + ) + try: + answer = await self.llm.ainvoke( + [ + {"role": "system", "content": system}, + {"role": "user", "content": json.dumps(payload, ensure_ascii=False)}, + ], + temperature=0.0, + max_tokens=256, + profile_name="router", + component_name="router.transaction_parameter_relevance", + generation_name="llm.transaction_parameter_relevance", + ) + data = self._parse_json(answer) + except Exception as exc: + logger.warning("Falha ao classificar pertinência do parâmetro transacional: %s", exc) + return None + relevance = str(data.get("relevance") or "").strip().upper() + try: + confidence = float(data.get("confidence") or 0.0) + except (TypeError, ValueError): + return None + if confidence < self.intent_shift_threshold: + return None + if relevance == "RELEVANT": + return True + if relevance == "NOT_RELEVANT": + return False + return None + async def _transaction_parameter_precedence( self, state: dict[str, Any], @@ -997,13 +1197,14 @@ class EnterpriseRouter: *, text: str, state_decision: RouteDecision, + abandon_only: bool = False, ) -> RouteDecision | None: """Detecta semanticamente mudança de intenção durante uma transação. - Não existe lista de palavras para desistência ou mudança de assunto. Uma - interrupção nasce de uma intent diferente resolvida por uma keyword - configurada no ``routing.yaml`` ou, na ausência dela, por uma decisão - semântica do LLM com o contexto da transação pendente. + Não existe lista de palavras para desistência. ABANDON é sempre uma + decisão semântica do LLM baseada no contexto transacional. Uma possível + nova intenção pode aparecer como hint do ``routing.yaml``, mas esse hint + não decide abandono e não substitui a classificação semântica. """ active_tx = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} started_intent = str(active_tx.get("started_from_intent") or "").strip() @@ -1017,7 +1218,17 @@ class EnterpriseRouter: or (started_intent and configured_candidate.intent != started_intent) or (previous_intent and not previous_intent.startswith("state:") and configured_candidate.intent != previous_intent) ) - if not different: + + # A configured candidate that resolves to the same intent/agent is not + # sufficient to conclude that the active transaction continues. The + # user may be starting a *new instance of the same intent* with a + # different transactional target (for example cancelling TIM Fashion + # while Tamboro is awaiting confirmation). In LLM mode keep the + # candidate only as a semantic hint and let the transaction classifier + # decide CONTINUE/REPLACE/ABANDON. Without semantic classification we + # retain the legacy state lock because guessing replacement from a + # keyword alone would be unsafe. + if not different and not (self.enable_llm_router and self.llm is not None): return None # During parameter collection, a configured keyword may be present in @@ -1058,6 +1269,11 @@ class EnterpriseRouter: "current_intent": started_intent or previous_intent, "transaction_status": state.get("transaction_status"), "tool_name": active_tx.get("tool_name"), + # Structured arguments are part of the identity of the active + # transaction. They let the semantic classifier distinguish a new + # instance of the same intent from a continuation without relying on + # domain keywords or hard-coded product names. + "active_arguments": dict(active_tx.get("arguments") or {}), "missing_parameters": list(state.get("missing_parameters") or []), "configured_candidate": ( { @@ -1069,18 +1285,36 @@ class EnterpriseRouter: else None ), } - system = ( - "Você decide apenas se o turno atual continua a transação ativa ou muda de intenção. " - "Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " - "A extração dos parâmetros pendentes já foi tentada antes desta etapa e não consumiu o turno. " - "Se ainda assim a mensagem for apenas uma resposta referencial/valor/nome ao dado pendente, retorne CONTINUE. " - "Se o usuário passou claramente a perseguir outro objetivo sem desistir explicitamente da ação atual, " - "retorne SHIFT e a nova intent permitida. " - "Se o usuário desistiu explicitamente da ação/transação atual, retorne ABANDON. " - "Quando ABANDON vier acompanhado de um novo objetivo, também informe intent e agent desse novo objetivo. " - "Quando for apenas abandono sem novo objetivo, intent e agent podem ser nulos. " - "Retorne somente JSON válido com decision, intent, agent, confidence, reason." - ) + if abandon_only: + system = ( + "Você decide somente se o usuário desistiu explicitamente da ação/transação ativa. " + "Quando a transação estava coletando parâmetros, a pertinência ao parâmetro já foi avaliada antes desta etapa; " + "portanto não transforme uma resposta pertinente ao parâmetro em abandono. " + "Use o significado completo da mensagem e o contexto transacional; não use palavras isoladas como regra. " + "Se houver desistência explícita da ação atual, retorne ABANDON. " + "Quando o abandono vier acompanhado de um novo objetivo, também informe intent e agent desse novo objetivo. " + "Quando for apenas abandono sem novo objetivo, intent e agent podem ser nulos. " + "Se a mensagem apenas fornece parâmetro, confirma contexto, ou somente muda/adiciona outro objetivo sem " + "desistir explicitamente da ação atual, retorne CONTINUE. " + "Retorne somente JSON válido com decision, intent, agent, confidence, reason." + ) + else: + system = ( + "Você decide apenas se o turno atual continua a transação ativa, substitui a instância transacional ativa, " + "ou muda de intenção. Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " + "A extração dos parâmetros pendentes já foi tentada antes desta etapa e não consumiu o turno. " + "Se ainda assim a mensagem for apenas uma resposta referencial/valor/nome ao dado pendente, retorne CONTINUE. " + "Se o usuário continua com a MESMA intent, mas claramente inicia outra instância da operação com alvo/argumentos diferentes, " + "retorne REPLACE e informe a mesma intent e agent. REPLACE significa substituir a transação ativa; não significa estado novo. " + "Não use REPLACE quando o usuário apenas complementa/adiciona itens à mesma operação (por exemplo 'também', 'além desses'), " + "nem quando apenas reformula ou confirma o mesmo alvo: nesses casos retorne CONTINUE. " + "Se o usuário passou claramente a perseguir outro objetivo/intent sem desistir explicitamente da ação atual, " + "retorne SHIFT e a nova intent permitida. " + "Se o usuário desistiu explicitamente da ação/transação atual, retorne ABANDON. " + "Quando ABANDON vier acompanhado de um novo objetivo, também informe intent e agent desse novo objetivo. " + "Quando for apenas abandono sem novo objetivo, intent e agent podem ser nulos. " + "Retorne somente JSON válido com decision, intent, agent, confidence, reason." + ) user = { "message": text, "transaction": transaction_context, @@ -1105,7 +1339,10 @@ class EnterpriseRouter: return None decision_kind = str(data.get("decision") or "").strip().upper() - if decision_kind not in {"SHIFT", "ABANDON"}: + if abandon_only: + if decision_kind != "ABANDON": + return None + elif decision_kind not in {"SHIFT", "REPLACE", "ABANDON"}: return None confidence = float(data.get("confidence") or 0.0) if confidence < self.intent_shift_threshold: @@ -1156,7 +1393,42 @@ class EnterpriseRouter: mcp_tools=mcp_tools, ) - if not intent_name or intent_name == (started_intent or previous_intent): + current_intent = started_intent or previous_intent + if not intent_name: + return None + + # Same-intent replacement is a transaction lifecycle event, not a new + # conversation state. It cancels only the active transaction instance + # and re-enters normal routing for the same configured intent. + if decision_kind == "REPLACE": + if not current_intent or intent_name != current_intent: + return None + agent = agent or str(self._agent_for_intent(intent_name) or "").strip() + if not agent: + return None + return RouteDecision( + route=agent, + agent=agent, + intent=intent_name, + confidence=confidence, + reason=str(data.get("reason") or "Nova instância transacional da mesma intenção."), + method="llm", + metadata={ + "transaction_interruption": "same_intent_replacement", + "interrupted_state": state_decision.next_state, + "interrupted_agent": state_decision.agent, + "interrupted_intent": current_intent, + "interruption_source": "semantic_classifier", + "configured_routing_hint": ( + configured_candidate.intent if configured_candidate is not None else None + ), + "raw_llm_answer": answer[:1000], + }, + domain=self._domain_for_intent(intent_name), + mcp_tools=self._tools_for_intent(intent_name), + ) + + if intent_name == current_intent: return None agent = agent or str(self._agent_for_intent(intent_name) or "").strip() if not agent: diff --git a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py index 3fa365af..dbae2498 100644 --- a/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py +++ b/agent_framework_oci/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py @@ -3113,21 +3113,33 @@ class AgentRuntimeMixin: self._normalize_transaction_lifecycle(state) # Uma transação em coleta/confirmação não pode aprisionar a sessão. O - # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT ou - # ABANDON. Não existe interpretação lexical de desistência no runtime; o - # runtime apenas aplica a decisão do router e limpa os latches da ação. + # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT, + # ABANDON ou replacement da instância transacional na mesma intent. Não + # existe interpretação lexical de desistência no runtime; o runtime apenas + # aplica a decisão do router e limpa os latches da ação. active_before_interruption = self._active_transaction(state) interruption = str(route_meta.get("transaction_interruption") or "").strip().lower() - if active_before_interruption and interruption in {"intent_shift", "explicit_abandonment"}: + if active_before_interruption and interruption in { + "intent_shift", + "explicit_abandonment", + "same_intent_replacement", + }: interrupted_tool = active_before_interruption.get("tool_name") self._finish_active_transaction(state, "CANCELLED") state["transaction_pre_validation"] = None + # Generic framework boundary: consumers do not need to know why the + # previous transactional context was invalidated. This deliberately + # avoids leaking same_intent_replacement (or any other interruption + # subtype) into domain agents/workflows. + state["operational_context_reset"] = True + if interruption == "explicit_abandonment": + action = "cancelled_by_explicit_abandonment" + elif interruption == "same_intent_replacement": + action = "cancelled_by_same_intent_replacement" + else: + action = "cancelled_by_intent_shift" state["tool_policy_result"] = { - "action": ( - "cancelled_by_explicit_abandonment" - if interruption == "explicit_abandonment" - else "cancelled_by_intent_shift" - ), + "action": action, "tool_name": interrupted_tool, } diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt b/agent_framework_oci/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt index a54b9a88..f524a570 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework.egg-info/SOURCES.txt @@ -53,6 +53,7 @@ src/agent_framework/guardrails/__init__.py src/agent_framework/guardrails/base.py src/agent_framework/guardrails/config_loader.py src/agent_framework/guardrails/custom_rails.py +src/agent_framework/guardrails/dlex_sanitizer.py src/agent_framework/guardrails/executor.py src/agent_framework/guardrails/framework_llm_client.py src/agent_framework/guardrails/langgraph_adapters.py diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/__init__.cpython-313.pyc index 1d80024a09a1682ae714783ee009ac56c27f3921..4beeadf8bb927e731c26c3ba132adb123a6e6c40 100644 GIT binary patch delta 64 zcmbQhJeQgKGcPX}0}!a1uARvJUZX%iH?KrLMc+5rGd`dwKPxr4q*y;YDJ4liF+DY} SBtAJmuOzWJ-e|HYV>JME3>5AF delta 51 zcmbQsJb{_}GcPX}0}$wGt)9sJUOHJnH?KrLC9xz?KRz)jF)t-QFFrG`xF9vTWU>-t FH2{Q^5Rd=> diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/extensions.cpython-313.pyc index 47e059740bea2d968009a7b21be7367607ba43a6..df240730d9713d6519f56ec19c71dcc3ea4efce0 100644 GIT binary patch delta 20 acmcaBa94o)GcPX}0}!a1uHDET!~p<383h^u delta 20 acmcaBa94o)GcPX}0}$NIS-g=uhywsa9R>RU diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/idempotency.cpython-313.pyc index 221978a7e1926b91f5757bb2f69d2de0126bf325..a526262e0cc97fd03683e032cad60442143b03a6 100644 GIT binary patch delta 66 zcmX@=ddZdhGcPX}0}!a1uHDG(#;j4SpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;HahdELn0JKdN&j0`b delta 53 zcmccQdeoKsGcPX}0}$wGt=`D(#w?wtpPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(Y^6 HIZ_?~x&#q% diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/__pycache__/observer.cpython-313.pyc index 01101db924a5b707e898df279b520ff566e9565a..2aa5c9bab3a8af051437b7862dfd8888dd8a8569 100644 GIT binary patch delta 22 ccmbQ*$vCx>k^3_*FBbz4sG6?b$lc`v07sey@&Et; delta 22 ccmbQ*$vCx>k^3_*FBbz4+{{_Lk-N(U08O3-(*OVf diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/__init__.cpython-313.pyc index 171af7a9e44fbd759ae24d118163e19b636a5eb1..6f06afb7404b9c9ad19e6f549edb8b1200c2ec39 100644 GIT binary patch delta 20 acmZ3=vXq7UGcPX}0}!a2uHDEj#RLE{u>@8C delta 20 acmZ3=vXq7UGcPX}0}$NIS-g>3iU|NXrv)_t diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/composite_publisher.cpython-313.pyc index e7aa90165a7fb514c4296d49c3706cfd65ef69cc..57af840d345729682e55e0713aa27ff9f411c64f 100644 GIT binary patch delta 20 acmX>hd_tJ}GcPX}0}!a2uHDGJmlFUxOa){B delta 20 acmX>hd_tJ}GcPX}0}$NIS-g>ZFDC#&LIy(s diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/event_builder.cpython-313.pyc index a06866c9d49d92743e08c19099ea2c6bb4aaf57a..d28453d97bfa21a375c12916a4de113258818740 100644 GIT binary patch delta 20 acmaFI`HqwOGcPX}0}!a2uHDG}m<0eo&jq~z delta 20 acmaFI`HqwOGcPX}0}$NIS-g?^F$(}i#Ri-J diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/factory.cpython-313.pyc index 1e73aaa889a99e2812e11fc4dbcb095e3e4830f9..09eb4d943ba4963954cc7d049ff9db26f25f83a9 100644 GIT binary patch delta 20 acmbOyKTn?fGcPX}0}!a2uHDEznI8Z)as>nc delta 20 acmbOyKTn?fGcPX}0}$NIS-g>ZGCu%3Xa(Z{ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/publisher.cpython-313.pyc index 8fcf15f5d02813d2c88cebd40810a82546dfd486..65d1d490a760d81612cb45e7d2901c300a61ee52 100644 GIT binary patch delta 20 acmcb_cZrYtGcPX}0}!a2uHDG(&JF-OCIt2X delta 20 acmcb_cZrYtGcPX}0}$NIS-g?kogDx{90k@B%LqdD>i)y$`7 ziiwH>UVIC)s%{x6Y^vU}M0%M-2Lj@Q+OiPufp+U}D~qL|)oQclzYJ3pfB%C3Ps(EnR83 z-EQN1E^uy2&8umaNGobZ-Ecy2(|B0crwzG5y(?3_De-6{1f%=Xr`w_g` z7wB0mmMMv4u-Lhfx$vDrmDQ>`6+XUO<+9?c5&B|56|JetTEbqUV;HM9T8`nd2|BsU zS_(O*jumPtsqk_U6&8&)N8tcbMDVS~omi%sAmBA%MMp|dm}-P4Mz^dm8ZT!S;AJ69 znSNNOjwF+@=sd$m6Y+gvHWH1qVJZ@3W8r0riE%6wUQ;GHcA1UyR5VV_&ax}e4SABx zS}ehYJu|bj6vMHUG>2P(!Ay!zEJ3|_CKg+xmJ?JY#iuw{$?!ZEolEiTGBwY{sYonw zh2q;5xJ2sW0yUnRYp3B87(3QJc6#6>btTF#BvL%J#3Yj}N6jxV@r&&8L%lhosL^l+ zxJ=H03~J&_MJgF)cvkq({;lCF2z0?`IjbOu8^mjyaMU5Jzg82jx{UYp#T)(?a@9wH z_XB|N%S?*xO5^R|XVafdr{5LLEziwe*{h%&5kNtZRU4h@`V5&pBpN$Gg@jfTWX)!E zMxIIJ4)`CtMC&vlB|=G%hK+`_DZQGtid7vzC8279v}`KUye4x#=Nk}f27w0R!kE)+ zgDLd8DMVxQb7M!A1qMW4clNJCx|$$O8u$9IkCZ zh13(Ic4H+S&J2i_=4|*U%l@1EBRSY3#WfP7ZL>2|pKI*Obq7TI*q==j-b|48&B2T} z*VLWs9Ty#^K#PR8;`k}iF#+~Tc-wBJJDqCJPv2i$?|Y)T7j#Il7tcB>+5^A`alzm+ zw!(rB!h&zD+zzJ)M4bn8NfC#zof{IZ!=M`ljJu?r=-VZ?uIKz`MC%mjNA$YOYK3~o zWyK<-tr(CY_JPX^?bH065jdw>>eA>|pJ+J>PDpe9bvaNA1CZTm`Btr1efY02#45lz z#HBB!utBHt_ym@5RWu(2bRItqaovXEHlLmp_4Oc_M<*d#Y_`wWWGIu?+jglR8 zTZZ5&;BA5(3(-0RqItDNNMJj0DUZgAnYWx`bw5~=k?i*TSU# zxUW{46tCgQEw2z(Ju`-9N@Gx^c;+R82F1@ZMbIGp;pv(G8eI(V^b<`At{(Y!k=e8B zA^xg49G&Mw%RJXg9f_~aP}itHBF;8dav3zwiOiSCNU<%@dnunwBx0N-epmRQ?x+J@ zHQx{%`cuYf(2UBSr9?OtWBa&!!tZt7cOo)b{|mhllp$wZWd-HP0hNT_1W3tM5(?Ab JD&$Hs{{iU0R4o7i delta 2395 zcmZuxTTB~A6x}6e&4QnpwOPMyOn8R(BRmq)G+-VD8U_g95=sLkAp}x`qJ}(`uSSa0 zq>EI4pw?1Jg_Iwv^ds#@AAeEvV|5~><)g|+RjGf|rixUls(NP}f*?L$fACbkC2F?|I!+jA4SmLPQ7)32 zfv3-mh>S6wGom~sHyifYd>xyQ3OJ=HX4L#9k7(4|SsXW2L4MOT7R^iR1!08EmU&D6 zhJ~0ZgN#@*S@ySCOy?tZ?U}(X!H5oMbl6NAO__ad{J6zP$D%0aa=h#eQlxB?pi#pe7|aRfUB#~ zdDL*T)Ph5`Rn@Ju4%Kto2A#G6HF8`NsIOUl(0|kF(Rs9?cCM~N=h1;WIqnd65G!4S z$wp(jjr#T20y@7Q6y-F%I=^1j$8r7O_pwdCq5<>Z9PRG1*&PUk4M z_nEW%Dxop$Tf6GZBDrW3jdA*M(0^es1?8??zatZ9lB=82%`k;x9QPJ@)D-GhFpW-f z6*D^Z40@a6WpmS(Gqs?3fo%aGdo#A!(8FUscW-hOe zIfs^BrD#Xz(ekSZw1O_|;Ywz-iPtk9DP4|3spYpkp5f4Hjod9R8^?0WUkYa{;s1_t z#H9s`8nk~LA(3W_IqP3(9~6E0;Ds_T*-UXe6vw?Z$Plzu?|u6gByI4w`k^EUH-tZ( zTCGnTB!;LMke=l^?u>mjd3Q2#87oy!mA375l1m9933A~^OCpk#x7)F!g%}x{C&~>hJ0XFK1SC1uLDz#YDwf^wPs>tVD^*aQB{BdRz~65(7#F zIQa!x*;q)7COfgSjs#eJP>>xPJ&C5I8y8iR5UuVC?7t4x5kazT422#po<$~%_e^u#TXwn|URq|sLGx023NpV2E0_gScr;t`tPqoH&! zqx`xuclOj;Y;`SO9ei#1(sb}jaA;+Dz9KLF39a+dkfecWt03DoytA^h6ldo5wD%)N y#Vze|#4X;}evLHM&xm5zk2VL{N0Vm-D#@h@$rEfvL}qEeAQinRplRF+cK-rkiV98u diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/__pycache__/tim_sequence.cpython-313.pyc index a18f97b5d01b9cc8958509185ddb1c51701fa80b..50374fbbd90ea2561a5316f5ac9dc54ea2f57088 100644 GIT binary patch delta 22 ccmbQ;$vC@{k^3_*FBbz4sGF|c$UVUY07$9^1^@s6 delta 22 ccmbQ;$vC@{k^3_*FBbz4+{{_Lk$Zv*08XF=&j|B4o delta 20 acmZo+X<_01%*)Hg00cL47H{PK$p`>7eg)A0 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/kafka.cpython-313.pyc index b5e221b2a45c0a9235e1c84e871489dbf9b41c73..e2bfdc6c855850d79dbe4ae0ce3be7e750c280ed 100644 GIT binary patch delta 20 acmbQnH;s?`GcPX}0}yDKuHDGZ%?+XL delta 20 acmbQnH;s?`GcPX}0}$NIS-g>(n;if*yagcu diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/langfuse.cpython-313.pyc index 1387eaaa92436c263a48b9ee63efbb77d9f43ff9..d7b6bafd78d865dae5c5b4cc560bc8ff6dbb4e55 100644 GIT binary patch delta 22 ccmZqM%GkD*k^3_*FBbz4Xqc|u$Xy);088`+HUIzs delta 22 ccmZqM%GkD*k^3_*FBbz4+{{_Lk-Ity08!%y6aWAK diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/oci_streaming.cpython-313.pyc index 585cc2841c4ad97f56b90c0430977154b1d3800d..1dfea145bd965b92c4b247ece660185137eecb2c 100644 GIT binary patch delta 20 acmbQwH=mFDGcPX}0}yDKuHDEj!VUm4H3VJ& delta 20 acmbQwH=mFDGcPX}0}$NIS-g>3gdG4mBn3PG diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/analytics/providers/__pycache__/pubsub.cpython-313.pyc index cfeae8e067794ecc40c113e8ea258e8abf616e15..a47ad3262c0216461c66ff66d79d69f3d58d4a0a 100644 GIT binary patch delta 20 acmbPaHOY$mGcPX}0}yDKuHDGZE(-uRa|ERT delta 20 acmbPaHOY$mGcPX}0}$NIS-g>(T^0a6Vg-W$ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/__init__.cpython-313.pyc index beff5c8389cb8b1869a4db7b456bdda0dfddb5e1..89af7e7df19745860731fd18c5bd7783c2b94074 100644 GIT binary patch delta 20 acmbQrJe8UIGcPX}0}yDKuHDGp#Rvc~69l9H delta 20 acmbQrJe8UIGcPX}0}$NGS-g?EixB`f2?c`y diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/billing/__pycache__/usage_repository.cpython-313.pyc index 386d5fbc73df7de8644101120ddc3a0fd2ee1cf1..b7c26689a9847cd4327e4e2093a0157d34c8b85b 100644 GIT binary patch delta 20 acmX?6b)t&k diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/cache/__pycache__/cache.cpython-313.pyc index e24b9c715e395aaba28f4d774c8b41c44c98d240..bb93988ad7d6142fe6b61d4db08c52cf2658329e 100644 GIT binary patch delta 68 zcmZo@WbAEZt`pWB3UlIU9eg)?M diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/channels/__pycache__/transcription.cpython-313.pyc index 17f6fd043738c906b863eb69aaeeeac64962535c..9ea9e415fb1b91d5937e8dac5b58e3f0f2bdaac9 100644 GIT binary patch delta 20 acmbQvJ)N8TGcPX}0}yDKuHDGp!wLX0hXlR= delta 20 acmbQvJ)N8TGcPX}0}$NIS-g?EhZO)gb_JXO diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/__init__.cpython-313.pyc index a89c93af39a9db8b271ef08fec274c2e85ac09dc..1d39403a903179f7a23edba73f72bad97712ef34 100644 GIT binary patch delta 20 acmbQsK9`;QGcPX}0}yDMuHDEzi5UPg>;%UE delta 20 acmbQsK9`;QGcPX}0}$NGS-g>Z5;Fie+XbZn diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/checkpoint_repository.cpython-313.pyc index 1b4826bfbc63729b3ebc55a8bc0dbee810bce08f..e874174f70f7924874a98ac3cabf68d361ab7078 100644 GIT binary patch delta 22 ccmbPxg>mi`M()qNyj%=GplP~xBezf%08^m`N&o-= delta 22 ccmbPxg>mi`M()qNyj%=Ga4To=MsA@j09lX+C;$Ke diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/checkpoints/__pycache__/langgraph_saver.cpython-313.pyc index 34b526898166801e1e2dba39f10ee5076fe78e54..1c996b7a1af61218396acd86fb3b561e0c6252c7 100644 GIT binary patch delta 22 ccmey|%=EdLiTg7zFBbz4Xqv9w$o--b09N=1LjV8( delta 22 ccmey|%=EdLiTg7zFBbz4+{#(Jk^4m>09@w?ApigX diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/__init__.cpython-313.pyc index a601bbbb7ae68db6bff50dbbc9748fe4fde8d6d5..37e51598390c6512a200f58fa511643fde356e95 100644 GIT binary patch delta 63 zcmX@jc!QDqGcPX}0}!a1uARtjtWmC?n^&TrqVF5*86QxTpOuGcPX}0}$wGt)9qjES;sFn^&Trl30?cAD@_%n3s~D7oV9|T#%YvGBK?P E0CXP^g#Z8m diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/agent_registry.cpython-313.pyc index 4d737a18775088b30cf45f2e189583f889e56583..e3620c741d762b6c1f63b61877932c36bdaefb83 100644 GIT binary patch delta 20 acmZ2#w$zOKGcPX}0}!a1uHDEjB@F;M#01d* delta 20 acmZ2#w$zOKGcPX}0}$NGS-g>3N*Vw^&jqsp diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/config/__pycache__/settings.cpython-313.pyc index 748819683d5fce09bdefee5ff2c7a3e03193bb1e..1876f380006442218df13b77df388cfaca6fa4dc 100644 GIT binary patch delta 66 zcmeyE^Dl?{GcPX}0}!a1uHDFeLrbGvKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUUKdoYJ00d4K@c;k- delta 53 zcmeyD^D&3}GcPX}0}$wGt=`CeLrXeKKR2&LKP9mwQ9nL0DKRf4KQBHruecyJxnwi9 Hb}=^q^YRju diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/gateways/__pycache__/__init__.cpython-313.pyc index 7f83e0f87871d6c23c096f475eac575221a07f01..b7933581bca688eb729e0ee31860acfa1f8cffed 100644 GIT binary patch delta 63 zcmZ3^w1tWLGcPX}0}yDKuARu;q*1A#n^&TrqVF5*86QxTpOu=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;Jw9V0h40JXOk5dZ)H delta 53 zcmZpZ`YFl%nU|M~0SFAVR&V4!!6;p(pPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(bE$ HBR4kyxO5S+ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc index e67486929d7acc87bcea7dd167e89642b5912002..b59b1a3f393a7e2316ee9eea9112d9545d6ace62 100644 GIT binary patch delta 20 acmcc5eV?2AGcPX}0}!a0uHDFeg%tojxdoH} delta 20 acmcc5eV?2AGcPX}0}$NGS-g?^3M&9Zy#{pv diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc index 2b0d27b2d00eb2c56273e7c304475988ed2ca6b6..dc2c8ddee836ab76e4513e332ce0a1b119fff4c7 100644 GIT binary patch delta 20 acmaFJ^^lAEGcPX}0}!a0uHDET#tHyDSv=>_=! diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc index fef46f5f8ec2c894d69228ab50d9016e1d2da8c5..f1aaec9a44b7fefb91e4db01c597bd9cded42e8b 100644 GIT binary patch delta 20 acmX@*e#)KuGcPX}0}!a0uHDGJUl{;G{{`y+ delta 20 acmX@*e#)KuGcPX}0}$NGS-g>ZzcK(x0|v|h diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc index f232e066e4e3ebf1319c5c739691e764a3c5729d..271e5d608e427c98d452e3ee972c1761260b9850 100644 GIT binary patch delta 20 acmdm{v`vZoGcPX}0}!a0uHDG3EeHTPnFP}S delta 20 acmdm{v`vZoGcPX}0}$NGS-g>3TMz(0odvW2 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/dlex_sanitizer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/dlex_sanitizer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8a868ccdcae27a426ac5bab3a2708a722dbef85 GIT binary patch literal 8489 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 K_GS$|j{gUY0tLkY literal 0 HcmV?d00001 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc index 3163328a157240a8b688d085b8449d56cd5b2212..48666933633e4c535edfda83a1bd60fa2b6bf332 100644 GIT binary patch delta 22 ccmcaTlkxUUM()qNyj%=Gpk}&uBliU#09D%t?EnA( delta 22 ccmcaTlkxUUM()qNyj%=Ga4To=M(ztf09)S&&Hw-a diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc index 8d8f919a3dcafa9df89d3f96e46e523b0bb75a8f..efdc67dbe678fbb689961c902c4e7907b021e74a 100644 GIT binary patch delta 20 acmZ3gw^Wb&GcPX}0}!a0uHDEjB@O^Mrv$zL delta 20 acmZ3gw^Wb&GcPX}0}$NGS-g>3N*n+_s|B9` diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc index 548ec0546a4323185c107c93e354229581f1706a..d945f0b1cb6a1294d997365d2112f746ed44630c 100644 GIT binary patch delta 22 ccmeyem+{M9M()qNyj%=Gpk}&uBlpWV0AHL3k^lez delta 22 ccmeyem+{M9M()qNyj%=Ga4To=M(&q!0A-*Ea{vGU diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc index e3641b6ad80f9d9cd01c3a877fc0639cee612d04..7c280a45c0b19ffe13f54791fe0e13d85c527397 100644 GIT binary patch delta 22 ccmaE{it)uNM()qNyj%=Gpk}&uBX?{l09mRAzW@LL delta 22 ccmaE{it)uNM()qNyj%=Ga4To=M()^90AH>Lpa1{> diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc index 1e5a9b9635eaf0a55be427989df3d21bd5e73b1f..9408e0b7f18f840e08f53d754e144cc9f75c0ada 100644 GIT binary patch delta 20 acmeAU?GEMs%*)Hg00e5LYd3N?XaN8_CIwLd delta 20 acmeAU?GEMs%*)Hg00g&k7H{Nk&;kHKDh4tD diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc index d80bc2d3f21f2dd934c75c02f562a34776a2a6b3..c43b8e564671a2848064f87185804a4f376aa26c 100644 GIT binary patch delta 20 acmbQhI)Rn@GcPX}0}!a0uHDGp$^-x~h6I=Z delta 20 acmbQhI)Rn@GcPX}0}$NGS-g?El?ebgiUoN9 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc index ac1ae631c5170120413e5fccaf54a2c6de3b1a72..3484fe0bf3b48951660d62338c4090e790376a94 100644 GIT binary patch delta 20 acmX@kbDW3!GcPX}0}!a0uHDFO!v+939t6(- delta 20 acmX@kbDW3!GcPX}0}$NGS-g?kh7ABeA_cGj diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc index 32fb0ab1872155d6cecf02e2dd503cfe11116602..69d84557413224f3f7c1dcc630aef7f8675c2a83 100644 GIT binary patch delta 20 acmaFI{*ImdGcPX}0}!a0uHDG}m>B>+e+9Py delta 20 acmaFI{*ImdGcPX}0}$NGS-g?^F*5)~g9exY diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc index c5b5b845d737ad32664c90229d022682e1266c2b..3c8b0a05da7a4ef2f925e6853a7c19e5ffe33601 100644 GIT binary patch delta 10888 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 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc index 3916d89d14c8cd6dc1a1a6c8befd1a03d5c15d4c..43a26825cc1cf5148a160e5128ae06a0bc3b5395 100644 GIT binary patch delta 20 acmeAd?ic3%%*)Hg00e5LYd3PYZ~_1_T?DuQ delta 20 acmeAd?ic3%%*)Hg00g&k7H{Nk;RFCUVFj50 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc index e818798a4488801df846c0dfe0dce323eecd1cb4..658adb8b245d74cb8fd74c8a19ad353295f25d1a 100644 GIT binary patch delta 20 acmZn`Y!>AH%*)Hg00e5LYd3ONZ~y=>Gz5AH%*)Hg00g&k7H{OP-~a$NI0bM3 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc index 037267311a8ef5266b355a4a03517c0f53d281d2..8c22899e1d3452a7e2d36f2d05269b45e5411eaa 100644 GIT binary patch delta 20 acmexv`rVZKGcPX}0}!a0uHDG}MhXB&b_PWN delta 20 acmexv`rVZKGcPX}0}$NGS-g?^jT8V)dIu%| diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc index b21b72b29b8169ad077af32b15313c7f07bded69..d13ac46c7fe94c90bc0503b5fbb1d03222f0bac9 100644 GIT binary patch delta 20 acmX>sb6AG^GcPX}0}!a0uHDFO!3O|3#supC delta 20 acmX>sb6AG^GcPX}0}$NGS-g?kf)4;d$_2~- diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc index b15f86c964479352421bd87898443705b294ee16..5295088950a8d94c2fea80020b1f3bbbcf6b4f9c 100644 GIT binary patch delta 20 acmca9dsCMCGcPX}0}!a0uHDFejt>AoS_Q8F delta 20 acmca9dsCMCGcPX}0}$NGS-g?^93KEhUIvf= diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc index 9c8fbc947c5d8c566150434f08e5cdb2165500a0..38f1aea0af2cb9d0c0823c73788c8acf2a502a84 100644 GIT binary patch delta 20 acmZ2rwZMw|GcPX}0}!a0uHDEjDhmKPl?2TI delta 20 acmZ2rwZMw|GcPX}0}$NGS-g>3R2Bd~nFX!@ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc index 78bb173bcacfea29a29e8641039bb6aed61a1019..9b2f632da982b4a35ce1c0948dc76ae7cc118730 100644 GIT binary patch delta 20 acmX@$dcc+YGcPX}0}!a0uHDGJRS5t=CI!d< delta 20 acmX@$dcc+YGcPX}0}$NGS-g>Zs}cZ5Dh8Z^#ybQ delta 20 acmcc3e4Cm3GcPX}0}$NGS-g?^0wVxH`36-0 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc index 24514e29c5219b838c316fe37de10dcb4f898b00..6bc38dea756c9989838583ecca74b0775397fed0 100644 GIT binary patch delta 20 acmeyU`%#zsGcPX}0}!a0uHDG}Obh@;Vg?@o delta 20 acmeyU`%#zsGcPX}0}$NGS-g?^nHT^|W(NQO diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc index c8eb5e0062312b89a3ac0758ba04c002edd013c9..e80e85dc224f55966a2c7560b48ff76c1df8f6ef 100644 GIT binary patch delta 20 acmZ1~Y`GX>TF delta 20 acmZ1Zi9P^GHwL!= diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc index acdb4fbd693863122f03afa4f795a4cab44e3237..b2fb4d1089a824243a22f6fec5a900c68eb26a99 100644 GIT binary patch delta 20 acmdn5uwQ}uGcPX}0}!a0uHDFOA^-q7xCH3{ delta 20 acmdn5uwQ}uGcPX}0}$NGS-g?kL;wIlyambt diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc index 2eea70b1e96c54eea71be9c4e91ba23a9936016b..d4050835f379bd07cf562e567843f55f7aa7f8b4 100644 GIT binary patch delta 20 acmbQmHH(Y;GcPX}0}!a0uHDGZ&k6uB%>+XL delta 20 acmbQmHH(Y;GcPX}0}$NGS-g>(pA`T%(FG&` diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc index 2b40e1b065dd7751bdcc235ccb39fcd504335b96..2d6adf34193140fd2bf35b50409ea963d93dbfc0 100644 GIT binary patch delta 20 acmbQqH(j~xIwI|VBM diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index 37cae7bbe7d2803af7c56dff82c4f92c71dc4c42..2c3791e9e4c2922bba01ca3559367d6b70cc9416 100644 GIT binary patch delta 22 ccmey?$@s04k^3_*FBbz4sF|+a$o<*{09Wh>761SM delta 22 ccmey?$@s04k^3_*FBbz4+{#(Jk^8j^0A270_W%F@ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc index 684c41674b4643a4a51832d14d71e151c8e0c4fc..3b9b522d5be407e9cce1e5593a5a2263680d30c6 100644 GIT binary patch delta 20 acmca?ec77(GcPX}0}!a0uHDFeLKXl*u?6S= delta 20 acmca?ec77(GcPX}0}$NGS-g?^ge(9^wFb!m diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc index 1122b7b6e5c4c48794c46a19d61bfb2fbd6927cc..702a1c147f5165a8469b7c42941e9c2f5ece4cf8 100644 GIT binary patch delta 22 ccmccK!Fauck^3_*FBbz4sF|+a$nE0-08x+zHvj+t delta 22 ccmccK!Fauck^3_*FBbz4+{#(Jk=w@w09TX;7ytkO diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc index 0fca1941bc13401b9f02347f41d3640c9e6491a5..e1602bd0bead516489b03edd3aa209458ba1434d 100644 GIT binary patch delta 20 acmaFm_{x#{GcPX}0}!a0uHDFeUjYC|BnCVH delta 20 acmaFm_{x#{GcPX}0}$NGS-g?^z5)PECZIx_${qXo18 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc index aa9d59997ce4ca41417f6443c95bf4911d40fd4a..4ac41e0ad986a47c71b57cc63e3dde54fbfa79b8 100644 GIT binary patch delta 20 acmZqVZRF+t%*)Hg00io$Yd3P2u>k-tSpy+Rs~=H diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc index 1013767cdcd1c369531be02eff94e8076f95b691..fede3088669a5c093f782db4e7af9822ef02fcd3 100644 GIT binary patch delta 20 acmeBF?^Ngh%*)Hg00io$Yd3P&2?GE$P6XKi delta 20 acmeBF?^Ngh%*)Hg00g&k7H{OP69xb}O9iZvoZijBL=7d diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc index 27d36a486d6d185f06f84c0029d494792b9e72b3..9feeeb4e04c70a10c2a3a47a0321a09a370087a3 100644 GIT binary patch delta 20 acmZ1ywj_-EGcPX}0}!a2uHDEjsSN-=It3sA delta 20 acmZ1ywj_-EGcPX}0}$NGS-g>3QX2q7HwFLz diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc index a5045828c44a8c80794144927c0b234f8f05ec9e..3947485cef5ebc839a48ef4791c51299cd5e0b24 100644 GIT binary patch delta 20 acmca8c~O%4GcPX}0}!a2uHDFej0XTcj|GkZ delta 20 acmca8c~O%4GcPX}0}$NGS-g?^7!LqLj0SE1 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc index 4b1e1f9dbe79bbeda4dd7ce2c92aba10c0006155..f46198f478a099fa00b01754cd964961baf838c7 100644 GIT binary patch delta 20 acmZpbYL(*t%*)Hg00io$Yd3QL;sF3M(*-O5 delta 20 acmZpbYL(*t%*)Hg00g&k7H{PK#RC91&;|?u diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc index d721fe49229127363d68206955ed824830ce6825..86d3ca74f19ed57713000867feb8ffba69faaa88 100644 GIT binary patch delta 20 acmcbibwi8$GcPX}0}!a2uHDG(D+&NW6a`KI delta 20 acmcbibwi8$GcPX}0}$NGS-g?kR}=t75e6;* diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc index 7973b2a7da6bffe6cd72c98debc6922994a1f921..d365db449802ab986f74c54bbd5a6cc7654c0452 100644 GIT binary patch delta 20 acmaE$^+1dJGcPX}0}!a2uHDETDhdEWR0U=L delta 20 acmaE$^+1dJGcPX}0}$NGS-g=uR1^S5Q3gf; diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc index a7e0e360abf8b03c15642407bb3cea9f4c2da0af..57ba0f7a708c4f1807b6b5957583a86a71608b59 100644 GIT binary patch delta 20 acmaFv@Z5p>GcPX}0}!a2uHDETqW}O!j|H*- delta 20 acmaFv@Z5p>GcPX}0}$NGS-g=uMgagzj0Tbb diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc index 526b5cf15a5d944671b22807b46dfa6ea0261e78..f48f21d49c43955fa2b9f5a97e70ad0f1b9239de 100644 GIT binary patch delta 20 acmbQvGM$C{GcPX}0}!a0uHDGZ!vp{@dIT!~ delta 20 acmbQvGM$C{GcPX}0}$NGS-g>(hY0{SegzBw diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc index 0c9b4c37a66435c37302b65fb8f139947a229ede..738af6c8e05297296b25bb99d1c91c97a55f3bf3 100644 GIT binary patch delta 20 acmcc3cbkv DlexMaskRule | None: + if not isinstance(raw, dict): + return None + name = str(raw.get("name") or "custom").strip() + pattern = str(raw.get("pattern") or "").strip() + replacement = str(raw.get("replacement") or "[DADO_PROTEGIDO]") + if not pattern: + return None + try: + re.compile(pattern) + except re.error: + return None + return DlexMaskRule(name=name, pattern=pattern, replacement=replacement, flags=0) + + +def _extra_rules_from_env() -> list[DlexMaskRule]: + raw = os.getenv("GUARDRAIL_DLEX_OUT_EXTRA_MASKS_JSON", "").strip() + if not raw: + return [] + try: + data = json.loads(raw) + except Exception: + return [] + if isinstance(data, dict): + data = [data] + return [rule for item in (data or []) if (rule := _parse_rule(item)) is not None] + + +def _extra_rules_from_policy(policy: dict[str, Any] | None) -> list[DlexMaskRule]: + data = dict(policy or {}).get("sanitize_patterns") or [] + if isinstance(data, dict): + data = [data] + return [rule for item in data if (rule := _parse_rule(item)) is not None] + + +def _mask_payment_sequences(text: str) -> tuple[str, int]: + """Mask 44/47/48-digit payment tokens without consuming surrounding prose. + + Regex alone is awkward because formatted boletos have variable punctuation. + We scan numeric/punctuation runs, count digits, and only redact the run when + it has a canonical payment digit count. + """ + token_re = re.compile(r"(? str: + nonlocal count + token = match.group(0) + digits = re.sub(r"\D", "", token) + if len(digits) in {44, 47, 48}: + count += 1 + return "[CODIGO_DE_PAGAMENTO_OCULTADO]" + return token + + return token_re.sub(repl, text), count + + +def sanitize_dlex_output( + text: str, + *, + policy: dict[str, Any] | None = None, + exclude_values: Iterable[str] | None = None, +) -> tuple[str, list[dict[str, Any]]]: + """Return ``(sanitized_text, findings)`` without exposing raw matches. + + ``exclude_values`` contains explicitly authorized values (for example a + workflow-generated protocol). They are temporarily protected so generic + masking never alters them. + """ + original = text or "" + sanitized = original + findings: list[dict[str, Any]] = [] + + protected: dict[str, str] = {} + for index, value in enumerate(exclude_values or []): + value = str(value or "").strip() + if not value or value not in sanitized: + continue + marker = f"__DLEX_AUTHORIZED_{index}__" + sanitized = sanitized.replace(value, marker) + protected[marker] = value + + # Payment sequences use digit-count validation rather than a plain rule. + sanitized, payment_count = _mask_payment_sequences(sanitized) + if payment_count: + findings.append({"type": "payment_barcode", "action": "redact", "count": payment_count}) + + rules = [r for r in _BUILTIN_RULES if r.name != "payment_barcode"] + rules.extend(_extra_rules_from_env()) + rules.extend(_extra_rules_from_policy(policy)) + + for rule in rules: + try: + regex = re.compile(rule.pattern, rule.flags) + except re.error: + continue + sanitized, count = regex.subn(rule.replacement, sanitized) + if count: + findings.append({"type": rule.name, "action": "redact", "count": count}) + + for marker, value in protected.items(): + sanitized = sanitized.replace(marker, value) + + return sanitized, findings + + +__all__ = ["DlexMaskRule", "sanitize_dlex_output"] diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py index 676683b5..8ae0a4ec 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/rails.py @@ -25,6 +25,7 @@ from .calibrated.output_sanitization import mascarar_pii_output, sanitizar_toxic from .calibrated.rules.pinj_patterns import _PINJ_PATTERNS, is_obvious_injection from .calibrated.rules.tox_blocklist import _EXPLICIT_TERMS, _THREAT_PATTERNS, is_obvious_toxic from .framework_llm_client import classify_with_framework_llm +from .dlex_sanitizer import sanitize_dlex_output from agent_framework.workflows.input_contract import has_meaningful_unmatched_policy, has_semantic_classifier @@ -832,11 +833,64 @@ class DataLeakageOutputRail(Guardrail): } protocol_authorization_verified = True + # DLEX_OUT is permissive for recoverable leakage: when classification + # rejects the candidate, mask only generic sensitive values and re-run + # the same classifier. The agent/domain remains unaware of this detail. + # If sanitization cannot change the candidate, or the sanitized version + # is still unsafe, the original blocking behavior is preserved. + dlex_sanitized_text = None + dlex_findings: list[dict[str, Any]] = [] + sanitization_recheck = None + original_dlex_result = dict(out) + if not bool(out.get("allowed", True)): + policy = dict(getattr(self, "_guardrail_policy", {}) or {}) + candidate_sanitized, dlex_findings = sanitize_dlex_output( + original_text, + policy=policy, + exclude_values=matched_expected_protocols, + ) + if candidate_sanitized != original_text: + sanitized_classifier_text = candidate_sanitized + sanitized_classifier_ctx: dict[str, Any] = ctx + if matched_expected_protocols: + sanitized_classifier_text = _mask_authorized_protocol_values( + sanitized_classifier_text, matched_expected_protocols + ) + sanitized_classifier_ctx = _mask_authorized_protocol_values( + ctx, matched_expected_protocols + ) + sanitization_recheck = await classify_with_framework_llm( + _llm(ctx), + "DLEX_OUT", + {"text": sanitized_classifier_text, "context": sanitized_classifier_ctx}, + profile_name="grl", + component_name="guardrail.dlex_out.sanitization_recheck", + generation_name="guardrail.dlex_out.sanitization_recheck", + ) + if bool(sanitization_recheck.get("allowed", True)): + dlex_sanitized_text = candidate_sanitized + out = { + "allowed": True, + "label": "SANITIZED", + "reason": "DLEX_OUT sanitizado e revalidado com segurança", + "original_reason": original_dlex_result.get("reason"), + } + metadata = { "mechanism": "llm_rail", "data": out, "calibrated": True, } + if dlex_findings: + metadata.update( + { + "action": "sanitize" if dlex_sanitized_text is not None else "block", + "sanitized": dlex_sanitized_text is not None, + "findings": dlex_findings, + "original_decision": original_dlex_result, + "sanitization_recheck": sanitization_recheck, + } + ) if matched_expected_protocols: metadata.update( { @@ -850,7 +904,7 @@ class DataLeakageOutputRail(Guardrail): code=self.code, allowed=bool(out.get("allowed", True)), reason=str(out.get("reason") or out.get("label") or "DLEX_OUT avaliado"), - sanitized_text=text, + sanitized_text=dlex_sanitized_text, metadata=metadata, ) diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/__init__.cpython-313.pyc index 335a5e10f178849c82442e340116f9d7936d4527..43e7ad7ae927c1fb13bd45685094fb58b045cbd6 100644 GIT binary patch delta 64 zcmeBWp2f`lnU|M~0SGir*G}Yqs!^$*n^&TrqVF5*86QxTpOuku?YZl2NdW4 delta 51 zcmbQm+{?`UnU|M~0SFAVR!`)9DxIUBn^&Trl30?cAD@_%n3s~D7oV9|T#%YvGFgJL F2>^p?5QqQ( diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/mcp_mapper.cpython-313.pyc index d121f1666814ac5ac4c6ccf635e7a61ec9e33b22..c2ca0e7533534d8399fe64f3a78e1dc76e67c8d4 100644 GIT binary patch delta 66 zcmX@9a!rN%GcPX}0}yDMuHDG(#H>-RpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;HagZUvJ0H{C~5C8xG delta 53 zcmcbna#Dr+GcPX}0}vQ!t=`D(#4MespPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(Y^8 H`5_+wurU#^ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/models.cpython-313.pyc index 46507e7c33db05676e23f2274eb5f6fd3d342ce3..257c4153cd81847c0c2d7763dac1f797054a57c4 100644 GIT binary patch delta 66 zcmdlhaY};wGcPX}0}yDMuHDF8&a6?cpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IFF7pBo0Hv@M{{R30 delta 53 zcmX>lu~&loGcPX}0}vQ!t=`C8&Mcj!pPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(Wr; H^8yY4t&|a; diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/identity/__pycache__/resolver.cpython-313.pyc index 0815e5b50c1a1a4f8e9fc0ed0160da52f6602bc1..03c55fa72c5ae596d477d4311edce450ee0c1c27 100644 GIT binary patch delta 66 zcmcbo@>+%aGcPX}0}yDMuHDET%&bwVpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;I_lKCkg0JaPkUH||9 delta 53 zcmaE@a!-Z(GcPX}0}vQ!t=`BT%q*RwpPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(Y^1 H`6(X&yBHDv diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/__init__.cpython-313.pyc index 2c9423c896ff7eda6f517aa24fc4dda292215725..8f8ba56a4d9ddbea53fcacc7ae0d195c560c8eef 100644 GIT binary patch delta 20 acmaFE@`i=`GcPX}0}!a1uHDF;$OHgAy9Fr# delta 20 acmaFE@`i=`GcPX}0}$NGS-g=ukqH1p#s&)j diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/__pycache__/judge.cpython-313.pyc index 5b6c9bea97c6f1d3443090e67e02bb68ac6f9cd8..04d6239795dc9c3606e20d6a3136cbd263f17f86 100644 GIT binary patch delta 22 ccmca~o$1nbChpI?yj%=GplZ5yBlq#u0A5E2t^fc4 delta 22 ccmca~o$1nbChpI?yj%=Ga4To=M(*RQ0c27LkN^Mx diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/__init__.cpython-313.pyc index a8f8bd445da0b4df9230c8f82739f1ff76b5dbbf..bf49c5979a47dce055acd252abcb936e65b8e152 100644 GIT binary patch delta 19 ZcmaFN_?VIVGcPX}0}!a1uARtz0{}Mv1!e#M delta 19 ZcmaFN_?VIVGcPX}0}$NGSv-;Z1^_%L21oz^ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/__pycache__/llm_client.cpython-313.pyc index da332e3a3dafa1075d3483a5b045fbf871d4f3d5..08fc703bd848abcc4fe8682ac273a72072f71ef0 100644 GIT binary patch delta 20 acmbQIHBXECGcPX}0}!a1uHDEjED8WN_5_{) delta 20 acmbQIHBXECGcPX}0}$NGS-g>3SQG#~0R?~n diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/__init__.cpython-313.pyc index 1e1a85bdf29402da666678e6963524da678698b0..be306050e0f4620d050608474792ecebfc772069 100644 GIT binary patch delta 19 ZcmaFO_?nUXGcPX}0}!a1uARvJ001~A1%3bk delta 19 ZcmaFO_?nUXGcPX}0}$NGSv-;Z0RTNv24DaH diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/aluc.cpython-313.pyc index 0569e929e37bbff41785643ab650b593faa09fcd..2ac1c47549192a6d0647617bedabb2edf2aff1d6 100644 GIT binary patch delta 20 acmZoRZZ_ur%*)Hg00gS0Yd3ONNCE&g1_a{( delta 20 acmZoRZZ_ur%*)Hg00g&k7H{OPkOTlb5e3Bn diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/csi.cpython-313.pyc index ee50b32c8475b42ab90941e0988cf89cba8c40fb..cf04890af0b6cf42657b642129641ef45df251a9 100644 GIT binary patch delta 20 acmdnSwT+AWGcPX}0}!a1uHDG3%?bcECIprM delta 20 acmdnSwT+AWGcPX}0}$NGS-g>3n-u^&F$H)4 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/fallback.cpython-313.pyc index 7487f111cdb60a86e419fd29b9db322657f4da4b..090fb2e2626d6425726232d01b0c74ba3c8d31ba 100644 GIT binary patch delta 20 acmaEA_tcL2GcPX}0}!a1uHDETB@X~Z=mn(! delta 20 acmaEA_tcL2GcPX}0}$NGS-g=uN*(}8^9F|i diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/rqlt.cpython-313.pyc index cdc86752ac40eabbf44f752e55db8f41eea34a22..ff5a0c2a87be34fdbba1878e972e70aa535735ac 100644 GIT binary patch delta 20 acmeC@?C0eE%*)Hg00gS0Yd3PYumAus=>(nt delta 20 acmeC@?C0eE%*)Hg00g&k7H{NkVF3U(^aX$b diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/judges/calibrated/prompts/__pycache__/vctn.cpython-313.pyc index e915a97034ede7a3a852c7562c6a69c8b5b54851..3cb62fd0831d3fb04035e75a29a1e9a33d945891 100644 GIT binary patch delta 20 acmeyu@`Z)_GcPX}0}!a1uHDF;$pipC`vpM& delta 20 acmeyu@`Z)_GcPX}0}$NGS-g=ulL-Jt1_mPl diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/__init__.cpython-313.pyc index e61826b7658120bc5b7fb2ccdfced10280185d26..e760abd24f8ebecf19ad70331dd49449522c39a9 100644 GIT binary patch delta 65 zcmcb}e2=_?Wl%JKFTvDu`os^QKpO~JS TR}!C`pI4Gt9B(w)pRox5egPE` delta 52 zcmcb|e36;^GcPX}0}$wIt=`D3$taz!pPN^rpORRTs2`u0l$e*2pBJB*S6q;qTrxR^ Gu?YZ=3lOsa diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/base.cpython-313.pyc index 13d5bf27f51ebca1e55a3448f88607816b0735db..f80dc48ba6b7478a5e143e45f98fa05543e9017b 100644 GIT binary patch delta 66 zcmX@ZbCZYrGcPX}0}!a0uHDFehEbzHKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUUC&nli0JX#xR{#J2 delta 53 zcmcb~bB2feGcPX}0}$wIt=`CehEY0MKR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn%Qy H#wZp5x%CnG diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/profile_resolver.cpython-313.pyc index edc92cf457a1fb8c763f9a6c0bc101d342c873e7..1a7095ac30c7a0560bb1b747b35de676cb2d8ec8 100644 GIT binary patch delta 20 acmaFk@y3JuGcPX}0}!a0uHDF;r~&{-bOqD^ delta 20 acmaFk@y3JuGcPX}0}$NGS-g=uQ3U`^cm}lq diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/providers.cpython-313.pyc index b551d192f147dafec9c2c97ec7c9aed0c7cbace7..1fe1d943f18f1f59c3369d55aed3432a69d22472 100644 GIT binary patch delta 22 ccmZp9&(!jsiTg7zFBbz4sF|+a$X&S|095D)0RR91 delta 22 ccmZp9&(!jsiTg7zFBbz4+{#(Jk-Ks`09xz^;s5{u diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/structured_output.cpython-313.pyc index 22a376607e556c3d6c4bb085e8a514b30249db5d..52218b3d024463491e8bace30a5edb82894312a7 100644 GIT binary patch delta 66 zcmbPXwbY9HGcPX}0}!a0uHDGJhgqXmKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUUBW5#60I`-9EdT%j delta 53 zcmZ2#HN%SgGcPX}0}$wIt=`DJhgrHnKR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn%Qu HW;00uwr~;8 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/llm/__pycache__/types.cpython-313.pyc index 55dada439bdd4ffed81752181030409394b518a4..c48d691bcb17a20c5b69d812f936702828273ade 100644 GIT binary patch delta 66 zcmaFQ`G=GHGcPX}0}!a0uHDF8!K_iJpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IF9&;cg0JU}%=l}o! delta 53 zcmeyv`JR*eGcPX}0}$wIt=`C8!7QDkpPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(WsJ Hb08xCx`Yvm diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/__init__.cpython-313.pyc index 6c24af46310fb38824813e76bee0697a392c58af..83879cb7224802f1369da89a4e47656b560b23e8 100644 GIT binary patch delta 65 zcmX@de4Cm3GcPX}0}yDKuHDG3#i&uDpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS TR}!C`pI4Gt9B(u^fUyn$eHRrA delta 52 zcmcc3e2$s>GcPX}0}$wGt=`D3#VDPwpPN^rpORRTs2`u0l$e*2pBJB*S6q;qTrxS9 Gu?_%@(-5iv diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/client.cpython-313.pyc index 283f1c7b5bf4c3f65ecbddd23ef41986d4c22a9c..0fcf8a97bf0cf5f59be418a9c1000b9e414daef1 100644 GIT binary patch delta 68 zcmaFg&-kaGk^3_*FBbz4Xqc|u$i0wRqewqDuS7pZ-#6GZKAb>KSU)={B}qRq WJvFZ+J~=ra7%#uGcPX}0}yDKuHDG(!K6{7pPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;HakLf-u0H1dii_@% delta 53 zcmZou{i(wJnU|M~0SNT8R&V4MVUkYQ&&?~*Pf09E)Q?Y0O3X{i&x_B@D=tV)F4^qP HWG?^!pUe=7 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/mcp/__pycache__/tool_policy.cpython-313.pyc index a14478d4924f2820ea5b43aac71df8c798c092eb..679ede05199d3c22660234ce2f050ad893435e1b 100644 GIT binary patch delta 66 zcmZ3bvt5V#GcPX}0}yDKuHDF8#H3NKpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IF2Gb;A0Hlr;@c;k- delta 53 zcmdn4vr32iGcPX}0}$wGt=`C8#3Y@ipPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(Ws7 H(J1HeeKQTQu UuOvP>Kd&UQINoUUQ?6Pa0P`UilK=n! delta 53 zcmaE#_b8A1GcPX}0}$wGt=`DJpG!JRKR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn%Pv Hu38-c>S+@) diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/__init__.cpython-313.pyc index 05dbbc5ef3b96ec0f76f024c923a558b0cb56717..6a7f80bcd768d0cdcc03da35a8b51e818c5ad44b 100644 GIT binary patch delta 65 zcmaFD@sWf3GcPX}0}yDMuHDEzmsz7+KR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu TuOvP>Kd&UQINoUTVdjMZqCOT< delta 52 zcmey!@q~l>GcPX}0}$wIt=`BzmsvVXKR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn%MM G=7j*N7ZLFQ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_extractor.cpython-313.pyc index 13860610360b430090606b699bec83b85f61caab..d94ef3d5d929c788260b4c5ab48584e100859abf 100644 GIT binary patch delta 20 acmcaCd|8V diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_memory.cpython-313.pyc index 6aa97e0f7d88b0725af5ca09e21fc380909d4316..9f8133b7581babf65520f27f2a99d4cff66c6392 100644 GIT binary patch delta 20 acmdmKy3>^VGcPX}0}yDMuHDGJP6_}$+XapQ delta 20 acmdmKy3>^VGcPX}0}$NKS-g>ZofH5=(FSb* diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_models.cpython-313.pyc index 5f9e9dabfa56f9be696d4b70ab95780e9ca966eb..3037dbf93d39614602812c9e0346cd887a80ee99 100644 GIT binary patch delta 20 acmcb{dySX-GcPX}0}yDMuHDFenhgLvuLXes delta 20 acmcb{dySX-GcPX}0}$NKS-g?^G#dayr3PRC diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/long_term_store.cpython-313.pyc index 36f94e18d7657228d70ff731d0dc5ac07e738425..439d80f766c9d965d5f4bd2a5b06a0d7726753c7 100644 GIT binary patch delta 22 ccmZp9!r1bJk^3_*FBbz4Xqv9w$o;br08`}$CIA2c delta 22 ccmZp9!r1bJk^3_*FBbz4+|F6Nk^5&M09o4z1poj5 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/message_history.cpython-313.pyc index bcb131bd6c1d56568345a23d3f0c95a0e8debf2a..b8449b8f52e57f5f71bb86bffd4be7c7d3c20a7a 100644 GIT binary patch delta 66 zcmbQ^xYCjPGcPX}0}yDMuHDG}k5QveKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoTp3R8s~0Ks||4*&oF delta 53 zcmZ4KILDFuGcPX}0}$wIt=`D}k5Rf%KR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn#2; HQ-vG=!AcRX diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_memory.cpython-313.pyc index 65e00ceea8941f2efe07bd685c3ff6129075f0af..ccafa0cbe40b755148c8e4aa77964590f90a2757 100644 GIT binary patch delta 66 zcmcbd_#lz{GcPX}0}yDMuHDFelv$%zKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUUYvva^0Os}=%K!iX delta 53 zcmaEmcsY^#GcPX}0}$wIt=`Celv%n!KR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn%PX H<`+5u;K&nd diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/memory/__pycache__/summary_store.cpython-313.pyc index f3f49c1459100b8be9085bcc1d8fea55690da703..2562f26cb36c73d4bb13cd48da95faa1087b490b 100644 GIT binary patch delta 66 zcmZ4LwaJV7GcPX}0}yDMuHDEj%cN1GpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;JQndy-d0HK8y-T(jq delta 53 zcmdnwwbYCIGcPX}0}$wIt=`Bj%OstzpPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(UWU H>5&ous{0Xr diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/__init__.cpython-313.pyc index 9b41ef63527adcd275cd29fc2b5f92db7e40c2fb..23837c6fc10ac234ad61be590d2e4fc723556ae2 100644 GIT binary patch delta 63 zcmX@jc!QDqGcPX}0}yDKuARtjtWmC?n^&TrqVF5*86QxTpOuGcPX}0}$wIt)9qjES;sFn^&Trl30?cAD@_%n3s~D7oV9|T#%YvGBK?P E0CYqUh5!Hn diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/models/__pycache__/identity.cpython-313.pyc index c273cb37cddb0c8c6957b72811b15877625d35fa..d3d2aada3171203b552e4f320eeac21f611d408c 100644 GIT binary patch delta 20 acmeB{?w02M%*)Hg00bJQYd3N?@B#ob?*zgC delta 20 acmeB{?w02M%*)Hg00g&k7H{Nk-~|9U&&?~*Pf09E)Q?Y0O3X{i&x_B@D=tV)F4=6x HoWKqMgv}5J diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/__init__.cpython-313.pyc index 3095e2ed2ae70eaa8dbf7dbad2e8cb5df9cebaa2..bf56e96e121d63e7e2824fc1ff4a6d9e61295a63 100644 GIT binary patch delta 20 acmeyu^@WT3GcPX}0}!a2uHDF;$qE2J9bGcPX}0}!a2uHDFeQ3e1**9HFo delta 20 acmca(dB>9bGcPX}0}$NIS-g?^q6`2@%?928 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/event_bus.cpython-313.pyc index 4dd047fb91b2d6efa7c79b50c948ce9b7589155a..88fdd0d5ebc5728da6d8a59d89aaa93a6d45fd15 100644 GIT binary patch delta 20 acmdlavq^^gGcPX}0}!a2uHDG3&IbTEB?P$u delta 20 acmdlavq^^gGcPX}0}$NIS-g>3oeuy$8wHpE diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/guardrail_events.cpython-313.pyc index f8b4db195dd31bac9b6eefce0875c8b713dcbc72..54c0264593e63bd8060aafd44492ba66eed9e7a7 100644 GIT binary patch delta 20 acmZn>Xc6H4%*)Hg00io$Yd3QLWCs8-J_Qf} delta 20 acmZn>Xc6H4%*)Hg00cL47H{PK$qoQFGzISf diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/ic_events.cpython-313.pyc index 1737199e4b5233b676c5d1f31b100a1a2eb74c8f..6d0d5f59287bb6c8b3c61c51081ea5e53c152348 100644 GIT binary patch delta 20 acmey&@|lJEGcPX}0}!a2uHDF;!2|$4;RQed delta 20 acmey&@|lJEGcPX}0}$NIS-g=ug9!jc*9IQ| diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/judge_events.cpython-313.pyc index 0adfa30d5d1e1d2de246e65447c1d107768a06e3..7aaf3326e5348a14706faccf92ec94ceb93e8bdc 100644 GIT binary patch delta 20 acmZqSZsF$s%*)Hg00io$Yd3ONvH}1vhy;WH delta 20 acmZqSZsF$s%*)Hg00cL47H{OPWCZ{~VGcPX}0}!a2uHDGJLIMChO9gNM delta 20 acmdmNxY>~VGcPX}0}$NIS-g>Zg#-XXK?Y9% diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/observer.cpython-313.pyc index 8b8e1e10fb2d3575334785f6caa0d3bd4ec73275..9b8ac37bfc661ac2bbac1f24fbc0d594aef27e3c 100644 GIT binary patch delta 20 acmeyQ{z;wtGcPX}0}!a2uHDG}To?dEwgwac delta 20 acmeyQ{z;wtGcPX}0}$NIS-g?^xiA1qtOoM{ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/otel.cpython-313.pyc index ef635b56ecc909f82a7e9ec76afaa0d4d64b5a3b..ef4d76febe2e05d6342db1b8a293f3cd924e63c9 100644 GIT binary patch delta 20 acmcaAbybS{GcPX}0}!a2uHDG(#R~vER0SOX delta 20 acmcaAbybS{GcPX}0}$NIS-g?kix&VxN(KA? diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/streaming_events.cpython-313.pyc index a877ac2ca0893d39169c0acfc4f2dc7745a3f309..209e518930f35da9d2c0ce8ea47e51716bb25c55 100644 GIT binary patch delta 20 acmZ3(w}y}VGcPX}0}!a2uHDG3$PNHCHUxwK delta 20 acmZ3(w}y}VGcPX}0}$NIS-g>3ksSa#ECpi# diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/telemetry.cpython-313.pyc index 6d765162bf91ab87aadf217533eb9669df2477f5..2518acb9a21b55c8f312b4c3f99c7fe69fa171de 100644 GIT binary patch delta 22 ccmX>*llkmSX710tyj%=Gpl-T$Bln>*08~;3NB{r; delta 22 ccmX>*llkmSX710tyj%=Ga5HD|M(#sr09r^0CjbBd diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/token_cost.cpython-313.pyc index 2446025531d61ad41fb114c373262163e5497d3b..374c66c0fafa53b8121bfb40c23a25c58b8f1b19 100644 GIT binary patch delta 20 acmez3{Kc93GcPX}0}!a2uHDG}QV{@21O{LL delta 20 acmez3{Kc93GcPX}0}$NIS-g?^r6K@M`UgJ% diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/observability/__pycache__/workflow_events.cpython-313.pyc index f503e3a187b4041fb9b46c426f795c2fe4559c33..73fd59ea7f916c902fa03aa7bcc42a6a0384720f 100644 GIT binary patch delta 20 acmZpcYL?>u%*)Hg00io$Yd3QL-~j+Kg9Ro4 delta 20 acmZpcYL?>u%*)Hg00cL47H{PK!2c$bm;GcPX}0}yDMuARtjtWl$%n^&TrqVF5*86QxTpOu@~ delta 22 ccmaF+jOpz&ChpI?yj%=Ga4To=M((7|0BS4=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;I_i&4}P0KsGy0ssI2 delta 53 zcmZ2eI=__rGcPX}0}vQ!t=`C;!YEy=pPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(WsO Hqo^eS!+sH> diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/__init__.cpython-313.pyc index 6f0c89e4d316b87231c641d658fbe6c99f1267b9..7849c05a935654cf954eaf26dc3da056219260fa 100644 GIT binary patch delta 20 acmX@Xe1e(#GcPX}0}!a2uHDGJmk|It1O+?* delta 20 acmX@Xe1e(#GcPX}0}$NGS-g>ZFCzdy0R|iZ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/presentation/__pycache__/renderers.cpython-313.pyc index 1968a29db873f5970f1124e908c34c17429c245b..b6c3eb30aad6ac6035a129160b7455493203195e 100644 GIT binary patch delta 20 acmcbwcwdqGGcPX}0}!a2uHDFeMF0Rm;|0tB delta 20 acmcbwcwdqGGcPX}0}$NGS-g?^iU0sc;0CM! diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/__init__.cpython-313.pyc index c9d635ac0b00452d49841cad12e6ca3c124e6e88..83b55a515bfd33a128b65dd93f48fa56a7a84e63 100644 GIT binary patch delta 20 acmX@kdYqN}GcPX}0}yDKuHDGJhY0{VECoLR delta 20 acmX@kdYqN}GcPX}0}$NGS-g>Z4-)`CA_g7+ diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/embedding_provider.cpython-313.pyc index bdf965d5ea2e7e233093c29a6562ab0a5bcbf657..1817712bb8c881209220775ce4b8a495c608d3d1 100644 GIT binary patch delta 20 acmexn^v#I-GcPX}0}yDKuHDF;EeQZc?*+X8 delta 20 acmexn^v#I-GcPX}0}$NGS-g=uTM__ET?O+1 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/ingest.cpython-313.pyc index 835fcdfa4cbfdcfde21b0100f28f6e51a169d579..bdfb59699bef8ad559da69c4fec801cea320093a 100644 GIT binary patch delta 20 acmZ3Mv@D7HGcPX}0}yDKuHDEjZ3qBBM+HRy delta 20 acmZ3Mv@D7HGcPX}0}$NGS-g>3+7JLnJq9EI diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/kbdb_service.cpython-313.pyc index eaaa2b5808805ad35294bf3867308d9f36f546bc..72b822106729185a4460e4ec597b84217ec72e2a 100644 GIT binary patch delta 5695 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 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/rag/__pycache__/vector_store.cpython-313.pyc index 0472c07d04ebc84bd833839d1ca33901a2beb67b..2903d0c825702d72ebbba6c8d86717b92ef54178 100644 GIT binary patch delta 22 ccmaDjk@4X~M()qNyj%=GpkcapBlk5A09CIB?EnA( delta 22 ccmaDjk@4X~M()qNyj%=Ga4To=M(%4K09&O8%m4rY diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/__init__.cpython-313.pyc index f326045174332481c69cffdab0c7d746162e1bd1..11ca557ed2c87504a1b1575d4be72b31d1cd4aab 100644 GIT binary patch delta 19 Zcmcb|c#o0$GcPX}0}yDKuARtz82~m|1zG?A delta 19 Zcmcb|c#o0$GcPX}0}$NISv-;ZG5|Yi1~>o! diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/repositories/__pycache__/session_repository.cpython-313.pyc index ae555f559a2fe60a0c521705d876a042da6dafd2..c97830a49e3fcb0375ae1d8701732bc8df8aecc4 100644 GIT binary patch delta 20 acmeBo?sw+?%*)Hg00bJQYd3PYC;|XELj^Jb delta 20 acmeBo?sw+?%*)Hg00cL47H{NkQ3L=#G6oO; diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/__init__.cpython-313.pyc index a0dd31bbb30658b3e8e73987641c509a5da659b3..96c6acb132e92eca605f994a7ddc2722e1ad9f9c 100644 GIT binary patch delta 65 zcmeyv(#gvGnU|M~0SHu0*KXuK#i&uCpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS TR}!C`pI4Gt9B(xF17i;Wl(iN) delta 52 zcmeBV{lmijnU|M~0SFAVR&V4!#VDPvpPN^rpORRTs2`u0l$e*2pBJB*S6q;qTr&AD GV-EnHwh`R` diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/config_loader.cpython-313.pyc index 24f2fc4436fab85a7cfac4e6aeef060bfba7654a..164b46f73377cf750028a25d15a7f49f5c4de243 100644 GIT binary patch delta 66 zcmew+(k#mTnU|M~0SHu0*KXvV$*57QpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IFKjU@|0H@6sLjV8( delta 53 zcmZn`{U*ZwnU|M~0SFAVR&V5<$tYc*pPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(bEL H<8}@JuBs8| diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/continuity.cpython-313.pyc index f0b371f4fa619675b1b4350c87cb5c397756af91..ab56bdb0a56ad4e851c76a48fabf78a225e25f4f 100644 GIT binary patch delta 66 zcmdl~v%iM>GcPX}0}!a1uHDF8!K6{8pPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;IF9@7+S0MY6e*8l(j delta 53 zcmdmAv#EyrGcPX}0}vQ!t=`C8!6colpPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(WsJ H(-dm}(Qy)c diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/enterprise_router.cpython-313.pyc index e3153e77fbc0940351addaa6f6609b7a2863bc2c..f8c401b745b7a5c3f0ce9f3f31cb8c5c6d470733 100644 GIT binary patch delta 13736 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 N`O6G}+z{yWe*k*xDIEX+ delta 8735 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 UD~V6e&nrnRjyKxu!)(q50CRH`ZvX%Q delta 53 zcmbOr+$_xfnU|M~0SFAVR&V51W0ua;&&?~*Pf09E)Q?Y0O3X{i&x_B@D=tV)F4-K( HY|aJ%hPx0G diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/__pycache__/multi_intent.cpython-313.pyc index ddb9f08bb9d2a6470a36ca3746bccb5671540431..4e496180f12cd23961b9b46a30bb32934a5a674f 100644 GIT binary patch delta 68 zcmbO~k8#aBM()qNyj%=GplZ5yBX=^hMvZ=MUWtB+zHhK+d_YltR%&udv3_<^N|JtJ WdTL%td~$wXNn&xl(dJI(Y2Ez>% delta 53 zcmZpx`&rBVnU|M~0SHoTS8wEIVU{k`&&?~*Pf09E)Q?Y0O3X{i&x_B@D=tV)F4=6( HeBK%W&esww diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py index 45f384d9..f6ec8a73 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py @@ -630,58 +630,144 @@ class EnterpriseRouter: if state_decision: tx_status = str(state.get("transaction_status") or "").strip().upper() - # Explicit abandonment must be evaluated before confirmation - # consumption. Otherwise an utterance such as - # "não quero cancelar; quero apenas consultar meus serviços" can be - # trapped by the confirmation state and never reach ABANDON. A plain - # yes/no still keeps deterministic confirmation precedence because it - # does not pass the abandonment gate. - if tx_status == "AWAITING_CONFIRMATION": - if self._looks_like_explicit_abandonment(str(text)): + active_tx = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} + legacy_tx = state.get("pending_tool_call") or state.get("selected_tool_call") or {} + state_lock_name = str( + state_decision.next_state + or current_state + or state_decision.intent.removeprefix("state:") + or "" + ).strip().upper() + transactional_state_lock = state_lock_name.startswith( + ("WAITING_", "COLLECTING_", "AWAITING_") + ) + + # Rich transactional latches remain the primary source of truth. + # The domain state lock is only a recovery fallback for checkpoints + # where those latches were not restored. This prevents a generic + # COLLECTING_* state from stealing a legitimate parameter answer. + rich_transaction_context = bool( + active_tx.get("tool_name") + or (isinstance(legacy_tx, dict) and legacy_tx.get("tool_name")) + or tx_status in {"COLLECTING_PARAMETERS", "AWAITING_CONFIRMATION"} + ) + fallback_state_lock_context = bool( + transactional_state_lock and not rich_transaction_context + ) + has_transaction_context = bool( + rich_transaction_context or fallback_state_lock_context + ) + + # Some hosts restore the domain state but omit transaction_status. + # Infer only the generic phase needed by the precedence logic; do not + # replace richer transaction data when it exists. + effective_tx_status = tx_status + if not effective_tx_status: + if state_lock_name.startswith("COLLECTING_"): + effective_tx_status = "COLLECTING_PARAMETERS" + elif state_lock_name.startswith("AWAITING_") or "CONFIRMATION" in state_lock_name: + effective_tx_status = "AWAITING_CONFIRMATION" + + precedence_state = state + if effective_tx_status and effective_tx_status != tx_status: + precedence_state = dict(state) + precedence_state["transaction_status"] = effective_tx_status + + # Transactional interruption precedence is semantic, not lexical. + # + # COLLECTING_PARAMETERS is special: the pending parameter gets the + # first opportunity to consume the turn. If the input is pertinent + # to the parameter, extraction returns values and the transaction + # continues. Only when the input is not pertinent do we ask the + # semantic interruption classifier whether the user abandoned the + # active transaction. This preserves inputs such as + # ``motivo é que desisti`` as a legitimate ``reason`` value while + # still allowing ``não quero mais devolver; quero ver meus serviços`` + # to abandon the current operation and start a new objective. + if effective_tx_status == "COLLECTING_PARAMETERS": + if rich_transaction_context: + relevance = await self._classify_transaction_parameter_relevance( + precedence_state, text=str(text) + ) + if relevance is True: + consumed = await self._transaction_parameter_precedence( + precedence_state, text=str(text), state_decision=state_decision + ) + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), + "transaction_parameter_relevant": True, + } + await self._emit(consumed, state) + return consumed + + # Pertinent to the requested parameter, but extraction did + # not produce a structured value. Keep transaction + # ownership and let the agent reprompt/clarify; do not + # reinterpret the same input as ABANDON or SHIFT. + state_decision.metadata = { + **(state_decision.metadata or {}), + "transaction_parameter_relevant": True, + "transaction_parameter_extraction_empty": True, + } + await self._emit(state_decision, state) + return state_decision + + if relevance is None: + # Compatibility/fail-safe when the relevance classifier is + # unavailable: retain the previous extractor-first behavior. + consumed = await self._transaction_parameter_precedence( + precedence_state, text=str(text), state_decision=state_decision + ) + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), + "transaction_parameter_relevance": "unknown", + } + await self._emit(consumed, state) + return consumed + + if has_transaction_context: abandonment = await self._transaction_state_interruption_candidate( - state, + precedence_state, text=str(text), state_decision=state_decision, abandon_only=True, ) if abandonment is not None: + abandonment.metadata = { + **(abandonment.metadata or {}), + "transaction_parameter_relevant": False if rich_transaction_context else None, + } await self._emit(abandonment, state) return abandonment - consumed = await self._transaction_parameter_precedence( - state, text=str(text), state_decision=state_decision + # In confirmation/waiting phases there is no pending free-form + # parameter whose value can be confused with abandonment. Evaluate + # ABANDON semantically before confirmation/state-lock consumption. + # Plain confirmations (e.g. yes/no) are classified as CONTINUE by + # this probe and then follow the normal confirmation path below. + elif has_transaction_context: + abandonment = await self._transaction_state_interruption_candidate( + precedence_state, + text=str(text), + state_decision=state_decision, + abandon_only=True, ) - if consumed is not None: - await self._emit(consumed, state) - return consumed - - # During parameter collection, explicit abandonment has stronger - # precedence than parameter extraction. Otherwise an LLM parameter - # extractor can incorrectly "consume" phrases such as - # "esquece, não quero mais cancelar" and the ABANDON classifier is - # never reached. SHIFT remains after parameter extraction so a valid - # value/name/reference answer is not stolen by a plausible new intent. - if tx_status == "COLLECTING_PARAMETERS": - if self._looks_like_explicit_abandonment(str(text)): - abandonment = await self._transaction_state_interruption_candidate( - state, - text=str(text), - state_decision=state_decision, - abandon_only=True, - ) - if abandonment is not None: - await self._emit(abandonment, state) - return abandonment + if abandonment is not None: + await self._emit(abandonment, state) + return abandonment + if effective_tx_status == "AWAITING_CONFIRMATION": consumed = await self._transaction_parameter_precedence( - state, text=str(text), state_decision=state_decision + precedence_state, text=str(text), state_decision=state_decision ) if consumed is not None: await self._emit(consumed, state) return consumed interruption = await self._transaction_state_interruption_candidate( - state, text=str(text), state_decision=state_decision + precedence_state, text=str(text), state_decision=state_decision ) if interruption is not None: await self._emit(interruption, state) @@ -712,49 +798,61 @@ class EnterpriseRouter: method="state", next_state=tx_status, ) - if tx_status == "AWAITING_CONFIRMATION": - if self._looks_like_explicit_abandonment(str(text)): - abandonment = await self._transaction_state_interruption_candidate( - state, - text=str(text), - state_decision=synthetic, - abandon_only=True, - ) - if abandonment is not None: - abandonment.metadata = { - **(abandonment.metadata or {}), - "transaction_state_recovered": True, - } - await self._emit(abandonment, state) - return abandonment - - consumed = await self._transaction_parameter_precedence( - state, text=str(text), state_decision=synthetic - ) - if consumed is not None: - consumed.metadata = { - **(consumed.metadata or {}), - "transaction_state_recovered": True, - } - await self._emit(consumed, state) - return consumed - if tx_status == "COLLECTING_PARAMETERS": - if self._looks_like_explicit_abandonment(str(text)): - abandonment = await self._transaction_state_interruption_candidate( - state, - text=str(text), - state_decision=synthetic, - abandon_only=True, + relevance = await self._classify_transaction_parameter_relevance( + state, text=str(text) + ) + if relevance is True: + consumed = await self._transaction_parameter_precedence( + state, text=str(text), state_decision=synthetic ) - if abandonment is not None: - abandonment.metadata = { - **(abandonment.metadata or {}), + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), "transaction_state_recovered": True, + "transaction_parameter_relevant": True, } - await self._emit(abandonment, state) - return abandonment + await self._emit(consumed, state) + return consumed + synthetic.metadata = { + **(synthetic.metadata or {}), + "transaction_state_recovered": True, + "transaction_parameter_relevant": True, + "transaction_parameter_extraction_empty": True, + } + await self._emit(synthetic, state) + return synthetic + if relevance is None: + consumed = await self._transaction_parameter_precedence( + state, text=str(text), state_decision=synthetic + ) + if consumed is not None: + consumed.metadata = { + **(consumed.metadata or {}), + "transaction_state_recovered": True, + "transaction_parameter_relevance": "unknown", + } + await self._emit(consumed, state) + return consumed + + # No parameter was consumed (or this is confirmation): now decide + # semantically whether the user abandoned the active transaction. + abandonment = await self._transaction_state_interruption_candidate( + state, + text=str(text), + state_decision=synthetic, + abandon_only=True, + ) + if abandonment is not None: + abandonment.metadata = { + **(abandonment.metadata or {}), + "transaction_state_recovered": True, + } + await self._emit(abandonment, state) + return abandonment + + if tx_status == "AWAITING_CONFIRMATION": consumed = await self._transaction_parameter_precedence( state, text=str(text), state_decision=synthetic ) @@ -893,6 +991,82 @@ class EnterpriseRouter: return decision + async def _classify_transaction_parameter_relevance( + self, state: dict[str, Any], *, text: str + ) -> bool | None: + """Semantically decide whether the turn answers a pending parameter. + + This is intentionally separate from extraction. A parameter extractor + may be able to manufacture a syntactically valid value from unrelated + text; relevance answers the higher-level question first: does the user + appear to be responding to what the active transaction asked for? + + Returns True/False when classification succeeds and None when the + classifier is unavailable or its output cannot be trusted. + """ + if not (self.enable_llm_router and self.llm is not None): + return None + missing = [str(name) for name in (state.get("missing_parameters") or []) if str(name).strip()] + if not missing: + return None + active = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} + tool_name = str(active.get("tool_name") or "").strip() + if not tool_name: + return None + payload = { + "message": str(text or ""), + "transaction": { + "intent": active.get("started_from_intent") or state.get("intent"), + "tool_name": tool_name, + "tool_description": active.get("tool_description"), + "missing_parameters": missing, + "parameter_schema": active.get("parameter_schema") or {}, + "known_arguments": active.get("arguments") or {}, + "relevant_conversation_context": ( + active.get("parameter_conversational_context") + or self._collect_transaction_parameter_context(state=state, current_text=str(text)) + ), + }, + } + system = ( + "Você decide somente se a mensagem do usuário é uma resposta pertinente ao(s) " + "parâmetro(s) que a transação ativa está pedindo. Avalie significado e contexto, " + "não palavras isoladas. Considere RELEVANT quando a mensagem fornece, esclarece, " + "corrige ou referencia plausivelmente o dado solicitado, mesmo em linguagem livre. " + "Considere NOT_RELEVANT quando a mensagem abandona a ação, inicia outro objetivo, " + "faz uma pergunta diferente ou não responde ao dado pedido. Não extraia valores e " + "não decida abandono/intent; classifique apenas pertinência. Retorne somente JSON " + "válido: {\"relevance\":\"RELEVANT|NOT_RELEVANT\",\"confidence\":0.0,\"reason\":\"...\"}." + ) + try: + answer = await self.llm.ainvoke( + [ + {"role": "system", "content": system}, + {"role": "user", "content": json.dumps(payload, ensure_ascii=False)}, + ], + temperature=0.0, + max_tokens=256, + profile_name="router", + component_name="router.transaction_parameter_relevance", + generation_name="llm.transaction_parameter_relevance", + ) + data = self._parse_json(answer) + except Exception as exc: + logger.warning("Falha ao classificar pertinência do parâmetro transacional: %s", exc) + return None + relevance = str(data.get("relevance") or "").strip().upper() + try: + confidence = float(data.get("confidence") or 0.0) + except (TypeError, ValueError): + return None + if confidence < self.intent_shift_threshold: + return None + if relevance == "RELEVANT": + return True + if relevance == "NOT_RELEVANT": + return False + return None + async def _transaction_parameter_precedence( self, state: dict[str, Any], @@ -1017,29 +1191,6 @@ class EnterpriseRouter: } return state_decision - @staticmethod - def _looks_like_explicit_abandonment(text: str) -> bool: - """Cheap, domain-neutral gate for semantic ABANDON classification. - - This is deliberately not the final decision. It only protects clear - abandonment utterances from being swallowed by parameter extraction; the - semantic classifier still decides whether the turn is actually ABANDON. - """ - import re - normalized = str(text or "").strip().lower() - if not normalized: - return False - cues = ( - r"\besquece(?:\s+isso|\s+essa|\s+esse|\s+tudo)?\b", - r"\bn[aã]o\s+quero\s+mais\b", - r"\bdesisti(?:\s+disso|\s+dessa|\s+desse)?\b", - r"\bdeixa\s+(?:isso|essa|esse|pra\s+l[aá]|para\s+l[aá])\b", - r"\bpode\s+parar\b", - r"\bquero\s+parar\b", - r"\bcancela\s+essa\s+solicita[cç][aã]o\b", - ) - return any(re.search(pattern, normalized, flags=re.IGNORECASE) for pattern in cues) - async def _transaction_state_interruption_candidate( self, state: dict[str, Any], @@ -1050,10 +1201,10 @@ class EnterpriseRouter: ) -> RouteDecision | None: """Detecta semanticamente mudança de intenção durante uma transação. - Não existe lista de palavras para desistência ou mudança de assunto. Uma - interrupção nasce de uma intent diferente resolvida por uma keyword - configurada no ``routing.yaml`` ou, na ausência dela, por uma decisão - semântica do LLM com o contexto da transação pendente. + Não existe lista de palavras para desistência. ABANDON é sempre uma + decisão semântica do LLM baseada no contexto transacional. Uma possível + nova intenção pode aparecer como hint do ``routing.yaml``, mas esse hint + não decide abandono e não substitui a classificação semântica. """ active_tx = state.get("active_transaction") if isinstance(state.get("active_transaction"), dict) else {} started_intent = str(active_tx.get("started_from_intent") or "").strip() @@ -1067,7 +1218,17 @@ class EnterpriseRouter: or (started_intent and configured_candidate.intent != started_intent) or (previous_intent and not previous_intent.startswith("state:") and configured_candidate.intent != previous_intent) ) - if not different: + + # A configured candidate that resolves to the same intent/agent is not + # sufficient to conclude that the active transaction continues. The + # user may be starting a *new instance of the same intent* with a + # different transactional target (for example cancelling TIM Fashion + # while Tamboro is awaiting confirmation). In LLM mode keep the + # candidate only as a semantic hint and let the transaction classifier + # decide CONTINUE/REPLACE/ABANDON. Without semantic classification we + # retain the legacy state lock because guessing replacement from a + # keyword alone would be unsafe. + if not different and not (self.enable_llm_router and self.llm is not None): return None # During parameter collection, a configured keyword may be present in @@ -1108,6 +1269,11 @@ class EnterpriseRouter: "current_intent": started_intent or previous_intent, "transaction_status": state.get("transaction_status"), "tool_name": active_tx.get("tool_name"), + # Structured arguments are part of the identity of the active + # transaction. They let the semantic classifier distinguish a new + # instance of the same intent from a continuation without relying on + # domain keywords or hard-coded product names. + "active_arguments": dict(active_tx.get("arguments") or {}), "missing_parameters": list(state.get("missing_parameters") or []), "configured_candidate": ( { @@ -1122,6 +1288,8 @@ class EnterpriseRouter: if abandon_only: system = ( "Você decide somente se o usuário desistiu explicitamente da ação/transação ativa. " + "Quando a transação estava coletando parâmetros, a pertinência ao parâmetro já foi avaliada antes desta etapa; " + "portanto não transforme uma resposta pertinente ao parâmetro em abandono. " "Use o significado completo da mensagem e o contexto transacional; não use palavras isoladas como regra. " "Se houver desistência explícita da ação atual, retorne ABANDON. " "Quando o abandono vier acompanhado de um novo objetivo, também informe intent e agent desse novo objetivo. " @@ -1132,11 +1300,15 @@ class EnterpriseRouter: ) else: system = ( - "Você decide apenas se o turno atual continua a transação ativa ou muda de intenção. " - "Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " + "Você decide apenas se o turno atual continua a transação ativa, substitui a instância transacional ativa, " + "ou muda de intenção. Use o significado da mensagem e o contexto transacional; não use palavras isoladas como regra. " "A extração dos parâmetros pendentes já foi tentada antes desta etapa e não consumiu o turno. " "Se ainda assim a mensagem for apenas uma resposta referencial/valor/nome ao dado pendente, retorne CONTINUE. " - "Se o usuário passou claramente a perseguir outro objetivo sem desistir explicitamente da ação atual, " + "Se o usuário continua com a MESMA intent, mas claramente inicia outra instância da operação com alvo/argumentos diferentes, " + "retorne REPLACE e informe a mesma intent e agent. REPLACE significa substituir a transação ativa; não significa estado novo. " + "Não use REPLACE quando o usuário apenas complementa/adiciona itens à mesma operação (por exemplo 'também', 'além desses'), " + "nem quando apenas reformula ou confirma o mesmo alvo: nesses casos retorne CONTINUE. " + "Se o usuário passou claramente a perseguir outro objetivo/intent sem desistir explicitamente da ação atual, " "retorne SHIFT e a nova intent permitida. " "Se o usuário desistiu explicitamente da ação/transação atual, retorne ABANDON. " "Quando ABANDON vier acompanhado de um novo objetivo, também informe intent e agent desse novo objetivo. " @@ -1170,7 +1342,7 @@ class EnterpriseRouter: if abandon_only: if decision_kind != "ABANDON": return None - elif decision_kind not in {"SHIFT", "ABANDON"}: + elif decision_kind not in {"SHIFT", "REPLACE", "ABANDON"}: return None confidence = float(data.get("confidence") or 0.0) if confidence < self.intent_shift_threshold: @@ -1221,7 +1393,42 @@ class EnterpriseRouter: mcp_tools=mcp_tools, ) - if not intent_name or intent_name == (started_intent or previous_intent): + current_intent = started_intent or previous_intent + if not intent_name: + return None + + # Same-intent replacement is a transaction lifecycle event, not a new + # conversation state. It cancels only the active transaction instance + # and re-enters normal routing for the same configured intent. + if decision_kind == "REPLACE": + if not current_intent or intent_name != current_intent: + return None + agent = agent or str(self._agent_for_intent(intent_name) or "").strip() + if not agent: + return None + return RouteDecision( + route=agent, + agent=agent, + intent=intent_name, + confidence=confidence, + reason=str(data.get("reason") or "Nova instância transacional da mesma intenção."), + method="llm", + metadata={ + "transaction_interruption": "same_intent_replacement", + "interrupted_state": state_decision.next_state, + "interrupted_agent": state_decision.agent, + "interrupted_intent": current_intent, + "interruption_source": "semantic_classifier", + "configured_routing_hint": ( + configured_candidate.intent if configured_candidate is not None else None + ), + "raw_llm_answer": answer[:1000], + }, + domain=self._domain_for_intent(intent_name), + mcp_tools=self._tools_for_intent(intent_name), + ) + + if intent_name == current_intent: return None agent = agent or str(self._agent_for_intent(intent_name) or "").strip() if not agent: diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/__init__.cpython-313.pyc index 8e3def81d3192da80ea3385b0baa1bba7a41184e..ac1069dc81a17bf1c4e4f568f648cb806babb0ed 100644 GIT binary patch delta 63 zcmcb}bdQPqGcPX}0}yDKuARueR--~cH?KrLMc+5rGd`dwKPxr4q*y;YDJ4liF+DY} RBtAJmuOzWJ-e}_GIskWP74-lB delta 50 zcmcb|bdibsGcPX}0}vQ!t)9reRytchH?KrLC9xz?KRz)jF)t-QFFrG`xF9vTWa5K5 E0EcrCmH+?% diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/agent_runtime.cpython-313.pyc index 468fe737ded49664ec99c88fe969e5d3ab6f6d37..61dadcc3d9dbf487bede12c43b15d7b51a6f8cff 100644 GIT binary patch delta 6266 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 literal 0 HcmV?d00001 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/__pycache__/transaction_parameters.cpython-313.pyc index 6286ad47897d0b55ae3be4118f08b27f15806d57..75b492e0773bbe75e51173445a83331cee0ee67b 100644 GIT binary patch delta 68 zcmew|gRyZYBll-sUM>b8&@f%Qk=un?qfI|IuS7pZ-#6GZKAb>KSU)={B}qRq WJvFZ+J~=b8Fwk1Pk=un?x>7$kuS7p3u_RGHJ~1gVFC{-OJ~OYlAT_yU Jb2&4YCji;`5##^> diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py index 3fa365af..dbae2498 100644 --- a/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py +++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py @@ -3113,21 +3113,33 @@ class AgentRuntimeMixin: self._normalize_transaction_lifecycle(state) # Uma transação em coleta/confirmação não pode aprisionar a sessão. O - # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT ou - # ABANDON. Não existe interpretação lexical de desistência no runtime; o - # runtime apenas aplica a decisão do router e limpa os latches da ação. + # EnterpriseRouter é a única fonte para interrupção semântica: SHIFT, + # ABANDON ou replacement da instância transacional na mesma intent. Não + # existe interpretação lexical de desistência no runtime; o runtime apenas + # aplica a decisão do router e limpa os latches da ação. active_before_interruption = self._active_transaction(state) interruption = str(route_meta.get("transaction_interruption") or "").strip().lower() - if active_before_interruption and interruption in {"intent_shift", "explicit_abandonment"}: + if active_before_interruption and interruption in { + "intent_shift", + "explicit_abandonment", + "same_intent_replacement", + }: interrupted_tool = active_before_interruption.get("tool_name") self._finish_active_transaction(state, "CANCELLED") state["transaction_pre_validation"] = None + # Generic framework boundary: consumers do not need to know why the + # previous transactional context was invalidated. This deliberately + # avoids leaking same_intent_replacement (or any other interruption + # subtype) into domain agents/workflows. + state["operational_context_reset"] = True + if interruption == "explicit_abandonment": + action = "cancelled_by_explicit_abandonment" + elif interruption == "same_intent_replacement": + action = "cancelled_by_same_intent_replacement" + else: + action = "cancelled_by_intent_shift" state["tool_policy_result"] = { - "action": ( - "cancelled_by_explicit_abandonment" - if interruption == "explicit_abandonment" - else "cancelled_by_intent_shift" - ), + "action": action, "tool_name": interrupted_tool, } diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/__init__.cpython-313.pyc index 26baa6268843892ba0f4e94b6f142d4d6df77c0d..4758e940d7689f4c44d92180adf3f58c424bae66 100644 GIT binary patch delta 19 Zcmcc0c$Ja+GcPX}0}yDKuARtz3IH_=1wQ}) delta 19 Zcmcc0c$Ja+GcPX}0}$NKSv-;Z6aYFX1|I+b diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/sse/__pycache__/events.cpython-313.pyc index c1716a172603309056589f5fea3bce23581dd11e..61119b566612ee11762b72e2fc85071c7dbd6751 100644 GIT binary patch delta 20 acmezB_0@~}GcPX}0}yDKuHDF;r3wH^GX?el delta 20 acmezB_0@~}GcPX}0}$NKS-g=uOBDc5Fb38D diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/supervisor/__pycache__/__init__.cpython-313.pyc index 568462bf6ba90c3d66770d4fe20db1309f16203f..73d0402c7d099c66766f3b669dc2d51c6a652833 100644 GIT binary patch delta 19 Zcmcb^c!!buGcPX}0}yDKuARtz5dbz01yle4 delta 19 Zcmcb^c!!buGcPX}0}$NKSv-;ZA^VgqR+|;)yQeFbS9aYP5NPcHz7V9eKHAnv12Ss#YrNHG9d|ZVe#&cC=r3^oWry zP=JeJ02X+}9R-*_;dc}Nsendco#dD}>8h7l zj+J4s30e`5pMpDAX5QX^pWW{n*~bsIix0mQjh&*ghpR8qzL5N$${YyiNDnhKKLi9Q zkpbQW+4>WkWa*dS2pnnt3QnRpuSvQ%WI6DqHg9)MK-f=TNjD|e=C|yUt&?H;L%IO3 z(Z5n(*i9bejyxTb`(TdF$w`=RR^_k0Q`ecUcC=dtN;PXuC#>mobO>s6H9DSDeQuxj zQShCq0hh1$)3t!Ip*&Y+>owiYSr^%vo(N8W=yrqD9K^%SGtB$5i)^p`Pxx_V$@a{1 zUQ9*g2Inyjt|~Cb+%sNAd*}DOIE}^Pu#=`^eG!#wmpSA(%+f+EeP@Bu7vRtr@(Zm5 z7`wVTvmGDZL*p$0Ou<{QB^LLg@jGI5Qb1?GVVku6|NxQ{Tl3Gzx zq&k_6(|}QRvpJ_di<}D3h9Z6tp$a5!@uel^qieH}5WU>{%3O7h!ks*lCpPb3B!l*X6k6(&WYx7F}Q%s`fKygjx zBCuYz#JuA2)FPn$9YWfSA(OWVS=1MafD|zLX|fe*fQ;Y<5g-$Pai?S^mnhUI_~qxN z-eN0CEh#O^150bN6xo7Q34sW25WxW?fCjjO*l+`iykPQG0wtvdIjPY}`T03I3dJQw zv6|dPq99qZ$p?gGxwSw{H4x!3`K_>;lqQhb&hU|mla=Fx2!pw0gYN}l(xY%&=BM}{5dq$a03_zkt7AOV)TvTjU diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/__init__.cpython-313.pyc index 7ebd04d55311f3cdc6f56d5ed81753a6fc477790..e036addd226b811fc346a1efb857db379ff97cc4 100644 GIT binary patch delta 65 zcmaFN_JNK2GcPX}0}yDKuHDEzpGl)iKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu TuOvP>Kd&UQINoUTQKoqSp+go; delta 52 zcmeys_LzPx# delta 53 zcmeC={=>!nnU|M~0SFAVR&V5XW0ua+&&?~*Pf09E)Q?Y0O3X{i&x_B@D=tV)F4IrDGcPX}0}yDKuHDGJj#;BYKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUU73Oj|0M8s2)&Kwi delta 53 zcmccNa@vLaGcPX}0}vQ!t=`DJj#;``KR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn%Q0 H=5jdz&JhxK diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/models.cpython-313.pyc index f8e30f6b43869ac6c3d037eaeb375eeca23aba2e..ab8b87d38ba5deaeb13bde96c5ab3019e133bd17 100644 GIT binary patch delta 66 zcmaE5{oR`TGcPX}0}yDKuHDG}iAkeEKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoTp1oI^+0OLg#*Z=?k delta 53 zcmexv{mPpAGcPX}0}vQ$t=`D}iAg$JKR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn#2{ H^Cc+&-6s-y diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/registry.cpython-313.pyc index 1b0c21f3fd9c04de213e142a87a6d4dd1a3c6374..0dbba318bb15f6fc0377e97e77b56b0ed1d6fcac 100644 GIT binary patch delta 66 zcmZ1{v|EV#GcPX}0}yDKuHDFeiczCVKR2&LKSkd+*fTz$C_gJTxujS>J1HeeKQTQu UuOvP>Kd&UQINoUU2Sz(~0IL@k761SM delta 53 zcmdljv`&coGcPX}0}vQ$t=`CeicvaOKR2&LKP9mwQ9nL0DKRf4KQBHruecyJxn%QS HMmu%@u{II7 diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/repository.cpython-313.pyc index 68c9663b676426bf601c99a4a685d0c687817ebb..af17ba4ba7e36da03ef11cd1a4af1d82b6861fd7 100644 GIT binary patch delta 66 zcmeAdog>QqnU|M~0SGiq*KXv#$*57IpPN^rpQ7&@>=_?Wl%JKFTvDu`os^QKpO~JS UR}!C`pI4Gt9B;JwKjQ=r0HEX+Q2+n{ delta 53 zcmbOu+AqrenU|M~0SFAVR&V6K$taz#pPN^rpORRTs2`u0l$e*2pBJB*S6q;qT(X&m HX#xiTq}dSj diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/__pycache__/runtime.cpython-313.pyc index d196675215eb336e21f6befccc89f9ab24d7d3cf..efb677e8240bb994c6d19140052bdee899f63531 100644 GIT binary patch delta 68 zcmZ2Gi)q&^ChpI?yj%=GpkcapBX=ybMx}mkUWtB+zHhK+d_YltR%&udv3_<^N|JtJ WdTL%td~$wXNn&xl(dK66RhJ1HeeKQTQu UuOvP>Kd&UQINoUULB>a{0Gyl_LjV8( delta 53 zcmdnMznGu H#z(9GrXdmJ diff --git a/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc index 8cf5ca3e52dcb9a64e1a4c756523b3b3a4ea0342..4af29071b0aac0e7bdd0affc4e1446fa20f6a08e 100644 GIT binary patch delta 56 zcmbQkwvdhMGcPX}0}!}A*vRF^q~NNbn^&Trl30?cpO~JSR}!C`pI4Gt9G{d}oSKuF KmpVC{X(IsEXcD&o delta 51 zcmZ3;HiwPtGcPX}0}z;KZRB!el6KI~%`4GQNi0d!k55cW%uC77i_gp}E=Wx-nOwlM F5ddBo5840# diff --git a/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_route_stickiness_transaction_shift.cpython-313-pytest-9.0.2.pyc index 2d5daa31672e7990a84b3e7fc5cef12d5df18d5c..04946e9667ec21ba4d74821dc1da60a553d48b97 100644 GIT binary patch delta 46 zcmaEHi}B4Z#tmVd^2z$Sc_sQOi6x2piAjlhDfxNv#i_Z8c_o?2@oAYAn+rL=Cj$V- CdlN+f delta 42 ycmaEJi}A%R#tmVd(((Gac_sQOi6x2p@rg-^c`5mM@tJwW1*yp;n@c&rCj$U|gb|zo diff --git a/agent_framework_oci/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_transaction_explicit_abandonment_and_structural_candidate.cpython-313-pytest-9.0.2.pyc index 939362255b78206dafbb72b636860b3b2a6130a1..56cc0fc17549814175b4ddbc31ba283eb4ec9e0d 100644 GIT binary patch literal 64263 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 ro_JI|Z)gRd)(JHD$A$r?hOgimR@_TKmo> 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 b^5gi+I!;KsKqOqg@w(}vkjcAw)${%fXm(Q= diff --git a/agent_framework_oci/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_transaction_parameter_llm_precedence.cpython-313-pytest-9.0.2.pyc index 798a4a8027a74b1752d9aa732cbaf7bf653ed5ef..833d957e3c512bea4177410452cbaee2d21867d9 100644 GIT binary patch delta 2664 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 P)SS$`)XmvEul4}|a3B^) delta 54 zcmcaTjrr;{X710tyj%=G5O8+dMs7DAsd)X|yb}GC#F9k)qSWO4yyVQB)cCZ_yu_T% Jg*>nJ0RZpR6RH3J diff --git a/agent_framework_oci/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc b/agent_framework_oci/tests/__pycache__/test_transaction_state_router_interruption.cpython-313-pytest-9.0.2.pyc index ca89294029a90d5050fd93e3425fbe3e85098daf..12437a20652a01271d31d70df679c1550e8e84df 100644 GIT binary patch delta 59 zcmex