Python: [BREAKING] update to v1.0.0 (#5062)

* updates to final deprecated pieces and versions

* fix mypy

* fix readme links
This commit is contained in:
Eduard van Valkenburg
2026-04-02 17:26:30 +02:00
committed by GitHub
Unverified
parent 5f06b68535
commit 3446eb8d5d
171 changed files with 2580 additions and 2392 deletions
@@ -188,7 +188,7 @@ class BaseGroupChatOrchestrator(Executor, ABC):
Usage:
workflow.run("Write a blog post about AI agents")
"""
await self._handle_messages([Message(role="user", text=task)], ctx)
await self._handle_messages([Message(role="user", contents=[task])], ctx)
@handler
async def handle_message(
@@ -205,7 +205,7 @@ class BaseGroupChatOrchestrator(Executor, ABC):
ctx: Workflow context
Usage:
workflow.run(Message(role="user", text="Write a blog post about AI agents"))
workflow.run(Message(role="user", contents=["Write a blog post about AI agents"]))
"""
await self._handle_messages([task], ctx)
@@ -224,8 +224,8 @@ class BaseGroupChatOrchestrator(Executor, ABC):
ctx: Workflow context
Usage:
workflow.run([
Message(role="user", text="Write a blog post about AI agents"),
Message(role="user", text="Make it engaging and informative.")
Message(role="user", contents=["Write a blog post about AI agents"]),
Message(role="user", contents=["Make it engaging and informative."])
])
"""
if not task:
@@ -377,7 +377,7 @@ class BaseGroupChatOrchestrator(Executor, ABC):
Returns:
Message with completion content
"""
return Message(role="assistant", text=message, author_name=self._name)
return Message(role="assistant", contents=[message], author_name=self._name)
# Participant routing (shared across all patterns)
@@ -441,7 +441,7 @@ class BaseGroupChatOrchestrator(Executor, ABC):
# AgentExecutors receive simple message list
messages: list[Message] = []
if additional_instruction:
messages.append(Message(role="user", text=additional_instruction))
messages.append(Message(role="user", contents=[additional_instruction]))
request = AgentExecutorRequest(messages=messages, should_respond=True)
await ctx.send_message(request, target_id=target)
await ctx.add_event(
@@ -499,7 +499,7 @@ class AgentBasedGroupChatOrchestrator(BaseGroupChatOrchestrator):
])
)
# Prepend instruction as system message
current_conversation.append(Message(role="user", text=instruction))
current_conversation.append(Message(role="user", contents=[instruction]))
retry_attempts = self._retry_attempts
while True:
@@ -515,7 +515,7 @@ class AgentBasedGroupChatOrchestrator(BaseGroupChatOrchestrator):
current_conversation = [
Message(
role="user",
text=f"Your input could not be parsed due to an error: {ex}. Please try again.",
contents=[f"Your input could not be parsed due to an error: {ex}. Please try again."],
)
]
@@ -161,7 +161,7 @@ class HandoffAgentUserRequest:
"""Create a HandoffAgentUserRequest from a simple text response."""
messages: list[Message] = []
if isinstance(response, str):
messages.append(Message(role="user", text=response))
messages.append(Message(role="user", contents=[response]))
elif isinstance(response, Message):
messages.append(response)
elif isinstance(response, list):
@@ -169,7 +169,7 @@ class HandoffAgentUserRequest:
if isinstance(item, Message):
messages.append(item)
elif isinstance(item, str):
messages.append(Message(role="user", text=item))
messages.append(Message(role="user", contents=[item]))
else:
raise TypeError("List items must be either str or Message instances")
else:
@@ -535,7 +535,7 @@ class HandoffAgentExecutor(AgentExecutor):
# or a termination condition is met.
# This allows the agent to perform long-running tasks without returning control
# to the coordinator or user prematurely.
self._cache.extend([Message(role="user", text=self._autonomous_mode_prompt)])
self._cache.extend([Message(role="user", contents=[self._autonomous_mode_prompt])])
self._autonomous_mode_turns += 1
await self._run_agent_and_emit(ctx)
else:
@@ -604,14 +604,14 @@ class StandardMagenticManager(MagenticManagerBase):
# Gather facts
facts_user = Message(
role="user",
text=self.task_ledger_facts_prompt.format(task=magentic_context.task),
contents=[self.task_ledger_facts_prompt.format(task=magentic_context.task)],
)
facts_msg = await self._complete([*magentic_context.chat_history, facts_user])
# Create plan
plan_user = Message(
role="user",
text=self.task_ledger_plan_prompt.format(team=team_text),
contents=[self.task_ledger_plan_prompt.format(team=team_text)],
)
plan_msg = await self._complete([*magentic_context.chat_history, facts_user, facts_msg, plan_user])
@@ -628,7 +628,7 @@ class StandardMagenticManager(MagenticManagerBase):
facts=facts_msg.text,
plan=plan_msg.text,
)
return Message(role="assistant", text=combined, author_name=MAGENTIC_MANAGER_NAME)
return Message(role="assistant", contents=[combined], author_name=MAGENTIC_MANAGER_NAME)
async def replan(self, magentic_context: MagenticContext) -> Message:
"""Update facts and plan when stalling or looping has been detected."""
@@ -640,16 +640,18 @@ class StandardMagenticManager(MagenticManagerBase):
# Update facts
facts_update_user = Message(
role="user",
text=self.task_ledger_facts_update_prompt.format(
task=magentic_context.task, old_facts=self.task_ledger.facts.text
),
contents=[
self.task_ledger_facts_update_prompt.format(
task=magentic_context.task, old_facts=self.task_ledger.facts.text
)
],
)
updated_facts = await self._complete([*magentic_context.chat_history, facts_update_user])
# Update plan
plan_update_user = Message(
role="user",
text=self.task_ledger_plan_update_prompt.format(team=team_text),
contents=[self.task_ledger_plan_update_prompt.format(team=team_text)],
)
updated_plan = await self._complete([
*magentic_context.chat_history,
@@ -671,7 +673,7 @@ class StandardMagenticManager(MagenticManagerBase):
facts=updated_facts.text,
plan=updated_plan.text,
)
return Message(role="assistant", text=combined, author_name=MAGENTIC_MANAGER_NAME)
return Message(role="assistant", contents=[combined], author_name=MAGENTIC_MANAGER_NAME)
async def create_progress_ledger(self, magentic_context: MagenticContext) -> MagenticProgressLedger:
"""Use the model to produce a JSON progress ledger based on the conversation so far.
@@ -691,7 +693,7 @@ class StandardMagenticManager(MagenticManagerBase):
team=team_text,
names=names_csv,
)
user_message = Message(role="user", text=prompt)
user_message = Message(role="user", contents=[prompt])
# Include full context to help the model decide current stage, with small retry loop
attempts = 0
@@ -718,12 +720,12 @@ class StandardMagenticManager(MagenticManagerBase):
async def prepare_final_answer(self, magentic_context: MagenticContext) -> Message:
"""Ask the model to produce the final answer addressed to the user."""
prompt = self.final_answer_prompt.format(task=magentic_context.task)
user_message = Message(role="user", text=prompt)
user_message = Message(role="user", contents=[prompt])
response = await self._complete([*magentic_context.chat_history, user_message])
# Ensure role is assistant
return Message(
role="assistant",
text=response.text,
contents=[response.text],
author_name=response.author_name or MAGENTIC_MANAGER_NAME,
)
@@ -806,11 +808,11 @@ class MagenticPlanReviewResponse:
def revise(feedback: str | list[str] | Message | list[Message]) -> "MagenticPlanReviewResponse":
"""Create a revision response with feedback."""
if isinstance(feedback, str):
feedback = [Message(role="user", text=feedback)]
feedback = [Message(role="user", contents=[feedback])]
elif isinstance(feedback, Message):
feedback = [feedback]
elif isinstance(feedback, list):
feedback = [Message(role="user", text=item) if isinstance(item, str) else item for item in feedback]
feedback = [Message(role="user", contents=[item]) if isinstance(item, str) else item for item in feedback]
return MagenticPlanReviewResponse(review=feedback)
@@ -1120,7 +1122,7 @@ class MagenticOrchestrator(BaseGroupChatOrchestrator):
# Add instruction to conversation (assistant guidance)
instruction_msg = Message(
role="assistant",
text=str(instruction),
contents=[str(instruction)],
author_name=MAGENTIC_MANAGER_NAME,
)
self._magentic_context.chat_history.append(instruction_msg)
@@ -1232,7 +1234,7 @@ class MagenticOrchestrator(BaseGroupChatOrchestrator):
*self._magentic_context.chat_history,
Message(
role="assistant",
text=f"Workflow terminated due to reaching maximum {limit_type} count.",
contents=[f"Workflow terminated due to reaching maximum {limit_type} count."],
author_name=MAGENTIC_MANAGER_NAME,
),
])
@@ -73,7 +73,7 @@ class AgentRequestInfoResponse:
Returns:
AgentRequestInfoResponse instance.
"""
return AgentRequestInfoResponse(messages=[Message(role="user", text=text) for text in texts])
return AgentRequestInfoResponse(messages=[Message(role="user", contents=[text]) for text in texts])
@staticmethod
def approve() -> "AgentRequestInfoResponse":
@@ -36,7 +36,7 @@ def clean_conversation_for_handoff(conversation: list[Message]) -> list[Message]
msg_copy = Message(
role=msg.role,
text=" ".join(text_parts),
contents=[" ".join(text_parts)],
author_name=msg.author_name,
additional_properties=dict(msg.additional_properties) if msg.additional_properties else None,
)
@@ -66,6 +66,6 @@ def create_completion_message(
message_text = text or f"Conversation {reason}."
return Message(
role="assistant",
text=message_text,
contents=[message_text],
author_name=author_name,
)
@@ -4,7 +4,7 @@ description = "Orchestration patterns for Microsoft Agent Framework. Includes Se
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
readme = "README.md"
requires-python = ">=3.10"
version = "1.0.0b260330"
version = "1.0.0b260402"
license-files = ["LICENSE"]
urls.homepage = "https://aka.ms/agent-framework"
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
@@ -23,7 +23,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"agent-framework-core>=1.0.0rc6",
"agent-framework-core>=1.0.0,<2",
]
[tool.uv]
@@ -32,7 +32,7 @@ class _FakeAgentExec(Executor):
@handler
async def run(self, request: AgentExecutorRequest, ctx: WorkflowContext[AgentExecutorResponse]) -> None:
response = AgentResponse(messages=Message(role="assistant", text=self._reply_text))
response = AgentResponse(messages=Message(role="assistant", contents=[self._reply_text]))
full_conversation = list(request.messages) + list(response.messages)
await ctx.send_message(AgentExecutorResponse(self.id, response, full_conversation=full_conversation))
@@ -49,7 +49,7 @@ class StubAgent(BaseAgent):
return self._run_impl()
async def _run_impl(self) -> AgentResponse:
response = Message(role="assistant", text=self._reply_text, author_name=self.name)
response = Message(role="assistant", contents=[self._reply_text], author_name=self.name)
return AgentResponse(messages=[response])
async def _run_stream_impl(self) -> AsyncIterable[AgentResponseUpdate]:
@@ -89,10 +89,12 @@ class StubManagerAgent(Agent):
messages=[
Message(
role="assistant",
text=(
'{"terminate": false, "reason": "Selecting agent", '
'"next_speaker": "agent", "final_message": null}'
),
contents=[
(
'{"terminate": false, "reason": "Selecting agent", '
'"next_speaker": "agent", "final_message": null}'
)
],
author_name=self.name,
)
],
@@ -110,10 +112,12 @@ class StubManagerAgent(Agent):
messages=[
Message(
role="assistant",
text=(
'{"terminate": true, "reason": "Task complete", '
'"next_speaker": null, "final_message": "agent manager final"}'
),
contents=[
(
'{"terminate": true, "reason": "Task complete", '
'"next_speaker": null, "final_message": "agent manager final"}'
)
],
author_name=self.name,
)
],
@@ -141,12 +145,14 @@ class ConcatenatedJsonManagerAgent(Agent):
messages=[
Message(
role="assistant",
text=(
'{"terminate": false, "reason": "invalid candidate", '
'"next_speaker": "unknown", "final_message": null} '
'{"terminate": false, "reason": "pick known participant", '
'"next_speaker": "agent", "final_message": null}'
),
contents=[
(
'{"terminate": false, "reason": "invalid candidate", '
'"next_speaker": "unknown", "final_message": null} '
'{"terminate": false, "reason": "pick known participant", '
'"next_speaker": "agent", "final_message": null}'
)
],
author_name=self.name,
)
]
@@ -156,10 +162,12 @@ class ConcatenatedJsonManagerAgent(Agent):
messages=[
Message(
role="assistant",
text=(
'{"terminate": true, "reason": "Task complete", '
'"next_speaker": null, "final_message": "concatenated manager final"}'
),
contents=[
(
'{"terminate": true, "reason": "Task complete", '
'"next_speaker": null, "final_message": "concatenated manager final"}'
)
],
author_name=self.name,
)
]
@@ -189,7 +197,7 @@ class StubMagenticManager(MagenticManagerBase):
self._round = 0
async def plan(self, magentic_context: MagenticContext) -> Message:
return Message(role="assistant", text="plan", author_name="magentic_manager")
return Message(role="assistant", contents=["plan"], author_name="magentic_manager")
async def replan(self, magentic_context: MagenticContext) -> Message:
return await self.plan(magentic_context)
@@ -215,7 +223,7 @@ class StubMagenticManager(MagenticManagerBase):
)
async def prepare_final_answer(self, magentic_context: MagenticContext) -> Message:
return Message(role="assistant", text="final", author_name="magentic_manager")
return Message(role="assistant", contents=["final"], author_name="magentic_manager")
async def test_group_chat_builder_basic_flow() -> None:
@@ -258,8 +266,8 @@ async def test_group_chat_as_agent_accepts_conversation() -> None:
agent = workflow.as_agent(name="group-chat-agent")
conversation = [
Message(role="user", text="kickoff", author_name="user"),
Message(role="assistant", text="noted", author_name="alpha"),
Message(role="user", contents=["kickoff"], author_name="user"),
Message(role="assistant", contents=["noted"], author_name="alpha"),
]
response = await agent.run(conversation)
@@ -549,7 +557,7 @@ class TestConversationHandling:
async def test_handle_chat_message_input(self) -> None:
"""Test handling Message input directly."""
task_message = Message(role="user", text="test message")
task_message = Message(role="user", contents=["test message"])
def selector(state: GroupChatState) -> str:
# Verify the task message was preserved in conversation
@@ -573,8 +581,8 @@ class TestConversationHandling:
async def test_handle_conversation_list_input(self) -> None:
"""Test handling conversation list preserves context."""
conversation = [
Message(role="system", text="system message"),
Message(role="user", text="user message"),
Message(role="system", contents=["system message"]),
Message(role="user", contents=["user message"]),
]
def selector(state: GroupChatState) -> str:
@@ -913,10 +921,12 @@ async def test_group_chat_with_orchestrator_factory_returning_chat_agent():
messages=[
Message(
role="assistant",
text=(
'{"terminate": false, "reason": "Selecting alpha", '
'"next_speaker": "alpha", "final_message": null}'
),
contents=[
(
'{"terminate": false, "reason": "Selecting alpha", '
'"next_speaker": "alpha", "final_message": null}'
)
],
author_name=self.name,
)
],
@@ -933,10 +943,12 @@ async def test_group_chat_with_orchestrator_factory_returning_chat_agent():
messages=[
Message(
role="assistant",
text=(
'{"terminate": true, "reason": "Task complete", '
'"next_speaker": null, "final_message": "dynamic manager final"}'
),
contents=[
(
'{"terminate": true, "reason": "Task complete", '
'"next_speaker": null, "final_message": "dynamic manager final"}'
)
],
author_name=self.name,
)
],
@@ -269,7 +269,7 @@ async def test_resume_keeps_prior_user_context_for_same_agent() -> None:
second_events = await _drain(
workflow.run(
stream=True,
responses={first_request.request_id: [Message(role="user", text="Order 2939393")]},
responses={first_request.request_id: [Message(role="user", contents=["Order 2939393"])]},
)
)
second_request = _latest_request_info_event(second_events)
@@ -280,7 +280,7 @@ async def test_resume_keeps_prior_user_context_for_same_agent() -> None:
third_events = await _drain(
workflow.run(
stream=True,
responses={second_request.request_id: [Message(role="user", text="It arrived broken and unusable.")]},
responses={second_request.request_id: [Message(role="user", contents=["It arrived broken and unusable."])]},
)
)
third_request = _latest_request_info_event(third_events)
@@ -370,7 +370,7 @@ async def test_tool_approval_responses_are_not_replayed_from_history() -> None:
await _drain(
workflow.run(
stream=True,
responses={second_request.request_id: [Message(role="user", text="Thanks, what's next?")]},
responses={second_request.request_id: [Message(role="user", contents=["Thanks, what's next?"])]},
)
)
@@ -679,7 +679,7 @@ async def test_handoff_resume_preserves_approved_tool_output_for_stateless_runs(
await _drain(
workflow.run(
stream=True,
responses={order_request.request_id: [Message(role="user", text="Please continue with refund.")]},
responses={order_request.request_id: [Message(role="user", contents=["Please continue with refund."])]},
)
)
@@ -767,7 +767,7 @@ def test_clean_conversation_for_handoff_keeps_text_only_history() -> None:
)
conversation = [
Message(role="user", text="My order arrived damaged."),
Message(role="user", contents=["My order arrived damaged."]),
Message(
role="assistant",
contents=[
@@ -933,7 +933,7 @@ async def test_handoff_async_termination_condition() -> None:
events = await _drain(
workflow.run(
stream=True, responses={requests[-1].request_id: [Message(role="user", text="Second user message")]}
stream=True, responses={requests[-1].request_id: [Message(role="user", contents=["Second user message"])]}
)
)
outputs = [ev for ev in events if ev.type == "output"]
@@ -1011,7 +1011,7 @@ async def test_tool_choice_preserved_from_agent_config():
if options:
recorded_tool_choices.append(options.get("tool_choice"))
return ChatResponse(
messages=[Message(role="assistant", text="Response")],
messages=[Message(role="assistant", contents=["Response"])],
response_id="test_response",
)
@@ -585,7 +585,7 @@ async def _collect_agent_responses_setup(participant: SupportsAgentRun) -> list[
captured.append(
Message(
role=ev.data.role or "assistant",
text=ev.data.text or "",
contents=[ev.data.text or ""],
author_name=ev.data.author_name,
)
)
@@ -72,7 +72,7 @@ class TestAgentRequestInfoResponse:
def test_create_response_with_messages(self):
"""Test creating an AgentRequestInfoResponse with messages."""
messages = [Message(role="user", text="Additional info")]
messages = [Message(role="user", contents=["Additional info"])]
response = AgentRequestInfoResponse(messages=messages)
assert response.messages == messages
@@ -80,8 +80,8 @@ class TestAgentRequestInfoResponse:
def test_from_messages_factory(self):
"""Test creating response from Message list."""
messages = [
Message(role="user", text="Message 1"),
Message(role="user", text="Message 2"),
Message(role="user", contents=["Message 1"]),
Message(role="user", contents=["Message 2"]),
]
response = AgentRequestInfoResponse.from_messages(messages)
@@ -113,7 +113,7 @@ class TestAgentRequestInfoExecutor:
"""Test that request_info handler calls ctx.request_info."""
executor = AgentRequestInfoExecutor(id="test_executor")
agent_response = AgentResponse(messages=[Message(role="assistant", text="Agent response")])
agent_response = AgentResponse(messages=[Message(role="assistant", contents=["Agent response"])])
agent_response = AgentExecutorResponse(
executor_id="test_agent",
agent_response=agent_response,
@@ -132,7 +132,7 @@ class TestAgentRequestInfoExecutor:
"""Test response handler when user provides additional messages."""
executor = AgentRequestInfoExecutor(id="test_executor")
agent_response = AgentResponse(messages=[Message(role="assistant", text="Original")])
agent_response = AgentResponse(messages=[Message(role="assistant", contents=["Original"])])
original_request = AgentExecutorResponse(
executor_id="test_agent",
agent_response=agent_response,
@@ -159,7 +159,7 @@ class TestAgentRequestInfoExecutor:
"""Test response handler when user approves (no additional messages)."""
executor = AgentRequestInfoExecutor(id="test_executor")
agent_response = AgentResponse(messages=[Message(role="assistant", text="Original")])
agent_response = AgentResponse(messages=[Message(role="assistant", contents=["Original"])])
original_request = AgentExecutorResponse(
executor_id="test_agent",
agent_response=agent_response,
@@ -212,10 +212,10 @@ class _TestAgent:
"""Dummy run method."""
if stream:
return self._run_stream_impl()
return AgentResponse(messages=[Message(role="assistant", text="Test response")])
return AgentResponse(messages=[Message(role="assistant", contents=["Test response"])])
async def _run_stream_impl(self) -> AsyncIterable[AgentResponseUpdate]:
yield AgentResponseUpdate(messages=[Message(role="assistant", text="Test response stream")])
yield AgentResponseUpdate(messages=[Message(role="assistant", contents=["Test response stream"])])
def create_session(self, **kwargs: Any) -> AgentSession:
"""Creates a new conversation session for the agent."""