Skip to content

Commit f6ba91b

Browse files
Runtime handling updates (#3451)
## Summary - Refresh runtime handling around session and tool-call flows. - Adjust model configuration metadata used by runtime integrations. - Add focused coverage for the updated behavior. ## Validation - .venv/bin/python -m pytest tests/model_settings/test_serialization.py tests/models/test_trace_config.py tests/mcp/test_streamable_http_client_factory.py tests/test_run_context_approvals.py tests/test_run_state.py::TestRunState::test_trace_api_key_serialization_is_opt_in tests/realtime/test_session.py - .venv/bin/ruff check <touched files> - .venv/bin/ruff format --check <touched files> - git diff --check
1 parent 65774ce commit f6ba91b

16 files changed

Lines changed: 679 additions & 157 deletions

src/agents/extensions/models/any_llm_model.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
response_terminal_failure_error,
3535
)
3636
from ...models._retry_runtime import should_disable_provider_managed_retries
37+
from ...models._trace import model_config_for_trace
3738
from ...models.chatcmpl_converter import Converter
3839
from ...models.chatcmpl_helpers import HEADERS, HEADERS_OVERRIDE, ChatCmplHelpers
3940
from ...models.chatcmpl_stream_handler import ChatCmplStreamHandler
@@ -467,12 +468,11 @@ async def _get_response_via_chat(
467468
) -> ModelResponse:
468469
with generation_span(
469470
model=str(self.model),
470-
model_config=model_settings.to_json_dict()
471-
| {
472-
"base_url": str(self.base_url or ""),
473-
"provider": self._provider_name,
474-
"model_impl": "any-llm",
475-
},
471+
model_config=model_config_for_trace(
472+
model_settings,
473+
base_url=self.base_url or "",
474+
extra_config={"provider": self._provider_name, "model_impl": "any-llm"},
475+
),
476476
disabled=tracing.is_disabled(),
477477
) as span_generation:
478478
response = await self._fetch_chat_response(
@@ -570,12 +570,11 @@ async def _stream_response_via_chat(
570570
) -> AsyncIterator[TResponseStreamEvent]:
571571
with generation_span(
572572
model=str(self.model),
573-
model_config=model_settings.to_json_dict()
574-
| {
575-
"base_url": str(self.base_url or ""),
576-
"provider": self._provider_name,
577-
"model_impl": "any-llm",
578-
},
573+
model_config=model_config_for_trace(
574+
model_settings,
575+
base_url=self.base_url or "",
576+
extra_config={"provider": self._provider_name, "model_impl": "any-llm"},
577+
),
579578
disabled=tracing.is_disabled(),
580579
) as span_generation:
581580
response, stream = await self._fetch_chat_response(

src/agents/extensions/models/litellm_model.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from ...model_settings import ModelSettings
4444
from ...models._openai_retry import get_openai_retry_advice
4545
from ...models._retry_runtime import should_disable_provider_managed_retries
46+
from ...models._trace import model_config_for_trace
4647
from ...models.chatcmpl_converter import Converter
4748
from ...models.chatcmpl_helpers import HEADERS, HEADERS_OVERRIDE, ChatCmplHelpers
4849
from ...models.chatcmpl_stream_handler import ChatCmplStreamHandler
@@ -213,8 +214,11 @@ async def get_response(
213214
) -> ModelResponse:
214215
with generation_span(
215216
model=str(self.model),
216-
model_config=model_settings.to_json_dict()
217-
| {"base_url": str(self.base_url or ""), "model_impl": "litellm"},
217+
model_config=model_config_for_trace(
218+
model_settings,
219+
base_url=self.base_url or "",
220+
extra_config={"model_impl": "litellm"},
221+
),
218222
disabled=tracing.is_disabled(),
219223
) as span_generation:
220224
response = await self._fetch_response(
@@ -327,8 +331,11 @@ async def stream_response(
327331
) -> AsyncIterator[TResponseStreamEvent]:
328332
with generation_span(
329333
model=str(self.model),
330-
model_config=model_settings.to_json_dict()
331-
| {"base_url": str(self.base_url or ""), "model_impl": "litellm"},
334+
model_config=model_config_for_trace(
335+
model_settings,
336+
base_url=self.base_url or "",
337+
extra_config={"model_impl": "litellm"},
338+
),
332339
disabled=tracing.is_disabled(),
333340
) as span_generation:
334341
response, stream = await self._fetch_response(

src/agents/mcp/server.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def _create_default_streamable_http_client(
9999
timeout: httpx.Timeout | None = None,
100100
auth: httpx.Auth | None = None,
101101
) -> httpx.AsyncClient:
102-
kwargs: dict[str, Any] = {"follow_redirects": True}
102+
kwargs: dict[str, Any] = {"follow_redirects": False}
103103
if timeout is not None:
104104
kwargs["timeout"] = timeout
105105
if headers is not None:
@@ -1441,8 +1441,9 @@ def create_streams(
14411441
auth=self.params.get("auth"),
14421442
transport_factory=_InitializedNotificationTolerantStreamableHTTPTransport,
14431443
)
1444-
if httpx_client_factory is not None:
1445-
kwargs["httpx_client_factory"] = httpx_client_factory
1444+
kwargs["httpx_client_factory"] = (
1445+
httpx_client_factory or _create_default_streamable_http_client
1446+
)
14461447
if "auth" in self.params:
14471448
kwargs["auth"] = self.params["auth"]
14481449
return streamablehttp_client(**kwargs)

src/agents/model_settings.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ class MCPToolChoice:
6060
Headers: TypeAlias = Mapping[str, str | Omit]
6161
ToolChoice: TypeAlias = Literal["auto", "required", "none"] | str | MCPToolChoice | None
6262

63+
_TRACEABLE_MODEL_SETTING_FIELDS = (
64+
"temperature",
65+
"top_p",
66+
"frequency_penalty",
67+
"presence_penalty",
68+
"tool_choice",
69+
"parallel_tool_calls",
70+
"truncation",
71+
"max_tokens",
72+
"reasoning",
73+
"verbosity",
74+
"metadata",
75+
"store",
76+
"prompt_cache_retention",
77+
"include_usage",
78+
"response_include",
79+
"top_logprobs",
80+
"retry",
81+
"context_management",
82+
)
83+
6384

6485
@dataclass
6586
class ModelSettings:
@@ -199,6 +220,11 @@ def resolve(self, override: ModelSettings | None) -> ModelSettings:
199220
def to_json_dict(self) -> dict[str, Any]:
200221
return cast(dict[str, Any], TypeAdapter(ModelSettings).dump_python(self, mode="json"))
201222

223+
def to_traceable_dict(self) -> dict[str, Any]:
224+
"""Serialize settings for tracing without provider-specific request extras."""
225+
payload = self.to_json_dict()
226+
return {key: payload[key] for key in _TRACEABLE_MODEL_SETTING_FIELDS if key in payload}
227+
202228

203229
def _merge_retry_settings(
204230
inherited: ModelRetrySettings | None,

src/agents/models/_trace.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
from urllib.parse import urlsplit, urlunsplit
5+
6+
from ..model_settings import ModelSettings
7+
8+
9+
def sanitize_url_for_trace(url: object) -> str:
10+
"""Return a URL safe for tracing by removing auth material and request parameters."""
11+
try:
12+
parts = urlsplit(str(url))
13+
except ValueError:
14+
return ""
15+
16+
netloc = parts.netloc.rsplit("@", 1)[-1]
17+
return urlunsplit((parts.scheme, netloc, parts.path, "", ""))
18+
19+
20+
def model_config_for_trace(
21+
model_settings: ModelSettings,
22+
*,
23+
base_url: object | None = None,
24+
extra_config: dict[str, Any] | None = None,
25+
) -> dict[str, Any]:
26+
config = model_settings.to_traceable_dict()
27+
if base_url is not None:
28+
config["base_url"] = sanitize_url_for_trace(base_url)
29+
if extra_config:
30+
config.update(extra_config)
31+
return config

src/agents/models/openai_chatcompletions.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from ..util._json import _to_dump_compatible
3434
from ._openai_retry import get_openai_retry_advice
3535
from ._retry_runtime import should_disable_provider_managed_retries
36+
from ._trace import model_config_for_trace
3637
from .chatcmpl_converter import Converter
3738
from .chatcmpl_helpers import HEADERS, HEADERS_OVERRIDE, ChatCmplHelpers
3839
from .chatcmpl_stream_handler import ChatCmplStreamHandler
@@ -147,7 +148,7 @@ async def get_response(
147148

148149
with generation_span(
149150
model=str(self.model),
150-
model_config=model_settings.to_json_dict() | {"base_url": str(self._client.base_url)},
151+
model_config=model_config_for_trace(model_settings, base_url=self._client.base_url),
151152
disabled=tracing.is_disabled(),
152153
) as span_generation:
153154
response = await self._fetch_response(
@@ -281,7 +282,7 @@ async def stream_response(
281282

282283
with generation_span(
283284
model=str(self.model),
284-
model_config=model_settings.to_json_dict() | {"base_url": str(self._client.base_url)},
285+
model_config=model_config_for_trace(model_settings, base_url=self._client.base_url),
285286
disabled=tracing.is_disabled(),
286287
) as span_generation:
287288
response, stream = await self._fetch_response(

0 commit comments

Comments
 (0)