* Python: fix ConnectTimeout on multi-turn FoundryAgent conversations (#6241) Expose a `timeout` parameter on `RawFoundryAgentChatClient`, `_FoundryAgentChatClient`, `RawFoundryAgent`, `FoundryAgent`, and `RawOpenAIChatClient` so callers can override the HTTP timeout used by the underlying AsyncOpenAI client. Root cause: `RawFoundryAgentChatClient.__init__` called `project_client.get_openai_client()` without configuring any timeout, inheriting the OpenAI SDK default of `httpx.Timeout(connect=5.0)`. When connections are recycled between turns under load, the 5 s connect timeout fires and surfaces as `openai.APITimeoutError`. Fix: - `load_openai_service_settings` (`_shared.py`): accept `timeout` and include it in `client_args` for all three `AsyncOpenAI`/ `AsyncAzureOpenAI` construction paths. - `RawOpenAIChatClient.__init__` (`_chat_client.py`): accept `timeout` and forward to `load_openai_service_settings`. - `RawFoundryAgentChatClient.__init__` (`_agent.py`): accept `timeout` and set `openai_client.timeout = timeout` on the client returned by `get_openai_client()` before passing it to the base class. - `_FoundryAgentChatClient`, `RawFoundryAgent`, `FoundryAgent`: accept and propagate `timeout` through the construction chain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add timeout parameter to FoundryAgent and RawOpenAIChatClient Expose a timeout parameter on RawFoundryAgentChatClient, _FoundryAgentChatClient, RawFoundryAgent, FoundryAgent, and RawOpenAIChatClient. When provided, the value is applied to the underlying AsyncOpenAI client so that connect timeouts under load or after connection recycling can be tuned by callers. Previously, get_openai_client() was called without any timeout override, so the SDK default of httpx.Timeout(connect=5.0) was inherited and could fire on multi-turn conversations where the underlying connection is recycled between turns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Add `timeout` parameter to `FoundryAgent` to fix `ConnectTimeout` on multi-turn conversations Fixes #6241 * fix(foundry): use with_options to avoid mutating shared OpenAI client timeout (#6241) Replace direct assignment with in RawFoundryAgentChatClient.__init__. The Azure AI Projects SDK caches and returns a shared AsyncOpenAI client per AIProjectClient. Mutating its .timeout attribute leaked the override to all other code paths sharing that client (other agents, user code). with_options() returns a new client instance with the override applied, leaving the original shared client untouched. Update tests to assert with_options is called with the correct timeout and that the original shared client's timeout attribute is not mutated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(foundry): assert with_options return value flows to instance.client (#6241) The four timeout propagation tests verified that with_options was called but did not confirm that the returned (timeout-configured) client was actually stored on the instance. A silent discard of the return value would have left the tests green while the timeout had no effect. Each test now captures the constructed instance and asserts: assert <instance>.client is openai_client_mock.with_options.return_value Affected tests: - test_raw_foundry_agent_chat_client_init_applies_timeout_to_openai_client - test_raw_foundry_agent_chat_client_init_applies_timeout_with_preview_enabled - test_foundry_agent_chat_client_init_propagates_timeout - test_foundry_agent_init_propagates_timeout_to_openai_client Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Agent Framework Foundry
This package contains the Microsoft Foundry integrations for Microsoft Agent Framework, including Foundry chat clients, preconfigured Foundry agents, Foundry embedding clients, and Foundry memory providers.
Toolboxes
A toolbox is a named, versioned bundle of hosted tool configurations — code interpreter, file search, image generation, MCP, web search, and so on — stored inside a Microsoft Foundry project. Toolboxes let you manage tool configuration once and reuse it across agents.
Authoring a toolbox
Toolboxes can be authored two ways:
- Foundry portal — create and version toolboxes through the UI without touching code.
- Programmatically — use the
azure-ai-projectsSDK to create, update, and version toolboxes from Python.
Toolbox authoring APIs (
ToolboxVersionObject,ToolboxObject,project_client.beta.toolboxes.*) requireazure-ai-projects>=2.1.0. Earlier versions can only consume toolboxes that already exist.
Using toolboxes with FoundryAgent
For hosted FoundryAgent, the toolbox must already be attached to the agent in the Microsoft Foundry project. Once attached, the agent invokes its toolbox tools transparently — no client-side wiring required — and you interact with the agent the same way you would with any other tool-equipped Foundry agent.
Using toolboxes with FoundryChatClient
Each toolbox is reachable as an MCP server. Connect to the toolbox's MCP endpoint with MCPStreamableHTTPTool — the agent then discovers and calls its tools over MCP at runtime:
from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.foundry import FoundryChatClient
async with Agent(
client=FoundryChatClient(...),
instructions="You are a helpful assistant. Use the toolbox tools when useful.",
tools=MCPStreamableHTTPTool(
name="my_toolbox",
description="Tools served by my Foundry toolbox",
url="https://<your-toolbox-mcp-endpoint>",
),
) as agent:
result = await agent.run("What tools are available?")
print(result.text)
Hosted tool factories
FoundryChatClient exposes static factory methods that return Foundry SDK tool
configurations ready to pass to an Agent's tools=[...] argument. These
factories don't require a FoundryChatClient instance — you can call them
statically and reuse the same tool configuration across agents.
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
agent = Agent(
client=FoundryChatClient(...),
instructions="...",
tools=[
FoundryChatClient.get_web_search_tool(),
FoundryChatClient.get_code_interpreter_tool(),
],
)
Generally available factories: get_code_interpreter_tool,
get_file_search_tool, get_web_search_tool,
get_image_generation_tool, get_mcp_tool.
Choosing a web grounding tool.
get_web_search_toolis the recommended default — it requires no separate Bing resource and works with Azure OpenAI models out of the box. Reach forget_bing_grounding_tool(experimental, see below) when you need finer Bing parameters (count,freshness,market,set_lang), are grounding non-OpenAI Foundry models, or are migrating from Grounding with Bing Search on the classic platform — it requires a Grounding with Bing Search Azure resource that you manage.get_bing_custom_search_tool(also experimental) is for grounding restricted to a curated list of domains via a Bing Custom Search instance. See the web grounding overview for the full comparison.
Experimental —
ExperimentalFeature.FOUNDRY_TOOLS. The following factories wrap GA Foundry tool SDK classes but are new wrappers inagent-framework-foundryand may change before the wrappers themselves reach GA. Calls emit anExperimentalWarningthe first time theFOUNDRY_TOOLSfeature is exercised in a process (then deduplicated).
| Factory | Foundry SDK tool |
|---|---|
get_azure_ai_search_tool(index_connection_id, index_name, ...) |
AzureAISearchTool |
get_bing_grounding_tool(connection_id, ...) |
BingGroundingTool |
Experimental —
ExperimentalFeature.FOUNDRY_PREVIEW_TOOLS. The following factories wrap preview Foundry tool SDK types — the underlying Foundry capability itself is in preview and may change or be removed before reaching GA. Calls emit a separateExperimentalWarningthe first time theFOUNDRY_PREVIEW_TOOLSfeature is exercised in a process (then deduplicated). UseFOUNDRY_TOOLSfor "wrapper is new" andFOUNDRY_PREVIEW_TOOLSfor "underlying Foundry feature is preview".
| Factory | Foundry SDK tool |
|---|---|
get_sharepoint_tool(connection_id) |
SharepointPreviewTool |
get_fabric_tool(connection_id) |
MicrosoftFabricPreviewTool |
get_memory_search_tool(memory_store_name, scope, ...) |
MemorySearchPreviewTool |
get_computer_use_tool(environment, display_width, display_height) |
ComputerUsePreviewTool |
get_browser_automation_tool(connection_id) |
BrowserAutomationPreviewTool |
get_bing_custom_search_tool(connection_id, instance_name, ...) |
BingCustomSearchPreviewTool |
get_a2a_tool(base_url=..., project_connection_id=..., ...) |
A2APreviewTool |
Publishing an agent as a Foundry prompt agent
Experimental —
ExperimentalFeature.TO_PROMPT_AGENT.to_prompt_agentis a preview API and may change before reaching GA. The warning fires the first time theTO_PROMPT_AGENTfeature is exercised in a process and is then deduplicated.
to_prompt_agent(agent) converts an Agent whose chat client is a
FoundryChatClient into a Foundry PromptAgentDefinition that can be
published with AIProjectClient.agents.create_version(...). The model is read
from default_options["model"] first and falls back to the bound
FoundryChatClient.model (matching Agent.__init__'s resolution order), so
the same agent definition you run locally can be published as a hosted prompt
agent without restating the model deployment name.
Every generation parameter that has an Agent Framework equivalent is sourced
from agent.default_options and translated into the matching Foundry shape by
_prepare_prompt_agent_options (a module-private helper in
agent_framework_foundry._to_prompt_agent that reuses the chat client's own
request-path helpers):
default_options key |
PromptAgentDefinition field |
|---|---|
temperature |
temperature |
top_p |
top_p |
tool_choice (dropped when no tools) |
tool_choice (str / ToolChoiceFunction / ToolChoiceAllowed) |
reasoning (dict or Reasoning) |
reasoning |
response_format (dict or BaseModel) |
text.format |
verbosity |
text.verbosity |
text |
merged into text |
This keeps the Agent as the single source of truth for everything it can
already express. Only Foundry-specific fields with no Agent Framework
equivalent are accepted as keyword arguments on to_prompt_agent:
structured_inputs—dict[str, StructuredInputDefinition]rai_config—RaiConfig
import asyncio
import os
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient, to_prompt_agent
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import AzureCliCredential
async def main() -> None:
credential = AzureCliCredential()
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
agent = Agent(
client=FoundryChatClient(
project_endpoint=project_endpoint,
model="gpt-4o",
credential=credential,
),
name="travel-agent",
description="Helps Contoso employees book travel.",
instructions="You are a helpful travel assistant.",
tools=[
FoundryChatClient.get_web_search_tool(),
FoundryChatClient.get_code_interpreter_tool(),
],
# Generation parameters set on the Agent flow through automatically.
default_options={
"temperature": 0.3,
"top_p": 0.95,
"reasoning": {"effort": "medium"},
},
)
definition = to_prompt_agent(agent)
project_client = AIProjectClient(endpoint=project_endpoint, credential=credential)
created = await project_client.agents.create_version(
agent_name=agent.name,
definition=definition,
description=agent.description,
)
print(f"Published {created.name} v{created.version}")
asyncio.run(main())
Behaviour:
-
agent.clientmust be aFoundryChatClient(or subclass) — otherwise the converter raisesTypeError. -
The bound client must have a
modelset — otherwise the converter raisesValueError. -
Foundry SDK tool instances returned by
FoundryChatClient.get_*_tool()are passed through unchanged. -
AF
FunctionToolinstances (and@tool-decorated callables) are emitted as FoundryFunctionTooldeclarations — the prompt agent receives the schema only, not the Python implementation. To execute the function when invoking the deployed prompt agent, connect withFoundryAgentand pass the same callable viatools=:from agent_framework.foundry import FoundryAgent deployed = FoundryAgent( project_endpoint=project_endpoint, agent_name="travel-agent", credential=credential, tools=[book_hotel], # same @tool-decorated callable used at publish time ) result = await deployed.run("Book me a hotel in Seattle for 3 nights.")FoundryAgentruns the function locally when the prompt agent calls it, so the declaration on the server and the implementation on the client stay in sync via the shared@tooldefinition. -
Local Agent Framework MCP tools cannot be published as prompt-agent tools — the converter raises
ValueErrorand points atFoundryChatClient.get_mcp_tool(...)for hosted MCP servers.
See the runnable example under samples/02-agents/providers/foundry/:
foundry_prompt_agents.py— publish withto_prompt_agent, then connect back withFoundryAgentand execute the same local@toolcallable that the deployed prompt agent invokes by name.