Python: update FoundryAgent for hosted agent sessions (#5447)

* fixes to FoundryAgent to connect to new hosted agents

Co-authored-by: Copilot <copilot@github.com>

* fix mypy

Co-authored-by: Copilot <copilot@github.com>

* Python: remove Foundry service session helpers

Remove the public hosted-agent service session CRUD helpers from FoundryAgent and drop the related feature-stage inventory entry.

Update the hosted-agent sample to create and delete service sessions directly through the preview AIProjectClient APIs, and tighten a few test harnesses surfaced by full workspace validation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix from merge

* fix hosted env detection

Co-authored-by: Copilot <copilot@github.com>

* reverted sample update

* fix tests and code

Co-authored-by: Copilot <copilot@github.com>

* remove aenter

* skipping some tests

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Eduard van Valkenburg
2026-04-24 09:25:03 +00:00
committed by GitHub
co-authored by Copilot Copilot
parent 63c0a51797
commit 62e02da698
15 changed files with 601 additions and 180 deletions
@@ -41,9 +41,10 @@ def _make_agent(
*,
response: AgentResponse | None = None,
stream_updates: list[AgentResponseUpdate] | None = None,
raw_agent: bool = True,
) -> MagicMock:
"""Create a mock agent implementing SupportsAgentRun."""
agent = MagicMock(spec=RawAgent)
agent = MagicMock(spec=RawAgent) if raw_agent else MagicMock()
agent.id = "test-agent"
agent.name = "Test Agent"
agent.description = "A mock agent for testing"
@@ -267,10 +268,18 @@ class TestNonStreaming:
async def test_chat_options_forwarded(self) -> None:
agent = _make_agent(
response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("ok")])])
response=AgentResponse(messages=[Message(role="assistant", contents=[Content.from_text("ok")])]),
raw_agent=True,
)
server = _make_server(agent)
resp = await _post(server, stream=False, temperature=0.5, top_p=0.9, max_output_tokens=1024)
resp = await _post(
server,
stream=False,
temperature=0.5,
top_p=0.9,
max_output_tokens=1024,
parallel_tool_calls=True,
)
assert resp.status_code == 200
agent.run.assert_awaited_once()
@@ -280,6 +289,7 @@ class TestNonStreaming:
assert options["temperature"] == 0.5
assert options["top_p"] == 0.9
assert options["max_tokens"] == 1024
assert options["allow_multiple_tool_calls"] is True
# endregion
@@ -289,6 +299,31 @@ class TestNonStreaming:
class TestStreaming:
async def test_chat_options_forwarded(self) -> None:
agent = _make_agent(
stream_updates=[AgentResponseUpdate(contents=[Content.from_text("ok")], role="assistant")],
raw_agent=True,
)
server = _make_server(agent)
resp = await _post(
server,
stream=True,
temperature=0.5,
top_p=0.9,
max_output_tokens=1024,
parallel_tool_calls=True,
)
assert resp.status_code == 200
agent.run.assert_called_once()
call_kwargs = agent.run.call_args.kwargs
assert call_kwargs["stream"] is True
options = call_kwargs["options"]
assert options["temperature"] == 0.5
assert options["top_p"] == 0.9
assert options["max_tokens"] == 1024
assert options["allow_multiple_tool_calls"] is True
async def test_basic_text_streaming(self) -> None:
agent = _make_agent(
stream_updates=[
@@ -1426,7 +1461,7 @@ class TestMultiTurnMixedContent:
assert body["status"] == "completed"
# Verify agent received text + image
messages = agent.run.call_args.args[0]
messages = agent.run.call_args.kwargs["messages"]
assert len(messages) == 1
assert messages[0].role == "user"
assert len(messages[0].contents) == 2
@@ -1464,7 +1499,7 @@ class TestMultiTurnMixedContent:
body = resp.json()
assert body["status"] == "completed"
messages = agent.run.call_args.args[0]
messages = agent.run.call_args.kwargs["messages"]
assert len(messages) == 1
assert len(messages[0].contents) == 2
assert messages[0].contents[0].type == "text"
@@ -1501,7 +1536,7 @@ class TestMultiTurnMixedContent:
body = resp.json()
assert body["status"] == "completed"
messages = agent.run.call_args.args[0]
messages = agent.run.call_args.kwargs["messages"]
assert len(messages) == 1
assert len(messages[0].contents) == 2
assert messages[0].contents[0].type == "text"
@@ -1542,7 +1577,7 @@ class TestMultiTurnMixedContent:
body = resp.json()
assert body["status"] == "completed"
messages = agent.run.call_args.args[0]
messages = agent.run.call_args.kwargs["messages"]
assert len(messages) == 3
assert messages[0].role == "user"
assert messages[0].contents[0].type == "text"
@@ -1591,7 +1626,7 @@ class TestMultiTurnMixedContent:
assert body2["status"] == "completed"
# Verify second call receives history from turn 1 + text+image input
second_call_messages = agent.run.call_args_list[1].args[0]
second_call_messages = agent.run.call_args_list[1].kwargs["messages"]
# History: output message from turn 1 ("Send me an image")
# Input: message with text + image
assert len(second_call_messages) >= 2
@@ -1652,7 +1687,7 @@ class TestMultiTurnMixedContent:
assert resp2.json()["status"] == "completed"
# Verify turn 2 received history including function call/result
second_call_messages = agent.run.call_args_list[1].args[0]
second_call_messages = agent.run.call_args_list[1].kwargs["messages"]
roles = [m.role for m in second_call_messages]
assert "assistant" in roles
assert "tool" in roles
@@ -1703,7 +1738,7 @@ class TestMultiTurnMixedContent:
assert resp2.json()["status"] == "completed"
# Verify history includes the reasoning and text from turn 1
second_call_messages = agent.run.call_args_list[1].args[0]
second_call_messages = agent.run.call_args_list[1].kwargs["messages"]
assert len(second_call_messages) >= 2 # history + new input
async def test_multi_turn_with_mixed_content_and_streaming(self) -> None:
@@ -1795,7 +1830,7 @@ class TestMultiTurnMixedContent:
body = resp.json()
assert body["status"] == "completed"
messages = agent.run.call_args.args[0]
messages = agent.run.call_args.kwargs["messages"]
assert len(messages) == 2
assert messages[0].role == "user"
assert messages[0].contents[0].type == "text"
@@ -1867,7 +1902,7 @@ class TestMultiTurnMixedContent:
assert resp3.json()["status"] == "completed"
# Verify turn 3 received full history from turns 1+2 plus new image input
third_call_messages = agent.run.call_args_list[2].args[0]
third_call_messages = agent.run.call_args_list[2].kwargs["messages"]
# Should have: history from turn 1 (assistant text) + history from turn 2
# (function_call, function_call_output, text) + new input (text + image)
assert len(third_call_messages) >= 5
@@ -1918,7 +1953,7 @@ class TestMultiTurnMixedContent:
body = resp.json()
assert body["status"] == "completed"
messages = agent.run.call_args.args[0]
messages = agent.run.call_args.kwargs["messages"]
assert len(messages) == 1
assert len(messages[0].contents) == 2
assert messages[0].contents[0].type == "text"
@@ -1982,7 +2017,7 @@ class TestMultiTurnMixedContent:
assert resp2.json()["status"] == "completed"
# Verify turn 2 received history from turn 1 + new text+file input
second_call_messages = agent.run.call_args_list[1].args[0]
second_call_messages = agent.run.call_args_list[1].kwargs["messages"]
assert len(second_call_messages) >= 2
# History should include the assistant response from turn 1
@@ -2050,7 +2085,7 @@ class TestMultiTurnMixedContent:
assert resp2.json()["status"] == "completed"
# Verify turn 2 received history with function call + new text+image
second_call_messages = agent.run.call_args_list[1].args[0]
second_call_messages = agent.run.call_args_list[1].kwargs["messages"]
# History should contain function_call and function_result from turn 1
fc_contents = [
c for m in second_call_messages if m.role == "assistant" for c in m.contents if c.type == "function_call"