Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
dbe5ff1
Group chat refactoring Part 1; Next: HIL and handoff
TaoChenOSU Dec 18, 2025
16ae7fe
Add agent approval flow; next samples
TaoChenOSU Dec 19, 2025
fc0268c
WIP: samples
TaoChenOSU Dec 19, 2025
e46ccf9
Merge branch 'main' into local-branch-python-group-chat-refactoring
TaoChenOSU Dec 19, 2025
3d5b831
WIP: HIL samples
TaoChenOSU Dec 20, 2025
5901421
Group chat HIL working; next: handoff
TaoChenOSU Dec 20, 2025
c9e7286
Fix group chat tool approval sample
TaoChenOSU Dec 22, 2025
aa5edbf
WIP: refactor handoff; next handoff handling
TaoChenOSU Dec 23, 2025
c6e5121
Handoff done; next handoff samples and concurrent and sequential
TaoChenOSU Dec 24, 2025
60fd7f0
Handoff samples, concurrent, and sequential done; next Magentic
TaoChenOSU Dec 25, 2025
b555421
WIP: magentic; next test with samples + HIL
TaoChenOSU Dec 26, 2025
b808c18
Magentic Working; next fix all samples and tests
TaoChenOSU Dec 27, 2025
36d908c
Fix handoff samples; next tests
TaoChenOSU Dec 27, 2025
c4e7c66
WIP: fixing tests; some orchestration as agent samples are failing
TaoChenOSU Dec 27, 2025
d9d371e
Group chat unit tests done
TaoChenOSU Jan 5, 2026
b2d918e
Handoff unit tests done
TaoChenOSU Jan 5, 2026
9b6a273
Remove old orchestration_request_info and fix related tests
TaoChenOSU Jan 6, 2026
a362161
Magentic unit tests done
TaoChenOSU Jan 6, 2026
22d56ca
Fix samples
TaoChenOSU Jan 7, 2026
c003804
Merge branch 'main' into local-branch-python-group-chat-refactoring
TaoChenOSU Jan 7, 2026
2d5110c
Fix test
TaoChenOSU Jan 7, 2026
8070a6d
Fix test 2
TaoChenOSU Jan 7, 2026
4540771
mypy
TaoChenOSU Jan 7, 2026
b018eab
Address comments
TaoChenOSU Jan 7, 2026
11678e5
Update readme
TaoChenOSU Jan 7, 2026
0cbef2f
Address comments
TaoChenOSU Jan 8, 2026
d241065
Address comments 2
TaoChenOSU Jan 8, 2026
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
75 changes: 35 additions & 40 deletions python/packages/core/agent_framework/_workflows/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
AgentExecutorRequest,
AgentExecutorResponse,
)
from ._base_group_chat_orchestrator import (
BaseGroupChatOrchestrator,
GroupChatRequestMessage,
GroupChatRequestSentEvent,
GroupChatResponseReceivedEvent,
)
from ._checkpoint import (
CheckpointStorage,
FileCheckpointStorage,
Expand Down Expand Up @@ -55,37 +61,30 @@
)
from ._function_executor import FunctionExecutor, executor
from ._group_chat import (
DEFAULT_MANAGER_INSTRUCTIONS,
DEFAULT_MANAGER_STRUCTURED_OUTPUT_PROMPT,
AgentBasedGroupChatOrchestrator,
GroupChatBuilder,
GroupChatDirective,
GroupChatStateSnapshot,
ManagerDirectiveModel,
ManagerSelectionRequest,
ManagerSelectionResponse,
GroupChatState,
)
from ._handoff import HandoffBuilder, HandoffUserInputRequest
from ._handoff import HandoffAgentUserRequest, HandoffBuilder, HandoffSentEvent
from ._magentic import (
MAGENTIC_EVENT_TYPE_AGENT_DELTA,
MAGENTIC_EVENT_TYPE_ORCHESTRATOR,
ORCH_MSG_KIND_INSTRUCTION,
ORCH_MSG_KIND_NOTICE,
ORCH_MSG_KIND_TASK_LEDGER,
ORCH_MSG_KIND_USER_TASK,
MagenticBuilder,
MagenticContext,
MagenticHumanInputRequest,
MagenticHumanInterventionDecision,
MagenticHumanInterventionKind,
MagenticHumanInterventionReply,
MagenticHumanInterventionRequest,
MagenticManagerBase,
MagenticStallInterventionDecision,
MagenticStallInterventionReply,
MagenticStallInterventionRequest,
MagenticOrchestrator,
MagenticOrchestratorEvent,
MagenticOrchestratorEventType,
MagenticPlanReviewRequest,
MagenticPlanReviewResponse,
MagenticProgressLedger,
MagenticProgressLedgerItem,
MagenticResetSignal,
StandardMagenticManager,
)
from ._orchestration_request_info import AgentInputRequest, AgentResponseReviewRequest, RequestInfoInterceptor
from ._orchestration_request_info import AgentRequestInfoResponse
from ._orchestration_state import OrchestrationState
from ._request_info_mixin import response_handler
from ._runner import Runner
Expand All @@ -111,22 +110,19 @@
from ._workflow_executor import SubWorkflowRequestMessage, SubWorkflowResponseMessage, WorkflowExecutor

