Skip to content

Commit 585f6ba

Browse files
fix(sdk): forward target context from decorators
1 parent 5c30630 commit 585f6ba

2 files changed

Lines changed: 81 additions & 7 deletions

File tree

sdks/python/src/agent_control/control_decorators.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ async def chat(message: str) -> str:
3939
from agent_control_telemetry import get_trace_context_from_provider
4040

4141
from agent_control import AgentControlClient
42-
from agent_control.evaluation import check_evaluation_with_local
42+
from agent_control._state import state
43+
from agent_control.evaluation import _resolve_session_target, check_evaluation_with_local
4344
from agent_control.observability import (
4445
get_logger,
4546
log_control_evaluation,
@@ -269,7 +270,14 @@ async def _evaluate(
269270
if span_id:
270271
headers["X-Span-Id"] = span_id
271272

272-
async with AgentControlClient(base_url=server_url) as client:
273+
target_type, target_id = _resolve_session_target(None, None)
274+
275+
async with AgentControlClient(
276+
base_url=server_url,
277+
api_key=state.api_key,
278+
api_key_header=state.api_key_header,
279+
runtime_token_cache=state.runtime_token_cache,
280+
) as client:
273281
# If we have controls, use local evaluation which handles both SDK and server controls
274282
if controls is not None:
275283
try:
@@ -282,6 +290,8 @@ async def _evaluate(
282290
step=step_obj,
283291
stage=stage, # type: ignore
284292
controls=controls,
293+
target_type=target_type,
294+
target_id=target_id,
285295
trace_id=trace_id,
286296
span_id=span_id,
287297
event_agent_name=event_agent_name,
@@ -369,13 +379,18 @@ async def _evaluate(
369379
)
370380

371381
# Fallback: server-only evaluation
382+
payload = {
383+
"agent_name": str(agent_name),
384+
"step": step,
385+
"stage": stage,
386+
}
387+
if target_type is not None and target_id is not None:
388+
payload["target_type"] = target_type
389+
payload["target_id"] = target_id
390+
372391
response = await client.http_client.post(
373392
"/api/v1/evaluation",
374-
json={
375-
"agent_name": str(agent_name),
376-
"step": step,
377-
"stage": stage
378-
},
393+
json=payload,
379394
headers=headers,
380395
)
381396
response.raise_for_status()

sdks/python/tests/test_control_decorators.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,65 @@ async def search_tool(query: str) -> str:
793793
assert captured_steps[0]["type"] == "tool"
794794

795795

796+
class TestEvaluateSessionContext:
797+
"""Tests for session context forwarding in the decorator evaluation path."""
798+
799+
@pytest.mark.asyncio
800+
async def test_evaluate_forwards_session_target_and_client_state(self):
801+
"""The decorator path should use the same session target as evaluate_controls()."""
802+
from agent_control.control_decorators import _evaluate
803+
from agent_control.runtime_auth import RuntimeTokenCache
804+
from agent_control_models import EvaluationResult
805+
806+
captured_client_kwargs = {}
807+
captured_eval_kwargs = {}
808+
runtime_token_cache = RuntimeTokenCache()
809+
810+
class FakeClient:
811+
def __init__(self, **kwargs):
812+
captured_client_kwargs.update(kwargs)
813+
814+
async def __aenter__(self):
815+
return self
816+
817+
async def __aexit__(self, exc_type, exc, tb):
818+
return None
819+
820+
async def fake_check_evaluation_with_local(**kwargs):
821+
captured_eval_kwargs.update(kwargs)
822+
return EvaluationResult(is_safe=True, confidence=1.0)
823+
824+
with (
825+
patch("agent_control.control_decorators.AgentControlClient", FakeClient),
826+
patch(
827+
"agent_control.control_decorators.check_evaluation_with_local",
828+
side_effect=fake_check_evaluation_with_local,
829+
),
830+
patch("agent_control.control_decorators.state.api_key", "session-key"),
831+
patch("agent_control.control_decorators.state.api_key_header", "X-Custom-Key"),
832+
patch(
833+
"agent_control.control_decorators.state.runtime_token_cache",
834+
runtime_token_cache,
835+
),
836+
patch("agent_control.control_decorators.state.target_type", "env"),
837+
patch("agent_control.control_decorators.state.target_id", "prod"),
838+
):
839+
result = await _evaluate(
840+
agent_name="agent",
841+
step={"type": "llm", "name": "chat", "input": "hello"},
842+
stage="pre",
843+
server_url="http://server.test",
844+
controls=[{"id": 1, "name": "control", "control": {}}],
845+
)
846+
847+
assert result["is_safe"] is True
848+
assert captured_client_kwargs["api_key"] == "session-key"
849+
assert captured_client_kwargs["api_key_header"] == "X-Custom-Key"
850+
assert captured_client_kwargs["runtime_token_cache"] is runtime_token_cache
851+
assert captured_eval_kwargs["target_type"] == "env"
852+
assert captured_eval_kwargs["target_id"] == "prod"
853+
854+
796855
# =============================================================================
797856
# STEERING CONTEXT TESTS
798857
# =============================================================================

0 commit comments

Comments
 (0)