Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Awaitable, Callable, Dict, List, Literal, Optional, Union
from typing import Any, Awaitable, Callable, Dict, List, Literal, Optional, Union

from autogen_core import ComponentModel
from autogen_core.models import ModelCapabilities, ModelInfo # type: ignore
Expand Down Expand Up @@ -57,6 +57,10 @@ class CreateArguments(TypedDict, total=False):
- 'low': Faster responses with less reasoning
- 'medium': Balanced reasoning and speed
- 'high': More thorough reasoning, may take longer"""
extra_body: Optional[Dict[str, Any]]
"""Additional JSON properties to include in the request body, passed directly to the API.
Useful for provider-specific parameters not covered by the standard OpenAI interface
(e.g. ``{"enable_thinking": false}`` for some Qwen-compatible endpoints)."""


AsyncAzureADTokenProvider = Callable[[], Union[str, Awaitable[str]]]
Expand Down Expand Up @@ -108,6 +112,8 @@ class CreateArgumentsConfigModel(BaseModel):
parallel_tool_calls: bool | None = None
# Controls the amount of effort the model uses for reasoning (reasoning models only)
reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None
# Additional JSON properties passed directly to the API request body
extra_body: Dict[str, Any] | None = None


class BaseOpenAIClientConfigurationConfigModel(CreateArgumentsConfigModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3378,3 +3378,46 @@ async def test_reasoning_effort_validation() -> None:
}

ChatCompletionClient.load_component(config)


@pytest.mark.asyncio
async def test_extra_body_parameter() -> None:
"""Test that extra_body is preserved through load_component round-trip (fixes #7418)."""

from autogen_core.models import ChatCompletionClient

# Test OpenAI client with extra_body via constructor
openai_client = OpenAIChatCompletionClient(
model="gpt-5",
api_key="fake_key",
extra_body={"enable_thinking": False},
)
assert openai_client._create_args["extra_body"] == {"enable_thinking": False} # pyright: ignore[reportPrivateUsage]

# Test that extra_body survives load_component round-trip (was silently dropped before)
config = {
"provider": "OpenAIChatCompletionClient",
"config": {
"model": "gpt-5",
"api_key": "fake_key",
"extra_body": {"enable_thinking": False},
},
}
loaded_client = ChatCompletionClient.load_component(config)
assert loaded_client._create_args["extra_body"] == {"enable_thinking": False} # type: ignore[attr-defined] # pyright: ignore[reportPrivateUsage, reportUnknownMemberType, reportAttributeAccessIssue]

# Test Azure OpenAI client with extra_body via constructor
azure_client = AzureOpenAIChatCompletionClient(
model="gpt-5",
azure_endpoint="fake_endpoint",
azure_deployment="gpt-5-2025-08-07",
api_version="2025-02-01-preview",
api_key="fake_key",
extra_body={"reasoning": {"effort": "high"}},
)
assert azure_client._create_args["extra_body"] == {"reasoning": {"effort": "high"}} # pyright: ignore[reportPrivateUsage]

# Test that extra_body survives dump/load cycle
dumped = openai_client.dump_component()
reloaded = OpenAIChatCompletionClient.load_component(dumped)
assert reloaded._create_args["extra_body"] == {"enable_thinking": False} # pyright: ignore[reportPrivateUsage]