__all__ = [
"DEFAULT_MANAGER_INSTRUCTIONS",
"DEFAULT_MANAGER_STRUCTURED_OUTPUT_PROMPT",
"DEFAULT_MAX_ITERATIONS",
"MAGENTIC_EVENT_TYPE_AGENT_DELTA",
"MAGENTIC_EVENT_TYPE_ORCHESTRATOR",
"ORCH_MSG_KIND_INSTRUCTION",
"ORCH_MSG_KIND_NOTICE",
"ORCH_MSG_KIND_TASK_LEDGER",
"ORCH_MSG_KIND_USER_TASK",
"AgentBasedGroupChatOrchestrator",
"AgentExecutor",
"AgentExecutorRequest",
"AgentExecutorResponse",
"AgentInputRequest",
"AgentResponseReviewRequest",
"AgentRequestInfoResponse",
"AgentRunEvent",
"AgentRunUpdateEvent",
"BaseGroupChatOrchestrator",
"Case",
"CheckpointStorage",
"ConcurrentBuilder",
Expand All @@ -144,30 +140,29 @@
"FunctionExecutor",
"GraphConnectivityError",
"GroupChatBuilder",
"GroupChatDirective",
"GroupChatStateSnapshot",
"GroupChatRequestMessage",
"GroupChatRequestSentEvent",
"GroupChatResponseReceivedEvent",
"GroupChatState",
"HandoffAgentUserRequest",
"HandoffBuilder",
"HandoffUserInputRequest",
"HandoffSentEvent",
"InMemoryCheckpointStorage",
"InProcRunnerContext",
"MagenticBuilder",
"MagenticContext",
"MagenticHumanInputRequest",
"MagenticHumanInterventionDecision",
"MagenticHumanInterventionKind",
"MagenticHumanInterventionReply",
"MagenticHumanInterventionRequest",
"MagenticManagerBase",
"MagenticStallInterventionDecision",
"MagenticStallInterventionReply",
"MagenticStallInterventionRequest",
"ManagerDirectiveModel",
"ManagerSelectionRequest",
"ManagerSelectionResponse",
"MagenticOrchestrator",
"MagenticOrchestratorEvent",
"MagenticOrchestratorEventType",
"MagenticPlanReviewRequest",
"MagenticPlanReviewResponse",
"MagenticProgressLedger",
"MagenticProgressLedgerItem",
"MagenticResetSignal",
"Message",
"OrchestrationState",
"RequestInfoEvent",
"RequestInfoInterceptor",
"Runner",
"RunnerContext",
"SequentialBuilder",
Expand Down
28 changes: 22 additions & 6 deletions python/packages/core/agent_framework/_workflows/_agent_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ def __init__(
self._pending_agent_requests: dict[str, FunctionApprovalRequestContent] = {}
self._pending_responses_to_agent: list[FunctionApprovalResponseContent] = []
self._output_response = output_response

# AgentExecutor maintains an internal cache of messages in between runs
self._cache: list[ChatMessage] = []
# This tracks the full conversation after each run
self._full_conversation: list[ChatMessage] = []

@property
def output_response(self) -> bool:
Expand Down Expand Up @@ -227,6 +231,7 @@ async def on_checkpoint_save(self) -> dict[str, Any]:

return {
"cache": encode_chat_messages(self._cache),
"full_conversation": encode_chat_messages(self._full_conversation),
"agent_thread": serialized_thread,
"pending_agent_requests": encode_checkpoint_value(self._pending_agent_requests),
"pending_responses_to_agent": encode_checkpoint_value(self._pending_responses_to_agent),
Expand All @@ -251,6 +256,16 @@ async def on_checkpoint_restore(self, state: dict[str, Any]) -> None:
else:
self._cache = []

full_conversation_payload = state.get("full_conversation")
if full_conversation_payload:
try:
self._full_conversation = decode_chat_messages(full_conversation_payload)
except Exception as exc:
logger.warning("Failed to restore full conversation: %s", exc)
self._full_conversation = []
else:
self._full_conversation = []

thread_payload = state.get("agent_thread")
if thread_payload:
try:
Expand Down Expand Up @@ -289,6 +304,12 @@ async def _run_agent_and_emit(self, ctx: WorkflowContext[AgentExecutorResponse,
# Non-streaming mode: use run() and emit single event
response = await self._run_agent(cast(WorkflowContext, ctx))

# Always extend full conversation with cached messages plus agent outputs
# (agent_run_response.messages) after each run. This is to avoid losing context
# when agent did not complete and the cache is cleared when responses come back.
# Do not mutate response.messages so AgentRunEvent remains faithful to the raw output.
self._full_conversation.extend(list(self._cache) + (list(response.messages) if response else []))

if response is None:
# Agent did not complete (e.g., waiting for user input); do not emit response
logger.info("AgentExecutor %s: Agent did not complete, awaiting user input", self.id)
Expand All @@ -297,12 +318,7 @@ async def _run_agent_and_emit(self, ctx: WorkflowContext[AgentExecutorResponse,
if self._output_response:
await ctx.yield_output(response)

# Always construct a full conversation snapshot from inputs (cache)
# plus agent outputs (agent_run_response.messages). Do not mutate
# response.messages so AgentRunEvent remains faithful to the raw output.
full_conversation: list[ChatMessage] = list(self._cache) + list(response.messages)

agent_response = AgentExecutorResponse(self.id, response, full_conversation=full_conversation)
agent_response = AgentExecutorResponse(self.id, response, full_conversation=self._full_conversation)
await ctx.send_message(agent_response)
self._cache.clear()

Expand Down
Loading
Loading