bugfix: max_tokens hardcoded
This commit is contained in:
29
agent_framework_oci/MULTI_ITEM_FRAMEWORK_FIX_20260918.md
Normal file
29
agent_framework_oci/MULTI_ITEM_FRAMEWORK_FIX_20260918.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Multi-item result preservation fix
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Preserve the terminal outcome of each item executed by the primary transactional tool. A failure in a later auxiliary/composite step must not erase successful item outcomes or turn an already-completed multi-item operation into a total failure.
|
||||||
|
|
||||||
|
## Framework-only change
|
||||||
|
No agent/template business logic is required for this fix. The runtime now recognizes an explicit structural contract on the requested tool: a `results` array with two or more entries carrying boolean `success` or `ok` fields.
|
||||||
|
|
||||||
|
For workflow-backed tools the authoritative source is preferentially:
|
||||||
|
|
||||||
|
`workflow.output[tool_name].results`
|
||||||
|
|
||||||
|
The framework derives one of:
|
||||||
|
- `SUCCESS`
|
||||||
|
- `PARTIAL_SUCCESS`
|
||||||
|
- `FAILED`
|
||||||
|
|
||||||
|
and publishes a generic `metadata.multi_item` summary with item counts.
|
||||||
|
|
||||||
|
When one or more primary items completed successfully but the outer wrapper is `ok=false` because a later auxiliary/post-processing step failed, the normalized result preserves the original failure as `original_ok=false` / `secondary_error` while exposing the requested operation as operationally successful (`ok=true`).
|
||||||
|
|
||||||
|
The LLM context also receives an explicit generic instruction that successful per-item terminal evidence cannot be overwritten by a secondary error and that mixed outcomes must be reported individually.
|
||||||
|
|
||||||
|
## Regression tests
|
||||||
|
Added `tests/test_multi_item_result_normalization.py` covering:
|
||||||
|
- 2 success + 1 failure -> `PARTIAL_SUCCESS`;
|
||||||
|
- all primary items successful + later secondary failure -> primary `SUCCESS` preserved;
|
||||||
|
- single-item behavior unchanged;
|
||||||
|
- unrelated nested tool results cannot override failure of the requested primary tool.
|
||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -9,11 +9,18 @@ profiles:
|
|||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 700
|
max_tokens: 700
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
|
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -9,11 +9,18 @@ profiles:
|
|||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 700
|
max_tokens: 700
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
|
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Correção — `max_tokens` por `llm_profiles.yaml` / `.env`
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Remover `max_tokens` efetivos fixados nos pontos de inferência do framework sem perder compatibilidade com os budgets anteriores.
|
||||||
|
|
||||||
|
A precedência usada pelos pontos corrigidos passa a ser:
|
||||||
|
|
||||||
|
1. `max_tokens` do profile selecionado em `llm_profiles.yaml`;
|
||||||
|
2. `max_tokens` do profile `default` do mesmo YAML, quando aplicável ao resolver;
|
||||||
|
3. `LLM_MAX_TOKENS` configurado no ambiente / `.env`;
|
||||||
|
4. valor legado do ponto de inferência, usado somente como fallback de compatibilidade.
|
||||||
|
|
||||||
|
O parâmetro interno `fallback_max_tokens` **não é uma nova configuração do usuário**. Ele apenas preserva o valor histórico quando não existe configuração externa.
|
||||||
|
|
||||||
|
## Pontos corrigidos
|
||||||
|
|
||||||
|
- Enterprise Router:
|
||||||
|
- transaction parameter relevance: fallback legado `256`;
|
||||||
|
- transaction intent shift: fallback legado `512`;
|
||||||
|
- multi-intent: fallback legado `768`;
|
||||||
|
- routing normal: fallback legado `512`.
|
||||||
|
- MCP parameter extraction: fallback `80`.
|
||||||
|
- Processing interruption classifier: fallback `8`.
|
||||||
|
- LLM guardrail: fallback `600`.
|
||||||
|
- RAG query rewrite: fallback `300`.
|
||||||
|
- RAG context compression: mantém a fórmula histórica `max(512, max_chars // 3)` apenas como fallback.
|
||||||
|
- Conversation summary memory: mantém a fórmula histórica `max(256, max_summary_chars // 4)` apenas como fallback.
|
||||||
|
|
||||||
|
## Atualização de `llm_profiles.yaml`
|
||||||
|
|
||||||
|
Como os quatro pontos do Enterprise Router usam o mesmo profile `router`, o profile foi ajustado para o **maior budget histórico**, conforme solicitado:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
router:
|
||||||
|
max_tokens: 768
|
||||||
|
```
|
||||||
|
|
||||||
|
Também foram garantidos os profiles explícitos:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
processing_interruption_classifier:
|
||||||
|
max_tokens: 8
|
||||||
|
|
||||||
|
mcp_parameter_extraction:
|
||||||
|
max_tokens: 80
|
||||||
|
```
|
||||||
|
|
||||||
|
Os profiles já existentes foram mantidos:
|
||||||
|
|
||||||
|
- `guardrail: 600`
|
||||||
|
- `rag_rewriter: 300`
|
||||||
|
- `rag_compressor: 1200`
|
||||||
|
- `summary_memory: 1200`
|
||||||
|
|
||||||
|
## Compatibilidade
|
||||||
|
|
||||||
|
Se `llm_profiles.yaml` não estiver disponível e `LLM_MAX_TOKENS` não estiver definido, cada ponto corrigido continua usando seu budget histórico. Portanto a correção não transforma a ausência de configuração em `2048` apenas por causa do default global de `Settings`.
|
||||||
@@ -9,3 +9,11 @@ profiles:
|
|||||||
model: mock-judge
|
model: mock-judge
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 300
|
max_tokens: 300
|
||||||
|
|
||||||
|
mcp_parameter_extraction:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 80
|
||||||
|
timeout_seconds: 5
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ async def classify_processing_interruption(
|
|||||||
response = await llm.ainvoke(
|
response = await llm.ainvoke(
|
||||||
[{"role": "system", "content": prompt}],
|
[{"role": "system", "content": prompt}],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=8,
|
fallback_max_tokens=8,
|
||||||
profile_name=profile_name,
|
profile_name=profile_name,
|
||||||
component_name=profile_name,
|
component_name=profile_name,
|
||||||
generation_name=f"llm.{profile_name}",
|
generation_name=f"llm.{profile_name}",
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class LLMGuardrailRail(Guardrail):
|
|||||||
{"role": "user", "content": prompt},
|
{"role": "user", "content": prompt},
|
||||||
],
|
],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=600,
|
fallback_max_tokens=600,
|
||||||
profile_name=self.profile_name,
|
profile_name=self.profile_name,
|
||||||
component_name=self.profile_name,
|
component_name=self.profile_name,
|
||||||
generation_name=f"llm.{self.profile_name}",
|
generation_name=f"llm.{self.profile_name}",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -164,6 +165,66 @@ class LLMProfileResolver:
|
|||||||
|
|
||||||
return effective
|
return effective
|
||||||
|
|
||||||
|
def resolve_configured_value(
|
||||||
|
self,
|
||||||
|
profile_name: str | None,
|
||||||
|
key: str,
|
||||||
|
*,
|
||||||
|
env_var: str | None = None,
|
||||||
|
fallback: Any = None,
|
||||||
|
) -> Any:
|
||||||
|
"""Resolve one configuration value with an explicit compatibility fallback.
|
||||||
|
|
||||||
|
Precedence is intentionally aligned with the framework configuration model:
|
||||||
|
1. specific ``llm_profiles.yaml`` profile;
|
||||||
|
2. YAML ``default`` profile;
|
||||||
|
3. explicitly configured general environment variable;
|
||||||
|
4. the caller-provided legacy fallback.
|
||||||
|
|
||||||
|
The last step is important for old inference points that historically had a
|
||||||
|
local constant. Pydantic's Settings object always has framework defaults, so
|
||||||
|
checking only ``settings.LLM_MAX_TOKENS`` would make an *unset* environment
|
||||||
|
indistinguishable from the global default.
|
||||||
|
"""
|
||||||
|
selected_name = self.normalize_profile_name(profile_name)
|
||||||
|
if self.enabled:
|
||||||
|
specific_profile = self._profiles.get(selected_name) or {}
|
||||||
|
default_profile = self._profiles.get("default") or {}
|
||||||
|
if key in specific_profile and specific_profile.get(key) is not None:
|
||||||
|
return specific_profile.get(key)
|
||||||
|
if key in default_profile and default_profile.get(key) is not None:
|
||||||
|
return default_profile.get(key)
|
||||||
|
|
||||||
|
if env_var:
|
||||||
|
raw = os.getenv(env_var)
|
||||||
|
if raw is not None and str(raw).strip() != "":
|
||||||
|
raw = str(raw).strip()
|
||||||
|
try:
|
||||||
|
if isinstance(fallback, bool):
|
||||||
|
return raw.lower() in {"1", "true", "yes", "on"}
|
||||||
|
if isinstance(fallback, int) and not isinstance(fallback, bool):
|
||||||
|
return int(raw)
|
||||||
|
if isinstance(fallback, float):
|
||||||
|
return float(raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
logger.warning(
|
||||||
|
"Invalid %s=%r for profile=%s key=%s; using legacy fallback=%r",
|
||||||
|
env_var, raw, selected_name, key, fallback,
|
||||||
|
)
|
||||||
|
return fallback
|
||||||
|
return raw
|
||||||
|
|
||||||
|
# Also honor a value explicitly supplied when Settings was instantiated
|
||||||
|
# programmatically, without treating the class default as configuration.
|
||||||
|
field_name = env_var or ""
|
||||||
|
fields_set = getattr(self.settings, "model_fields_set", set()) or set()
|
||||||
|
if field_name and field_name in fields_set:
|
||||||
|
value = getattr(self.settings, field_name, None)
|
||||||
|
if value is not None:
|
||||||
|
return value
|
||||||
|
|
||||||
|
return fallback
|
||||||
|
|
||||||
def normalize_profile_name(self, profile_name: str | None) -> str:
|
def normalize_profile_name(self, profile_name: str | None) -> str:
|
||||||
return _canonical_profile_name(profile_name)
|
return _canonical_profile_name(profile_name)
|
||||||
|
|
||||||
|
|||||||
@@ -370,6 +370,14 @@ class OCICompatibleOpenAIProvider(LLMProvider):
|
|||||||
profile_name = kwargs.pop("profile_name", None)
|
profile_name = kwargs.pop("profile_name", None)
|
||||||
component_name = kwargs.pop("component_name", None) or kwargs.pop("component", None) or profile_name or "default"
|
component_name = kwargs.pop("component_name", None) or kwargs.pop("component", None) or profile_name or "default"
|
||||||
generation_name = kwargs.pop("generation_name", None) or f"llm.{component_name}"
|
generation_name = kwargs.pop("generation_name", None) or f"llm.{component_name}"
|
||||||
|
fallback_max_tokens = kwargs.pop("fallback_max_tokens", None)
|
||||||
|
if fallback_max_tokens is not None:
|
||||||
|
kwargs["max_tokens"] = self.profile_resolver.resolve_configured_value(
|
||||||
|
profile_name,
|
||||||
|
"max_tokens",
|
||||||
|
env_var="LLM_MAX_TOKENS",
|
||||||
|
fallback=fallback_max_tokens,
|
||||||
|
)
|
||||||
generation_name, generation_mapping_meta = _normalize_generation_name(self.telemetry, generation_name)
|
generation_name, generation_mapping_meta = _normalize_generation_name(self.telemetry, generation_name)
|
||||||
effective = self.profile_resolver.resolve(profile_name, **kwargs)
|
effective = self.profile_resolver.resolve(profile_name, **kwargs)
|
||||||
provider = str(effective.get("provider") or self.provider_name)
|
provider = str(effective.get("provider") or self.provider_name)
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class ConversationSummaryMemory:
|
|||||||
summary = await self.llm.ainvoke([
|
summary = await self.llm.ainvoke([
|
||||||
{"role": "system", "content": "Você resume memória conversacional para agentes corporativos."},
|
{"role": "system", "content": "Você resume memória conversacional para agentes corporativos."},
|
||||||
{"role": "user", "content": prompt},
|
{"role": "user", "content": prompt},
|
||||||
], max_tokens=max(256, max_summary_chars // 4), temperature=0.1, profile_name="summary_memory", component_name="summary_memory", generation_name="llm.summary_memory")
|
], fallback_max_tokens=max(256, max_summary_chars // 4), temperature=0.1, profile_name="summary_memory", component_name="summary_memory", generation_name="llm.summary_memory")
|
||||||
summary = (summary or "").strip()
|
summary = (summary or "").strip()
|
||||||
if not summary:
|
if not summary:
|
||||||
return self._deterministic_summary(previous_summary=previous_summary, messages=messages, max_chars=max_summary_chars)
|
return self._deterministic_summary(previous_summary=previous_summary, messages=messages, max_chars=max_summary_chars)
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class RagService:
|
|||||||
{'role': 'user', 'content': prompt},
|
{'role': 'user', 'content': prompt},
|
||||||
],
|
],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=300,
|
fallback_max_tokens=300,
|
||||||
profile_name=profile_name,
|
profile_name=profile_name,
|
||||||
component_name=profile_name,
|
component_name=profile_name,
|
||||||
generation_name=f"llm.{profile_name}",
|
generation_name=f"llm.{profile_name}",
|
||||||
@@ -112,7 +112,7 @@ class RagService:
|
|||||||
{'role': 'user', 'content': prompt},
|
{'role': 'user', 'content': prompt},
|
||||||
],
|
],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=max(512, max_chars // 3),
|
fallback_max_tokens=max(512, max_chars // 3),
|
||||||
profile_name=profile_name,
|
profile_name=profile_name,
|
||||||
component_name=profile_name,
|
component_name=profile_name,
|
||||||
generation_name=f"llm.{profile_name}",
|
generation_name=f"llm.{profile_name}",
|
||||||
|
|||||||
@@ -1045,7 +1045,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=256,
|
fallback_max_tokens=256,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router.transaction_parameter_relevance",
|
component_name="router.transaction_parameter_relevance",
|
||||||
generation_name="llm.transaction_parameter_relevance",
|
generation_name="llm.transaction_parameter_relevance",
|
||||||
@@ -1328,7 +1328,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=512,
|
fallback_max_tokens=512,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router",
|
component_name="router",
|
||||||
generation_name="llm.transaction_intent_shift",
|
generation_name="llm.transaction_intent_shift",
|
||||||
@@ -1674,7 +1674,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=768,
|
fallback_max_tokens=768,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router.multi_intent",
|
component_name="router.multi_intent",
|
||||||
generation_name="llm.router.multi_intent",
|
generation_name="llm.router.multi_intent",
|
||||||
@@ -1730,7 +1730,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=512,
|
fallback_max_tokens=512,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router",
|
component_name="router",
|
||||||
generation_name="llm.router",
|
generation_name="llm.router",
|
||||||
|
|||||||
@@ -849,7 +849,7 @@ class AgentRuntimeMixin:
|
|||||||
component_name="mcp_parameter_extraction",
|
component_name="mcp_parameter_extraction",
|
||||||
generation_name="llm.mcp_parameter_extraction",
|
generation_name="llm.mcp_parameter_extraction",
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=80,
|
fallback_max_tokens=80,
|
||||||
)
|
)
|
||||||
raw = self._llm_response_text(response).strip()
|
raw = self._llm_response_text(response).strip()
|
||||||
payload = parse_json_object(raw)
|
payload = parse_json_object(raw)
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
@@ -72,6 +78,13 @@ profiles:
|
|||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0.2
|
temperature: 0.2
|
||||||
|
mcp_parameter_extraction:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 80
|
||||||
|
timeout_seconds: 5
|
||||||
|
|
||||||
transaction_parameter_extraction:
|
transaction_parameter_extraction:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1-mini
|
model: openai.gpt-4.1-mini
|
||||||
|
|||||||
@@ -15,11 +15,18 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 700
|
max_tokens: 700
|
||||||
|
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
|
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
|
|
||||||
# Safety / evaluation
|
# Safety / evaluation
|
||||||
guardrail:
|
guardrail:
|
||||||
@@ -87,3 +94,11 @@ profiles:
|
|||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0.2
|
temperature: 0.2
|
||||||
|
|
||||||
|
mcp_parameter_extraction:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 80
|
||||||
|
timeout_seconds: 5
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -58,7 +58,7 @@ async def classify_processing_interruption(
|
|||||||
response = await llm.ainvoke(
|
response = await llm.ainvoke(
|
||||||
[{"role": "system", "content": prompt}],
|
[{"role": "system", "content": prompt}],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=8,
|
fallback_max_tokens=8,
|
||||||
profile_name=profile_name,
|
profile_name=profile_name,
|
||||||
component_name=profile_name,
|
component_name=profile_name,
|
||||||
generation_name=f"llm.{profile_name}",
|
generation_name=f"llm.{profile_name}",
|
||||||
|
|||||||
Binary file not shown.
@@ -49,7 +49,7 @@ class LLMGuardrailRail(Guardrail):
|
|||||||
{"role": "user", "content": prompt},
|
{"role": "user", "content": prompt},
|
||||||
],
|
],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=600,
|
fallback_max_tokens=600,
|
||||||
profile_name=self.profile_name,
|
profile_name=self.profile_name,
|
||||||
component_name=self.profile_name,
|
component_name=self.profile_name,
|
||||||
generation_name=f"llm.{self.profile_name}",
|
generation_name=f"llm.{self.profile_name}",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -164,6 +165,66 @@ class LLMProfileResolver:
|
|||||||
|
|
||||||
return effective
|
return effective
|
||||||
|
|
||||||
|
def resolve_configured_value(
|
||||||
|
self,
|
||||||
|
profile_name: str | None,
|
||||||
|
key: str,
|
||||||
|
*,
|
||||||
|
env_var: str | None = None,
|
||||||
|
fallback: Any = None,
|
||||||
|
) -> Any:
|
||||||
|
"""Resolve one configuration value with an explicit compatibility fallback.
|
||||||
|
|
||||||
|
Precedence is intentionally aligned with the framework configuration model:
|
||||||
|
1. specific ``llm_profiles.yaml`` profile;
|
||||||
|
2. YAML ``default`` profile;
|
||||||
|
3. explicitly configured general environment variable;
|
||||||
|
4. the caller-provided legacy fallback.
|
||||||
|
|
||||||
|
The last step is important for old inference points that historically had a
|
||||||
|
local constant. Pydantic's Settings object always has framework defaults, so
|
||||||
|
checking only ``settings.LLM_MAX_TOKENS`` would make an *unset* environment
|
||||||
|
indistinguishable from the global default.
|
||||||
|
"""
|
||||||
|
selected_name = self.normalize_profile_name(profile_name)
|
||||||
|
if self.enabled:
|
||||||
|
specific_profile = self._profiles.get(selected_name) or {}
|
||||||
|
default_profile = self._profiles.get("default") or {}
|
||||||
|
if key in specific_profile and specific_profile.get(key) is not None:
|
||||||
|
return specific_profile.get(key)
|
||||||
|
if key in default_profile and default_profile.get(key) is not None:
|
||||||
|
return default_profile.get(key)
|
||||||
|
|
||||||
|
if env_var:
|
||||||
|
raw = os.getenv(env_var)
|
||||||
|
if raw is not None and str(raw).strip() != "":
|
||||||
|
raw = str(raw).strip()
|
||||||
|
try:
|
||||||
|
if isinstance(fallback, bool):
|
||||||
|
return raw.lower() in {"1", "true", "yes", "on"}
|
||||||
|
if isinstance(fallback, int) and not isinstance(fallback, bool):
|
||||||
|
return int(raw)
|
||||||
|
if isinstance(fallback, float):
|
||||||
|
return float(raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
logger.warning(
|
||||||
|
"Invalid %s=%r for profile=%s key=%s; using legacy fallback=%r",
|
||||||
|
env_var, raw, selected_name, key, fallback,
|
||||||
|
)
|
||||||
|
return fallback
|
||||||
|
return raw
|
||||||
|
|
||||||
|
# Also honor a value explicitly supplied when Settings was instantiated
|
||||||
|
# programmatically, without treating the class default as configuration.
|
||||||
|
field_name = env_var or ""
|
||||||
|
fields_set = getattr(self.settings, "model_fields_set", set()) or set()
|
||||||
|
if field_name and field_name in fields_set:
|
||||||
|
value = getattr(self.settings, field_name, None)
|
||||||
|
if value is not None:
|
||||||
|
return value
|
||||||
|
|
||||||
|
return fallback
|
||||||
|
|
||||||
def normalize_profile_name(self, profile_name: str | None) -> str:
|
def normalize_profile_name(self, profile_name: str | None) -> str:
|
||||||
return _canonical_profile_name(profile_name)
|
return _canonical_profile_name(profile_name)
|
||||||
|
|
||||||
|
|||||||
@@ -370,6 +370,14 @@ class OCICompatibleOpenAIProvider(LLMProvider):
|
|||||||
profile_name = kwargs.pop("profile_name", None)
|
profile_name = kwargs.pop("profile_name", None)
|
||||||
component_name = kwargs.pop("component_name", None) or kwargs.pop("component", None) or profile_name or "default"
|
component_name = kwargs.pop("component_name", None) or kwargs.pop("component", None) or profile_name or "default"
|
||||||
generation_name = kwargs.pop("generation_name", None) or f"llm.{component_name}"
|
generation_name = kwargs.pop("generation_name", None) or f"llm.{component_name}"
|
||||||
|
fallback_max_tokens = kwargs.pop("fallback_max_tokens", None)
|
||||||
|
if fallback_max_tokens is not None:
|
||||||
|
kwargs["max_tokens"] = self.profile_resolver.resolve_configured_value(
|
||||||
|
profile_name,
|
||||||
|
"max_tokens",
|
||||||
|
env_var="LLM_MAX_TOKENS",
|
||||||
|
fallback=fallback_max_tokens,
|
||||||
|
)
|
||||||
generation_name, generation_mapping_meta = _normalize_generation_name(self.telemetry, generation_name)
|
generation_name, generation_mapping_meta = _normalize_generation_name(self.telemetry, generation_name)
|
||||||
effective = self.profile_resolver.resolve(profile_name, **kwargs)
|
effective = self.profile_resolver.resolve(profile_name, **kwargs)
|
||||||
provider = str(effective.get("provider") or self.provider_name)
|
provider = str(effective.get("provider") or self.provider_name)
|
||||||
|
|||||||
Binary file not shown.
@@ -167,7 +167,7 @@ class ConversationSummaryMemory:
|
|||||||
summary = await self.llm.ainvoke([
|
summary = await self.llm.ainvoke([
|
||||||
{"role": "system", "content": "Você resume memória conversacional para agentes corporativos."},
|
{"role": "system", "content": "Você resume memória conversacional para agentes corporativos."},
|
||||||
{"role": "user", "content": prompt},
|
{"role": "user", "content": prompt},
|
||||||
], max_tokens=max(256, max_summary_chars // 4), temperature=0.1, profile_name="summary_memory", component_name="summary_memory", generation_name="llm.summary_memory")
|
], fallback_max_tokens=max(256, max_summary_chars // 4), temperature=0.1, profile_name="summary_memory", component_name="summary_memory", generation_name="llm.summary_memory")
|
||||||
summary = (summary or "").strip()
|
summary = (summary or "").strip()
|
||||||
if not summary:
|
if not summary:
|
||||||
return self._deterministic_summary(previous_summary=previous_summary, messages=messages, max_chars=max_summary_chars)
|
return self._deterministic_summary(previous_summary=previous_summary, messages=messages, max_chars=max_summary_chars)
|
||||||
|
|||||||
Binary file not shown.
@@ -98,7 +98,7 @@ class RagService:
|
|||||||
{'role': 'user', 'content': prompt},
|
{'role': 'user', 'content': prompt},
|
||||||
],
|
],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=300,
|
fallback_max_tokens=300,
|
||||||
profile_name=profile_name,
|
profile_name=profile_name,
|
||||||
component_name=profile_name,
|
component_name=profile_name,
|
||||||
generation_name=f"llm.{profile_name}",
|
generation_name=f"llm.{profile_name}",
|
||||||
@@ -126,7 +126,7 @@ class RagService:
|
|||||||
{'role': 'user', 'content': prompt},
|
{'role': 'user', 'content': prompt},
|
||||||
],
|
],
|
||||||
temperature=0,
|
temperature=0,
|
||||||
max_tokens=max(512, max_chars // 3),
|
fallback_max_tokens=max(512, max_chars // 3),
|
||||||
profile_name=profile_name,
|
profile_name=profile_name,
|
||||||
component_name=profile_name,
|
component_name=profile_name,
|
||||||
generation_name=f"llm.{profile_name}",
|
generation_name=f"llm.{profile_name}",
|
||||||
|
|||||||
Binary file not shown.
@@ -1045,7 +1045,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=256,
|
fallback_max_tokens=256,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router.transaction_parameter_relevance",
|
component_name="router.transaction_parameter_relevance",
|
||||||
generation_name="llm.transaction_parameter_relevance",
|
generation_name="llm.transaction_parameter_relevance",
|
||||||
@@ -1328,7 +1328,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=512,
|
fallback_max_tokens=512,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router",
|
component_name="router",
|
||||||
generation_name="llm.transaction_intent_shift",
|
generation_name="llm.transaction_intent_shift",
|
||||||
@@ -1674,7 +1674,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=768,
|
fallback_max_tokens=768,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router.multi_intent",
|
component_name="router.multi_intent",
|
||||||
generation_name="llm.router.multi_intent",
|
generation_name="llm.router.multi_intent",
|
||||||
@@ -1730,7 +1730,7 @@ class EnterpriseRouter:
|
|||||||
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
{"role": "user", "content": json.dumps(user, ensure_ascii=False)},
|
||||||
],
|
],
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=512,
|
fallback_max_tokens=512,
|
||||||
profile_name="router",
|
profile_name="router",
|
||||||
component_name="router",
|
component_name="router",
|
||||||
generation_name="llm.router",
|
generation_name="llm.router",
|
||||||
|
|||||||
Binary file not shown.
@@ -849,7 +849,7 @@ class AgentRuntimeMixin:
|
|||||||
component_name="mcp_parameter_extraction",
|
component_name="mcp_parameter_extraction",
|
||||||
generation_name="llm.mcp_parameter_extraction",
|
generation_name="llm.mcp_parameter_extraction",
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
max_tokens=80,
|
fallback_max_tokens=80,
|
||||||
)
|
)
|
||||||
raw = self._llm_response_text(response).strip()
|
raw = self._llm_response_text(response).strip()
|
||||||
payload = parse_json_object(raw)
|
payload = parse_json_object(raw)
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
###############################################################################
|
|
||||||
# AI AGENT PLATFORM - CONFIGURAÇÃO ÚNICA
|
|
||||||
# Este arquivo é lido por Pydantic Settings no framework e no backend template.
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
APP_NAME=ai-agent-template
|
|
||||||
APP_ENV=local
|
|
||||||
LOG_LEVEL=INFO
|
|
||||||
API_HOST=0.0.0.0
|
|
||||||
API_PORT=8000
|
|
||||||
CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# LLM - OCI Generative AI como provider principal
|
|
||||||
###############################################################################
|
|
||||||
# Opções: mock, oci_openai, oci_sdk, openai_compatible
|
|
||||||
LLM_PROVIDER=oci_sdk
|
|
||||||
LLM_TEMPERATURE=0.2
|
|
||||||
LLM_MAX_TOKENS=2048
|
|
||||||
LLM_TIMEOUT_SECONDS=120
|
|
||||||
|
|
||||||
# OCI OpenAI-compatible endpoint
|
|
||||||
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com
|
|
||||||
OCI_GENAI_MODEL=openai.gpt-4.1
|
|
||||||
OCI_GENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS
|
|
||||||
OCI_GENAI_PROJECT_OCID=
|
|
||||||
|
|
||||||
#OCI_GENAI_BASE_URL=https://inference.generativeai.eu-frankfurt-1.oci.oraclecloud.com/openai/v1
|
|
||||||
#OCI_GENAI_MODEL=ocid1.generativeaiendpoint.oc1.eu-frankfurt-1.amaaaaaad6nji3aa5fmicyft7qci6snu5idhwoomviehnsv4lrostzbmenkq
|
|
||||||
#OCI_GENAI_API_KEY=sk-TB0OzfrhPLXbQTqZ4aKlrLpcgjHAcn11O1o8C4I4aPvWF0Vx
|
|
||||||
#OCI_GENAI_PROJECT_OCID=
|
|
||||||
|
|
||||||
|
|
||||||
# OCI_AUTH_MODE=config_file|instance_principal|resource_principal
|
|
||||||
OCI_AUTH_MODE=config_file
|
|
||||||
# OCI SDK / signer / profiles
|
|
||||||
OCI_CONFIG_FILE=~/.oci/config
|
|
||||||
OCI_PROFILE=LATINOAMERICA-Chicago
|
|
||||||
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaexpiw4a7dio64mkfv2t273s2hgdl6mgfvvyv7tycalnjlvpvfl3q
|
|
||||||
OCI_REGION=us-chicago-1
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Persistência
|
|
||||||
###############################################################################
|
|
||||||
# Opções: memory, autonomous, mongodb
|
|
||||||
SESSION_REPOSITORY_PROVIDER=autonomous
|
|
||||||
MEMORY_REPOSITORY_PROVIDER=autonomous
|
|
||||||
CHECKPOINT_REPOSITORY_PROVIDER=autonomous
|
|
||||||
|
|
||||||
# Autonomous Database
|
|
||||||
ADB_USER=admin
|
|
||||||
ADB_PASSWORD=Moniquinha19721972
|
|
||||||
ADB_DSN=oradb23ai_high
|
|
||||||
ADB_WALLET_LOCATION=/mnt/d/Dropbox/ORACLE/LatinoAmerica/Wallet_ORADB23ai
|
|
||||||
ADB_WALLET_PASSWORD=Moniquinha1972
|
|
||||||
ADB_TABLE_PREFIX=AGENTFW
|
|
||||||
|
|
||||||
# MongoDB - também pode representar Autonomous usando API compatível com Mongo, se habilitada no ambiente
|
|
||||||
MONGODB_URI=mongodb://mongo:mongopassword@localhost:27017
|
|
||||||
MONGODB_DATABASE=agent_platform
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
REDIS_URL=redis://localhost:6379/0
|
|
||||||
ENABLE_REDIS_CACHE=false
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# RAG / Vector / Graph
|
|
||||||
###############################################################################
|
|
||||||
VECTOR_STORE_PROVIDER=autonomous
|
|
||||||
GRAPH_STORE_PROVIDER=autonomous
|
|
||||||
RAG_TOP_K=5
|
|
||||||
EMBEDDING_PROVIDER=oci
|
|
||||||
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
|
||||||
RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Observabilidade
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_LANGFUSE=true
|
|
||||||
# Opcional: verbose, compact
|
|
||||||
LANGFUSE_TRACE_MODE=compact
|
|
||||||
# Nome customizado do trace pai, ex.: backoffice.checklist.workflow ou backoffice.emulador.workflow
|
|
||||||
LANGFUSE_COMPACT_VISIBLE_EVENT_PREFIXES=AGA.,NOC., IC.
|
|
||||||
LANGFUSE_COMPACT_SUPPRESSED_PREFIXES=llm.chat_completion
|
|
||||||
LANGFUSE_IGNORE_HEALTHCHECKS=true
|
|
||||||
LANGFUSE_IGNORED_PATHS=/health,/ready,/metrics
|
|
||||||
LANGFUSE_PUBLIC_KEY=pk-lf-4a1e3921-5158-4fd3-a16d-7a77549fb312
|
|
||||||
LANGFUSE_SECRET_KEY=sk-lf-efc6fd59-c5ec-4858-b6ec-4aa129734915
|
|
||||||
LANGFUSE_HOST=http://localhost:3005
|
|
||||||
ENABLE_OTEL=false
|
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT=
|
|
||||||
OTEL_SERVICE_NAME=ai-agent-template
|
|
||||||
ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true
|
|
||||||
ENABLE_LANGFUSE_ANALYTICS_PUBLISHER=false
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Analytics / Observer corporativo
|
|
||||||
###############################################################################
|
|
||||||
# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo.
|
|
||||||
ENABLE_ANALYTICS=true
|
|
||||||
# Providers aceitos: oci_streaming,pubsub,noop
|
|
||||||
ANALYTICS_PROVIDERS=pubsub
|
|
||||||
# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente.
|
|
||||||
AGENT_PUBSUB_TOPIC=teste_pubsub
|
|
||||||
PUBSUB_TOPIC_PATH=projects/project-7f829b68-bf43-442c-ad3/topics/teste_pubsub
|
|
||||||
PROJECT_ID=project-7f829b68-bf43-442c-ad3
|
|
||||||
GCP_PUBSUB_TOPIC_PATH=projects/project-7f829b68-bf43-442c-ad3/topics/teste_pubsub
|
|
||||||
GCP_PROJECT_ID=project-7f829b68-bf43-442c-ad3
|
|
||||||
GCP_PUBSUB_TOPIC=teste_pubsub
|
|
||||||
GCP_PUBSUB_TIMEOUT_SECONDS=30
|
|
||||||
# Credencial GCP segue padrão Google:
|
|
||||||
GOOGLE_APPLICATION_CREDENTIALS=/mnt/d/Dropbox/ORACLE/TIM/FY27/Testes/sa-hoshikawa.json
|
|
||||||
|
|
||||||
PUBSUB_SEQUENCE_ENABLED=true
|
|
||||||
PUBSUB_SEQUENCE_PROVIDER=mongodb
|
|
||||||
|
|
||||||
#PUBSUB_SEQUENCE_MONGODB_URI=mongodb://mongo:mongopassword@localhost:27017
|
|
||||||
#PUBSUB_SEQUENCE_MONGODB_DATABASE=agent_platform
|
|
||||||
|
|
||||||
PUBSUB_SEQUENCE_MONGODB_URI=mongodb://ADMIN:Moniquinha1972@srlrz1eg.adb.sa-saopaulo-1.oraclecloudapps.com:37017/ADMIN?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true
|
|
||||||
PUBSUB_SEQUENCE_MONGODB_DATABASE=ADMIN
|
|
||||||
|
|
||||||
PUBSUB_SEQUENCE_MONGODB_COLLECTION=observer_event_counters
|
|
||||||
|
|
||||||
PUBSUB_SEQUENCE_TTL_SECONDS=86400
|
|
||||||
PUBSUB_SEQUENCE_KEY_PREFIX=observer:sequence
|
|
||||||
|
|
||||||
PUBSUB_SEQUENCE_MEMORY_FALLBACK=false
|
|
||||||
SIMULATE_AGA_MISSING_TRANSACTION_ID=true
|
|
||||||
###############################################################################
|
|
||||||
# OCI Streaming
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_OCI_STREAMING=false
|
|
||||||
OCI_STREAM_ENDPOINT=
|
|
||||||
OCI_STREAM_OCID=
|
|
||||||
OCI_STREAM_PARTITION_KEY=agent-events
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Guardrails, Judges, Supervisor
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_INPUT_GUARDRAILS=true
|
|
||||||
ENABLE_OUTPUT_GUARDRAILS=true
|
|
||||||
ENABLE_JUDGES=true
|
|
||||||
ENABLE_SUPERVISOR=true
|
|
||||||
ENABLE_OUTPUT_SUPERVISOR=true
|
|
||||||
ENABLE_PARALLEL_GUARDRAILS=true
|
|
||||||
GUARDRAILS_FAIL_FAST=true
|
|
||||||
OUTPUT_SUPERVISOR_MAX_RETRIES=3
|
|
||||||
GUARDRAILS_CONFIG_PATH=./config/guardrails.yaml
|
|
||||||
JUDGES_CONFIG_PATH=./config/judges.yaml
|
|
||||||
PROMPT_POLICY_PATH=./config/prompt_policy.yaml
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Gateway de canais
|
|
||||||
###############################################################################
|
|
||||||
DEFAULT_CHANNEL=web
|
|
||||||
# embedded = backend may parse simple/native channel payloads.
|
|
||||||
# external = backend only accepts GatewayRequest normalized by an external Channel Gateway.
|
|
||||||
FRAMEWORK_CHANNEL_INPUT_MODE=embedded
|
|
||||||
ENABLE_VOICE_ADAPTER=true
|
|
||||||
ENABLE_WHATSAPP_ADAPTER=true
|
|
||||||
ENABLE_TEXT_ADAPTER=true
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
# ENTERPRISE ROUTING
|
|
||||||
#################################################
|
|
||||||
# Arquivo YAML com intents, keywords, políticas de estado e fallback.
|
|
||||||
ROUTING_CONFIG_PATH=./config/routing.yaml
|
|
||||||
# true = usa LLM para classificar quando keywords/estado não resolverem.
|
|
||||||
# Em produção, costuma ser útil; em desenvolvimento, false evita custo e latência.
|
|
||||||
ENABLE_LLM_ROUTER=true
|
|
||||||
|
|
||||||
# Semantic route stickiness (optional).
|
|
||||||
# Uses a lightweight LLM profile to decide only CONTINUE vs ROUTE.
|
|
||||||
# There are no regexes or deterministic language rules.
|
|
||||||
ENABLE_ROUTE_STICKINESS=true
|
|
||||||
ROUTE_STICKINESS_LLM_PROFILE=route_continuity
|
|
||||||
ROUTE_STICKINESS_CONFIDENCE_THRESHOLD=0.90
|
|
||||||
ROUTE_STICKINESS_HISTORY_TURNS=2
|
|
||||||
ROUTE_STICKINESS_MAX_TOKENS=80
|
|
||||||
HUMAN_HANDOFF_MESSAGE=Vou encaminhar seu atendimento para uma pessoa.
|
|
||||||
END_SESSION_MESSAGE=Atendimento encerrado. Obrigado pelo contato.
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# MCP / Tools
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_MCP_TOOLS=true
|
|
||||||
MCP_SERVERS_CONFIG_PATH=./config/mcp_servers.yaml
|
|
||||||
TOOLS_CONFIG_PATH=./config/tools.yaml
|
|
||||||
MCP_TOOL_TIMEOUT_SECONDS=30
|
|
||||||
|
|
||||||
# router = EnterpriseRouter seleciona um agente; supervisor = pode acionar múltiplos agentes
|
|
||||||
ROUTING_MODE=router
|
|
||||||
|
|
||||||
# Usage/cost accounting
|
|
||||||
USAGE_REPOSITORY_PROVIDER=autonomous
|
|
||||||
IDENTITY_CONFIG_PATH=./config/identity.yaml
|
|
||||||
MCP_PARAMETER_MAPPING_PATH=./config/mcp_parameter_mapping.yaml
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# ConversationSummaryMemory / compressão de contexto conversacional
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
ENABLE_CONVERSATION_SUMMARY_MEMORY=true
|
|
||||||
MEMORY_CONTEXT_STRATEGY=summary
|
|
||||||
MEMORY_HISTORY_LIMIT=80
|
|
||||||
MEMORY_RECENT_MESSAGES_LIMIT=8
|
|
||||||
MEMORY_SUMMARY_TRIGGER_MESSAGES=20
|
|
||||||
MEMORY_MAX_SUMMARY_CHARS=6000
|
|
||||||
MEMORY_SUMMARY_USE_LLM=true
|
|
||||||
MEMORY_INJECT_RECENT_MESSAGES=true
|
|
||||||
MEMORY_INJECT_SUMMARY=true
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# LONG-TERM MEMORY
|
|
||||||
###############################################################################
|
|
||||||
ENABLE_LONG_TERM_MEMORY=true
|
|
||||||
LONG_TERM_MEMORY_PROVIDER=sqlite
|
|
||||||
LONG_TERM_MEMORY_SQLITE_PATH=./data/agent_framework.db
|
|
||||||
LONG_TERM_MEMORY_TABLE=agentfw_long_term_memory
|
|
||||||
# For Autonomous/Oracle, defaults to ${ADB_TABLE_PREFIX}_LONG_TERM_MEMORY
|
|
||||||
# LONG_TERM_MEMORY_ORACLE_TABLE=AGENTFW_LONG_TERM_MEMORY
|
|
||||||
LONG_TERM_MEMORY_MAX_CONTEXT_ITEMS=20
|
|
||||||
LONG_TERM_MEMORY_MIN_CONFIDENCE=0.70
|
|
||||||
LONG_TERM_MEMORY_AUTO_EXTRACT=true
|
|
||||||
LONG_TERM_MEMORY_INJECT_CONTEXT=true
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# RAG provider selection (mutually exclusive at runtime)
|
|
||||||
###############################################################################
|
|
||||||
# standard = RAG original do agent_framework_oci (default, backward compatible)
|
|
||||||
# kbdb = KBDB enterprise via PKG_KB_SERVING.SEARCH_KNOWLEDGE_BASE
|
|
||||||
RAG_PROVIDER=kbdb
|
|
||||||
|
|
||||||
# Somente usados quando RAG_PROVIDER=kbdb. Se vazios, credenciais caem para ADB_*.
|
|
||||||
KBDB_DB_USER=KB_USER
|
|
||||||
KBDB_DB_PASSWORD=SuperStr0ngPa##w0rd
|
|
||||||
KBDB_DB_DSN=kbdb_public_high
|
|
||||||
KBDB_DB_WALLET_LOCATION=/mnt/d/Dropbox/ORACLE/TIM/FY27/kb/Wallet_kbdb
|
|
||||||
KBDB_DB_WALLET_PASSWORD=SuperStr0ngPa##w0rd
|
|
||||||
KBDB_SEARCH_TYPE=hybrid
|
|
||||||
KBDB_NODE_EXPANSION=true
|
|
||||||
KBDB_NODE_MAX_RELATED=8
|
|
||||||
KBDB_GRAPH_CROSS_REF=false
|
|
||||||
KBDB_MAX_CROSS_REF_HOPS=1
|
|
||||||
KBDB_DOCUMENT_TYPE=customer_safe
|
|
||||||
KBDB_METADATA_JSON=
|
|
||||||
KBDB_MIN_SCORE=0.5
|
|
||||||
KBDB_IDENTIFY_DOCUMENT=true
|
|
||||||
KBDB_STORE_QUERY=true
|
|
||||||
KBDB_IDENTIFY_TOP_N=3
|
|
||||||
Binary file not shown.
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
@@ -15,11 +15,17 @@ profiles:
|
|||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 80
|
max_tokens: 80
|
||||||
timeout_seconds: 5
|
timeout_seconds: 5
|
||||||
|
processing_interruption_classifier:
|
||||||
|
provider: oci_openai
|
||||||
|
model: openai.gpt-4.1-mini
|
||||||
|
temperature: 0
|
||||||
|
max_tokens: 8
|
||||||
|
timeout_seconds: 5
|
||||||
router:
|
router:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
temperature: 0
|
temperature: 0
|
||||||
max_tokens: 500
|
max_tokens: 768
|
||||||
guardrail:
|
guardrail:
|
||||||
provider: oci_openai
|
provider: oci_openai
|
||||||
model: openai.gpt-4.1
|
model: openai.gpt-4.1
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,95 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
LIB = ROOT / "libs" / "agent_framework" / "src"
|
||||||
|
if str(LIB) not in sys.path:
|
||||||
|
sys.path.insert(0, str(LIB))
|
||||||
|
|
||||||
|
from agent_framework.llm.profile_resolver import LLMProfileResolver
|
||||||
|
|
||||||
|
|
||||||
|
def _settings(path: Path):
|
||||||
|
return SimpleNamespace(
|
||||||
|
LLM_PROFILES_PATH=str(path),
|
||||||
|
LLM_PROVIDER="oci_openai",
|
||||||
|
OCI_GENAI_MODEL="openai.gpt-4.1",
|
||||||
|
LLM_TEMPERATURE=0.2,
|
||||||
|
LLM_MAX_TOKENS=2048,
|
||||||
|
LLM_TIMEOUT_SECONDS=120,
|
||||||
|
OCI_GENAI_BASE_URL="",
|
||||||
|
OCI_GENAI_API_KEY=None,
|
||||||
|
OCI_GENAI_PROJECT_OCID=None,
|
||||||
|
OCI_AUTH_MODE="config_file",
|
||||||
|
OCI_GENAI_ENDPOINT=None,
|
||||||
|
OCI_REGION="",
|
||||||
|
model_fields_set=set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_profile_max_tokens_wins_over_env_and_legacy_fallback(tmp_path, monkeypatch):
|
||||||
|
profile = tmp_path / "llm_profiles.yaml"
|
||||||
|
profile.write_text(
|
||||||
|
"profiles:\n"
|
||||||
|
" default:\n"
|
||||||
|
" provider: oci_openai\n"
|
||||||
|
" router:\n"
|
||||||
|
" max_tokens: 768\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
monkeypatch.setenv("LLM_MAX_TOKENS", "999")
|
||||||
|
resolver = LLMProfileResolver.from_settings(_settings(profile))
|
||||||
|
assert resolver.resolve_configured_value(
|
||||||
|
"router", "max_tokens", env_var="LLM_MAX_TOKENS", fallback=512
|
||||||
|
) == 768
|
||||||
|
|
||||||
|
|
||||||
|
def test_env_max_tokens_wins_when_profiles_file_is_absent(tmp_path, monkeypatch):
|
||||||
|
missing = tmp_path / "missing.yaml"
|
||||||
|
monkeypatch.setenv("LLM_MAX_TOKENS", "901")
|
||||||
|
resolver = LLMProfileResolver.from_settings(_settings(missing))
|
||||||
|
assert resolver.resolve_configured_value(
|
||||||
|
"router", "max_tokens", env_var="LLM_MAX_TOKENS", fallback=512
|
||||||
|
) == 901
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_fallback_is_kept_when_yaml_and_env_are_absent(tmp_path, monkeypatch):
|
||||||
|
missing = tmp_path / "missing.yaml"
|
||||||
|
monkeypatch.delenv("LLM_MAX_TOKENS", raising=False)
|
||||||
|
resolver = LLMProfileResolver.from_settings(_settings(missing))
|
||||||
|
assert resolver.resolve_configured_value(
|
||||||
|
"router", "max_tokens", env_var="LLM_MAX_TOKENS", fallback=512
|
||||||
|
) == 512
|
||||||
|
|
||||||
|
|
||||||
|
def test_canonical_profiles_keep_existing_budgets_and_router_uses_largest_value():
|
||||||
|
profile_path = ROOT / "libs" / "agent_framework" / "config" / "llm_profiles.yaml"
|
||||||
|
profiles = yaml.safe_load(profile_path.read_text(encoding="utf-8"))["profiles"]
|
||||||
|
assert profiles["router"]["max_tokens"] == 768
|
||||||
|
assert profiles["processing_interruption_classifier"]["max_tokens"] == 8
|
||||||
|
assert profiles["mcp_parameter_extraction"]["max_tokens"] == 80
|
||||||
|
assert profiles["guardrail"]["max_tokens"] == 600
|
||||||
|
assert profiles["rag_rewriter"]["max_tokens"] == 300
|
||||||
|
assert profiles["rag_compressor"]["max_tokens"] == 1200
|
||||||
|
assert profiles["summary_memory"]["max_tokens"] == 1200
|
||||||
|
|
||||||
|
|
||||||
|
def test_inference_points_use_fallback_max_tokens_instead_of_direct_max_tokens():
|
||||||
|
checks = {
|
||||||
|
"libs/agent_framework/src/agent_framework/routing/enterprise_router.py": [256, 512, 768],
|
||||||
|
"libs/agent_framework/src/agent_framework/runtime/agent_runtime.py": [80],
|
||||||
|
"libs/agent_framework/src/agent_framework/channels/interruption.py": [8],
|
||||||
|
"libs/agent_framework/src/agent_framework/guardrails/llm_rails.py": [600],
|
||||||
|
"libs/agent_framework/src/agent_framework/rag/rag_service.py": [300],
|
||||||
|
}
|
||||||
|
for relative, values in checks.items():
|
||||||
|
text = (ROOT / relative).read_text(encoding="utf-8")
|
||||||
|
for value in values:
|
||||||
|
assert f"fallback_max_tokens={value}" in text
|
||||||
|
assert f"max_tokens={value}," not in text.replace(f"fallback_max_tokens={value},", "")
|
||||||
Reference in New Issue
Block a user