Files
agent-framework/python/packages/foundry
T
Evan Mattson 6b94315161 Python: Add timeout parameter to FoundryAgent to fix ConnectTimeout on multi-turn conversations (#6263)
* 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>
6b94315161 · 2026-06-04 18:25:18 +00:00
History
..

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-projects SDK to create, update, and version toolboxes from Python.

Toolbox authoring APIs (ToolboxVersionObject, ToolboxObject, project_client.beta.toolboxes.*) require azure-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_tool is the recommended default — it requires no separate Bing resource and works with Azure OpenAI models out of the box. Reach for get_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 in agent-framework-foundry and may change before the wrappers themselves reach GA. Calls emit an ExperimentalWarning the first time the FOUNDRY_TOOLS feature 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 separate ExperimentalWarning the first time the FOUNDRY_PREVIEW_TOOLS feature is exercised in a process (then deduplicated). Use FOUNDRY_TOOLS for "wrapper is new" and FOUNDRY_PREVIEW_TOOLS for "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_agent is a preview API and may change before reaching GA. The warning fires the first time the TO_PROMPT_AGENT feature 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_inputsdict[str, StructuredInputDefinition]
  • rai_configRaiConfig
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.client must be a FoundryChatClient (or subclass) — otherwise the converter raises TypeError.

  • The bound client must have a model set — otherwise the converter raises ValueError.

  • Foundry SDK tool instances returned by FoundryChatClient.get_*_tool() are passed through unchanged.

  • AF FunctionTool instances (and @tool-decorated callables) are emitted as Foundry FunctionTool declarations — the prompt agent receives the schema only, not the Python implementation. To execute the function when invoking the deployed prompt agent, connect with FoundryAgent and pass the same callable via tools=:

    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.")
    

    FoundryAgent runs 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 @tool definition.

  • Local Agent Framework MCP tools cannot be published as prompt-agent tools — the converter raises ValueError and points at FoundryChatClient.get_mcp_tool(...) for hosted MCP servers.

See the runnable example under samples/02-agents/providers/foundry/:

  • foundry_prompt_agents.py — publish with to_prompt_agent, then connect back with FoundryAgent and execute the same local @tool callable that the deployed prompt agent invokes by name.