mirror of
https://github.com/hoshikawa2/agent_platform_oci.git
synced 2026-07-09 14:04:19 +00:00
bugfix Alex 2026-06-30
This commit is contained in:
164
.env
Normal file
164
.env
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
###############################################################################
|
||||||
|
# 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_openai
|
||||||
|
LLM_TEMPERATURE=0.2
|
||||||
|
LLM_MAX_TOKENS=2048
|
||||||
|
LLM_TIMEOUT_SECONDS=120
|
||||||
|
|
||||||
|
# OCI OpenAI-compatible endpoint
|
||||||
|
OCI_GENAI_BASE_URL=https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1
|
||||||
|
OCI_GENAI_MODEL=openai.gpt-4.1
|
||||||
|
OCI_GENAI_API_KEY=sk-ph3FgX6iP3fxAQCXb9IpPIDTadkeeYAWntUWhzcWysIM6zsS
|
||||||
|
OCI_GENAI_PROJECT_OCID=
|
||||||
|
|
||||||
|
# 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=sqlite
|
||||||
|
MEMORY_REPOSITORY_PROVIDER=sqlite
|
||||||
|
CHECKPOINT_REPOSITORY_PROVIDER=sqlite
|
||||||
|
SQLITE_DB_PATH=./data/agent_framework.db
|
||||||
|
|
||||||
|
# Autonomous Database
|
||||||
|
ADB_USER=admin
|
||||||
|
ADB_PASSWORD=Moniquinha1972
|
||||||
|
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=memory
|
||||||
|
GRAPH_STORE_PROVIDER=memory
|
||||||
|
RAG_TOP_K=5
|
||||||
|
EMBEDDING_PROVIDER=mock
|
||||||
|
OCI_EMBEDDING_MODEL=cohere.embed-multilingual-v3.0
|
||||||
|
RAG_FILE_GLOBS=*.md,*.txt,*.yaml,*.yml,*.json
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Observabilidade
|
||||||
|
###############################################################################
|
||||||
|
ENABLE_LANGFUSE=true
|
||||||
|
LANGFUSE_TRACE_MODE=compact # Opcional: verbose, compact
|
||||||
|
LANGFUSE_PUBLIC_KEY=pk-lf-2f9da109-5b0f-4c78-b61d-9598ed787eba
|
||||||
|
LANGFUSE_SECRET_KEY=sk-lf-a4cb0cdd-f2ea-4468-9911-cebeb91ba944
|
||||||
|
LANGFUSE_HOST=http://localhost:3005
|
||||||
|
ENABLE_OTEL=false
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT=
|
||||||
|
OTEL_SERVICE_NAME=ai-agent-template
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Analytics / Observer corporativo
|
||||||
|
###############################################################################
|
||||||
|
# Quando true, AgentObserver publica eventos IC.*, NOC.* e GRL.* nos providers abaixo.
|
||||||
|
ENABLE_ANALYTICS=false
|
||||||
|
# Providers aceitos: oci_streaming,pubsub,noop
|
||||||
|
ANALYTICS_PROVIDERS=pubsub
|
||||||
|
# Compatibilidade FIRST/TIM: pode informar AGENT_PUBSUB_TOPIC diretamente.
|
||||||
|
AGENT_PUBSUB_TOPIC=
|
||||||
|
GCP_PUBSUB_TOPIC_PATH=
|
||||||
|
GCP_PROJECT_ID=
|
||||||
|
GCP_PUBSUB_TOPIC=
|
||||||
|
GCP_PUBSUB_TIMEOUT_SECONDS=30
|
||||||
|
# Credencial GCP segue padrão Google:
|
||||||
|
# GOOGLE_APPLICATION_CREDENTIALS=/secrets/gcp-service-account.json
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# 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
|
||||||
|
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
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# MCP / Tools
|
||||||
|
###############################################################################
|
||||||
|
ENABLE_MCP_TOOLS=true
|
||||||
|
MCP_SERVERS_CONFIG_PATH=./agent_template_backend/config/mcp_servers.yaml
|
||||||
|
TOOLS_CONFIG_PATH=./agent_template_backend/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=sqlite
|
||||||
|
IDENTITY_CONFIG_PATH=./agent_template_backend/config/identity.yaml
|
||||||
|
MCP_PARAMETER_MAPPING_PATH=./agent_template_backend/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
|
||||||
194
tests/unit/test_telemetry_langfuse_compact.py
Normal file
194
tests/unit/test_telemetry_langfuse_compact.py
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from agent_framework.observability.context import clear_observability_context
|
||||||
|
from agent_framework.observability.telemetry import Telemetry
|
||||||
|
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
ENABLE_LANGFUSE = False
|
||||||
|
ENABLE_OTEL = False
|
||||||
|
LANGFUSE_TRACE_MODE = "compact"
|
||||||
|
|
||||||
|
|
||||||
|
class FakeObservation:
|
||||||
|
_next_id = 1
|
||||||
|
|
||||||
|
def __init__(self, kwargs):
|
||||||
|
self.kwargs = kwargs
|
||||||
|
self.id = f"obs-{FakeObservation._next_id}"
|
||||||
|
self.trace_id = "trace-123"
|
||||||
|
FakeObservation._next_id += 1
|
||||||
|
self.updates = []
|
||||||
|
self.trace_updates = []
|
||||||
|
self.trace_io_updates = []
|
||||||
|
|
||||||
|
def update(self, **kwargs):
|
||||||
|
self.updates.append(kwargs)
|
||||||
|
|
||||||
|
def update_trace(self, **kwargs):
|
||||||
|
self.trace_updates.append(kwargs)
|
||||||
|
|
||||||
|
def set_trace_io(self, **kwargs):
|
||||||
|
self.trace_io_updates.append(kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeContextManager:
|
||||||
|
def __init__(self, observation):
|
||||||
|
self.observation = observation
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.observation
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc, tb):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class FakePropagationContext:
|
||||||
|
def __init__(self, owner, kwargs):
|
||||||
|
self.owner = owner
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.owner.propagations.append(self.kwargs)
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc, tb):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class FakeLangfuse:
|
||||||
|
def __init__(self, *, legacy_api: bool = False):
|
||||||
|
self.observations = []
|
||||||
|
self.propagations = []
|
||||||
|
self.flush_count = 0
|
||||||
|
self.api = FakeApi() if legacy_api else None
|
||||||
|
|
||||||
|
def start_as_current_observation(self, **kwargs):
|
||||||
|
observation = FakeObservation(kwargs)
|
||||||
|
self.observations.append(observation)
|
||||||
|
return FakeContextManager(observation)
|
||||||
|
|
||||||
|
def propagate_attributes(self, **kwargs):
|
||||||
|
return FakePropagationContext(self, kwargs)
|
||||||
|
|
||||||
|
def flush(self):
|
||||||
|
self.flush_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
class FakeIngestionResponse:
|
||||||
|
errors = []
|
||||||
|
successes = []
|
||||||
|
|
||||||
|
|
||||||
|
class FakeIngestion:
|
||||||
|
def __init__(self):
|
||||||
|
self.batches = []
|
||||||
|
|
||||||
|
def batch(self, *, batch, metadata=None):
|
||||||
|
self.batches.append({"batch": batch, "metadata": metadata})
|
||||||
|
return FakeIngestionResponse()
|
||||||
|
|
||||||
|
|
||||||
|
class FakeApi:
|
||||||
|
def __init__(self):
|
||||||
|
self.ingestion = FakeIngestion()
|
||||||
|
|
||||||
|
|
||||||
|
def telemetry_with_fake_langfuse(*, legacy_api: bool = False):
|
||||||
|
FakeObservation._next_id = 1
|
||||||
|
telemetry = Telemetry(Settings())
|
||||||
|
telemetry.enabled = True
|
||||||
|
telemetry.langfuse = FakeLangfuse(legacy_api=legacy_api)
|
||||||
|
return telemetry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_compact_keeps_root_output_and_shows_only_aga_noc_events():
|
||||||
|
clear_observability_context()
|
||||||
|
telemetry = telemetry_with_fake_langfuse()
|
||||||
|
|
||||||
|
async with telemetry.span("agent.gateway_message", session_id="s1", input={"request": "cms"}, _root_span=True) as span:
|
||||||
|
await telemetry.event("IC.INTERNAL", {"step": "hidden"}, kind="ic")
|
||||||
|
await telemetry.event("NOC.001", {"step": "visible"}, kind="noc")
|
||||||
|
await telemetry.event("AGA.010", {"step": "visible"}, kind="ic")
|
||||||
|
span.set_output({"answer": "ok"})
|
||||||
|
|
||||||
|
names = [obs.kwargs["name"] for obs in telemetry.langfuse.observations]
|
||||||
|
assert names == ["agent.gateway_message", "NOC.001", "AGA.010"]
|
||||||
|
|
||||||
|
root = telemetry.langfuse.observations[0]
|
||||||
|
assert root.updates[-1]["input"] == {"request": "cms"}
|
||||||
|
assert root.updates[-1]["output"] == {"answer": "ok"}
|
||||||
|
assert root.trace_io_updates[-1] == {"input": {"request": "cms"}, "output": {"answer": "ok"}}
|
||||||
|
assert telemetry.langfuse.observations[1].kwargs.get("trace_context") is None
|
||||||
|
assert telemetry.langfuse.observations[2].kwargs.get("trace_context") is None
|
||||||
|
assert telemetry.langfuse.propagations[-1]["trace_name"] == "agent.gateway_message"
|
||||||
|
|
||||||
|
aggregated = root.updates[-1]["metadata"]["aggregated_events"]
|
||||||
|
assert [event["name"] for event in aggregated] == ["IC.INTERNAL", "NOC.001", "AGA.010"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_compact_generation_records_io_model_parameters_and_usage_details():
|
||||||
|
clear_observability_context()
|
||||||
|
telemetry = telemetry_with_fake_langfuse()
|
||||||
|
|
||||||
|
async with telemetry.span("agent.gateway_message", session_id="s1", input={"request": "cms"}, _root_span=True):
|
||||||
|
async with telemetry.generation_span(
|
||||||
|
name="llm.test",
|
||||||
|
model="test-model",
|
||||||
|
input=[{"role": "user", "content": "ping"}],
|
||||||
|
metadata={"profile_name": "test"},
|
||||||
|
model_parameters={"temperature": 0.2, "max_tokens": 100},
|
||||||
|
) as generation:
|
||||||
|
generation.set_output("pong")
|
||||||
|
generation.set_usage({"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2, "cost_usd": 0.01})
|
||||||
|
|
||||||
|
generation = telemetry.langfuse.observations[1]
|
||||||
|
assert generation.kwargs["name"] == "llm.test"
|
||||||
|
assert generation.kwargs["as_type"] == "generation"
|
||||||
|
assert generation.kwargs["input"] == [{"role": "user", "content": "ping"}]
|
||||||
|
assert generation.kwargs["model"] == "test-model"
|
||||||
|
assert generation.kwargs["model_parameters"] == {"temperature": 0.2, "max_tokens": 100}
|
||||||
|
assert "usage" not in generation.kwargs
|
||||||
|
assert "usage_details" not in generation.kwargs
|
||||||
|
assert generation.updates[-1]["input"] == [{"role": "user", "content": "ping"}]
|
||||||
|
assert generation.updates[-1]["output"] == "pong"
|
||||||
|
assert generation.updates[-1]["usage_details"] == {"input": 1, "output": 1}
|
||||||
|
assert generation.updates[-1]["cost_details"] == {"total": 0.01}
|
||||||
|
assert generation.kwargs.get("trace_context") is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_legacy_io_fallback_updates_same_root_and_generation_observations():
|
||||||
|
clear_observability_context()
|
||||||
|
telemetry = telemetry_with_fake_langfuse(legacy_api=True)
|
||||||
|
|
||||||
|
async with telemetry.span("agent.gateway_message", session_id="s1", input={"request": "cms"}, _root_span=True) as root:
|
||||||
|
async with telemetry.generation_span(
|
||||||
|
name="llm.test",
|
||||||
|
model="test-model",
|
||||||
|
input=[{"role": "user", "content": "ping"}],
|
||||||
|
model_parameters={"temperature": 0.2},
|
||||||
|
) as generation:
|
||||||
|
generation.set_output("pong")
|
||||||
|
generation.set_usage({"prompt_tokens": 2, "completion_tokens": 3, "total_tokens": 5})
|
||||||
|
root.set_output({"answer": "ok"})
|
||||||
|
|
||||||
|
batches = telemetry.langfuse.api.ingestion.batches
|
||||||
|
assert [event.type for item in batches for event in item["batch"]] == ["generation-update", "span-update"]
|
||||||
|
|
||||||
|
generation_event = batches[0]["batch"][0]
|
||||||
|
assert generation_event.body.id == "obs-2"
|
||||||
|
assert generation_event.body.trace_id == "trace-123"
|
||||||
|
assert generation_event.body.input == [{"role": "user", "content": "ping"}]
|
||||||
|
assert generation_event.body.output == "pong"
|
||||||
|
assert generation_event.body.usage_details == {"input": 2, "output": 3}
|
||||||
|
|
||||||
|
root_event = batches[1]["batch"][0]
|
||||||
|
assert root_event.body.id == "obs-1"
|
||||||
|
assert root_event.body.trace_id == "trace-123"
|
||||||
|
assert root_event.body.input == {"request": "cms"}
|
||||||
|
assert root_event.body.output == {"answer": "ok"}
|
||||||
|
assert len(telemetry.langfuse.observations) == 2
|
||||||
Reference in New Issue
Block a user