Feat/tool call loop (#6651)

## Why are these changes needed?

This PR addresses critical issues in the AssistantAgent that affect tool
handling:

**Lack of tool call loop functionality**: The agent could not perform
multiple consecutive tool calls in a single turn, limiting its ability
to complete complex multi-step tasks that require chaining tool
operations.

These changes enhance the agent's robustness and capability while
maintaining full backward compatibility through feature flags.

## Related issue number

Closes  #6268

## Checks

- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests corresponding to the changes introduced in this
PR.
- [x] I've made sure all auto checks have passed.

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
Tejas Dharani 2025-06-29 22:22:03 +05:30 committed by GitHub
parent 2b873a483b
commit 6f15270cb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 3490 additions and 1490 deletions

View File

@ -5,7 +5,20 @@ import json
import logging
import uuid
import warnings
from typing import Any, AsyncGenerator, Awaitable, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Union
from typing import (
Any,
AsyncGenerator,
Awaitable,
Callable,
Dict,
List,
Mapping,
Optional,
Sequence,
Tuple,
TypeVar,
Union,
)
from autogen_core import CancellationToken, Component, ComponentModel, FunctionCall
from autogen_core.memory import Memory
@ -24,7 +37,7 @@ from autogen_core.models import (
SystemMessage,
)
from autogen_core.tools import BaseTool, FunctionTool, StaticStreamWorkbench, ToolResult, Workbench
from pydantic import BaseModel
from pydantic import BaseModel, Field
from typing_extensions import Self
from .. import EVENT_LOGGER_NAME
@ -50,6 +63,10 @@ from ._base_chat_agent import BaseChatAgent
event_logger = logging.getLogger(EVENT_LOGGER_NAME)
# Add type variables for more specific typing
T = TypeVar("T", bound=BaseModel)
R = TypeVar("R", bound=BaseModel)
class AssistantAgentConfig(BaseModel):
"""The declarative configuration for the assistant agent."""
@ -66,13 +83,13 @@ class AssistantAgentConfig(BaseModel):
model_client_stream: bool = False
reflect_on_tool_use: bool
tool_call_summary_format: str
max_tool_iterations: int = Field(default=1, ge=1)
metadata: Dict[str, str] | None = None
structured_message_factory: ComponentModel | None = None
class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
"""An agent that provides assistance with tool use.
The :meth:`on_messages` returns a :class:`~autogen_agentchat.base.Response`
in which :attr:`~autogen_agentchat.base.Response.chat_message` is the final
response message.
@ -120,13 +137,14 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
**Tool call behavior:**
* If the model returns no tool call, then the response is immediately returned as a :class:`~autogen_agentchat.messages.TextMessage` or a :class:`~autogen_agentchat.messages.StructuredMessage` (when using structured output) in :attr:`~autogen_agentchat.base.Response.chat_message`.
* If the model returns no tool call, then the response is immediately returned as a :class:`~autogen_agentchat.messages.TextMessage` or a :class:`~autogen_agentchat.messages.StructuredMessage` (when using structured output) in :attr:`~autogen_agentchat.base.Response.chat_message`. This ends the tool call iteration loop regardless of the `max_tool_iterations` setting.
* When the model returns tool calls, they will be executed right away:
- When `reflect_on_tool_use` is False, the tool call results are returned as a :class:`~autogen_agentchat.messages.ToolCallSummaryMessage` in :attr:`~autogen_agentchat.base.Response.chat_message`. You can customise the summary with either a static format string (`tool_call_summary_format`) **or** a callable (`tool_call_summary_formatter`); the callable is evaluated once per tool call.
- When `reflect_on_tool_use` is True, the another model inference is made using the tool calls and results, and final response is returned as a :class:`~autogen_agentchat.messages.TextMessage` or a :class:`~autogen_agentchat.messages.StructuredMessage` (when using structured output) in :attr:`~autogen_agentchat.base.Response.chat_message`.
- `reflect_on_tool_use` is set to `True` by default when `output_content_type` is set.
- `reflect_on_tool_use` is set to `False` by default when `output_content_type` is not set.
* If the model returns multiple tool calls, they will be executed concurrently. To disable parallel tool calls you need to configure the model client. For example, set `parallel_tool_calls=False` for :class:`~autogen_ext.models.openai.OpenAIChatCompletionClient` and :class:`~autogen_ext.models.openai.AzureOpenAIChatCompletionClient`.
* The `max_tool_iterations` parameter controls how many sequential tool call iterations the agent can perform in a single run. When set to 1 (default), the agent executes tool calls once and returns the result. When set higher, the agent can make additional model calls to execute more tool calls if the model continues to request them, enabling multi-step tool-based workflows. The agent stops when either the model returns a text response (instead of tool calls) or the maximum number of iterations is reached.
.. tip::
@ -174,7 +192,6 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
messages as the model client produces chunks of response.
The chunk messages will not be included in the final response's inner messages.
Args:
name (str): The name of the agent.
model_client (ChatCompletionClient): The model client to use for inference.
@ -199,6 +216,12 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
If this is set, the agent will respond with a :class:`~autogen_agentchat.messages.StructuredMessage` instead of a :class:`~autogen_agentchat.messages.TextMessage`
in the final response, unless `reflect_on_tool_use` is `False` and a tool call is made.
output_content_type_format (str | None, optional): (Experimental) The format string used for the content of a :class:`~autogen_agentchat.messages.StructuredMessage` response.
max_tool_iterations (int, optional): The maximum number of tool iterations to perform until the model stops making tool calls. Defaults to `1`, which means the agent will
only execute the tool calls made by the model once, and return the result as a :class:`~autogen_agentchat.messages.ToolCallSummaryMessage`,
or a :class:`~autogen_agentchat.messages.TextMessage` or a :class:`~autogen_agentchat.messages.StructuredMessage` (when using structured output)
in :attr:`~autogen_agentchat.base.Response.chat_message` as the final response.
As soon as the model stops making tool calls, the agent will stop executing tool calls and return the result as the final response.
The value must be greater than or equal to 1.
tool_call_summary_format (str, optional): Static format string applied to each tool call result when composing the :class:`~autogen_agentchat.messages.ToolCallSummaryMessage`.
Defaults to ``"{result}"``. Ignored if `tool_call_summary_formatter` is provided. When `reflect_on_tool_use` is ``False``, the summaries for all tool
calls are concatenated with a newline ('\\n') and returned as the response. Placeholders available in the template:
@ -337,7 +360,59 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
asyncio.run(main())
**Example 4: agent with Model-Context Protocol (MCP) workbench**
**Example 4: agent with max_tool_iterations**
The following example demonstrates how to use the `max_tool_iterations` parameter
to control how many times the agent can execute tool calls in a single run.
This is useful when you want the agent to perform multiple sequential tool
operations to reach a goal.
.. code-block:: python
import asyncio
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.ui import Console
# Global counter state
counter = 0
def increment_counter() -> str:
\"\"\"Increment the counter by 1 and return the current value.\"\"\"
global counter
counter += 1
return f"Counter incremented to: {counter}"
def get_counter() -> str:
\"\"\"Get the current counter value.\"\"\"
global counter
return f"Current counter value: {counter}"
async def main() -> None:
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
# api_key = "your_openai_api_key"
)
# Create agent with max_tool_iterations=5 to allow multiple tool calls
agent = AssistantAgent(
name="assistant",
model_client=model_client,
tools=[increment_counter, get_counter],
max_tool_iterations=5, # Allow up to 5 tool call iterations
reflect_on_tool_use=True, # Get a final summary after tool calls
)
await Console(agent.run_stream(task="Increment the counter 3 times and then tell me the final value."))
asyncio.run(main())
**Example 5: agent with Model-Context Protocol (MCP) workbench**
The following example demonstrates how to create an assistant agent with
a model client and an :class:`~autogen_ext.tools.mcp.McpWorkbench` for
@ -375,7 +450,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
asyncio.run(main())
**Example 5: agent with structured output and tool**
**Example 6: agent with structured output and tool**
The following example demonstrates how to create an assistant agent with
a model client configured to use structured output and a tool.
@ -443,7 +518,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
---------- assistant ----------
{"thoughts":"The user expresses a clear positive emotion by stating they are happy today, suggesting an upbeat mood.","response":"happy"}
**Example 6: agent with bounded model context**
**Example 7: agent with bounded model context**
The following example shows how to use a
:class:`~autogen_core.model_context.BufferedChatCompletionContext`
@ -496,7 +571,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
That's great! Blue is often associated with calmness and serenity. Do you have a specific shade of blue that you like, or any particular reason why it's your favorite?
No, you didn't ask a question. I apologize for any misunderstanding. If you have something specific you'd like to discuss or ask, feel free to let me know!
**Example 7: agent with memory**
**Example 8: agent with memory**
The following example shows how to use a list-based memory with the assistant agent.
The memory is preloaded with some initial content.
@ -552,7 +627,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
Serve it with a side salad or some garlic bread to complete the meal! Enjoy your dinner!
**Example 8: agent with `o1-mini`**
**Example 9: agent with `o1-mini`**
The following example shows how to use `o1-mini` model with the assistant agent.
@ -584,7 +659,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
See `o1 beta limitations <https://platform.openai.com/docs/guides/reasoning#beta-limitations>`_ for more details.
**Example 9: agent using reasoning model with custom model context.**
**Example 10: agent using reasoning model with custom model context.**
The following example shows how to use a reasoning model (DeepSeek R1) with the assistant agent.
The model context is used to filter out the thought field from the assistant message.
@ -640,6 +715,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
asyncio.run(run_reasoning_agent())
For detailed examples and usage, see the Examples section below.
"""
component_version = 2
@ -661,6 +737,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
) = "You are a helpful AI assistant. Solve tasks using your tools. Reply with TERMINATE when the task has been completed.",
model_client_stream: bool = False,
reflect_on_tool_use: bool | None = None,
max_tool_iterations: int = 1,
tool_call_summary_format: str = "{result}",
tool_call_summary_formatter: Callable[[FunctionCall, FunctionExecutionResult], str] | None = None,
output_content_type: type[BaseModel] | None = None,
@ -730,12 +807,23 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
handoff_tool_names = [tool.name for tool in self._handoff_tools]
if len(handoff_tool_names) != len(set(handoff_tool_names)):
raise ValueError(f"Handoff names must be unique: {handoff_tool_names}")
# Check if handoff tool names not in tool names.
if any(name in tool_names for name in handoff_tool_names):
raise ValueError(
f"Handoff names must be unique from tool names. "
f"Handoff names: {handoff_tool_names}; tool names: {tool_names}"
)
# Create sets for faster lookup
tool_names_set = set(tool_names)
handoff_tool_names_set = set(handoff_tool_names)
# Check if there's any overlap between handoff tool names and tool names
overlap = tool_names_set.intersection(handoff_tool_names_set)
# Also check if any handoff target name matches a tool name
# This handles the case where a handoff is specified directly with a string that matches a tool name
for handoff in handoffs or []:
if isinstance(handoff, str) and handoff in tool_names_set:
raise ValueError("Handoff names must be unique from tool names")
elif isinstance(handoff, HandoffBase) and handoff.target in tool_names_set:
raise ValueError("Handoff names must be unique from tool names")
if overlap:
raise ValueError("Handoff names must be unique from tool names")
if workbench is not None:
if self._tools:
@ -769,41 +857,71 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
UserWarning,
stacklevel=2,
)
# Tool call loop
self._max_tool_iterations = max_tool_iterations
if self._max_tool_iterations < 1:
raise ValueError(
f"Maximum number of tool iterations must be greater than or equal to 1, got {max_tool_iterations}"
)
self._tool_call_summary_format = tool_call_summary_format
self._tool_call_summary_formatter = tool_call_summary_formatter
self._is_running = False
@property
def produced_message_types(self) -> Sequence[type[BaseChatMessage]]:
message_types: List[type[BaseChatMessage]] = []
if self._handoffs:
message_types.append(HandoffMessage)
if self._tools:
message_types.append(ToolCallSummaryMessage)
if self._output_content_type:
message_types.append(StructuredMessage[self._output_content_type]) # type: ignore[name-defined]
else:
message_types.append(TextMessage)
return tuple(message_types)
"""Get the types of messages this agent can produce.
Returns:
Sequence of message types this agent can generate
"""
types: List[type[BaseChatMessage]] = [TextMessage, ToolCallSummaryMessage, HandoffMessage]
if self._structured_message_factory is not None:
types.append(StructuredMessage)
return types
@property
def model_context(self) -> ChatCompletionContext:
"""
The model context in use by the agent.
"""Get the model context used by this agent.
Returns:
The chat completion context for this agent
"""
return self._model_context
async def on_messages(self, messages: Sequence[BaseChatMessage], cancellation_token: CancellationToken) -> Response:
async def on_messages(
self,
messages: Sequence[BaseChatMessage],
cancellation_token: CancellationToken,
) -> Response:
"""Process incoming messages and generate a response.
Args:
messages: Sequence of messages to process
cancellation_token: Token for cancelling operation
Returns:
Response containing the agent's reply
"""
async for message in self.on_messages_stream(messages, cancellation_token):
if isinstance(message, Response):
return message
raise AssertionError("The stream should have returned the final result.")
async def on_messages_stream(
self, messages: Sequence[BaseChatMessage], cancellation_token: CancellationToken
) -> AsyncGenerator[BaseAgentEvent | BaseChatMessage | Response, None]:
"""
Process the incoming messages with the assistant agent and yield events/responses as they happen.
self,
messages: Sequence[BaseChatMessage],
cancellation_token: CancellationToken,
) -> AsyncGenerator[Union[BaseAgentEvent, BaseChatMessage, Response], None]:
"""Process messages and stream the response.
Args:
messages: Sequence of messages to process
cancellation_token: Token for cancelling operation
Yields:
Events, messages and final response during processing
"""
# Gather all relevant state here
@ -817,10 +935,10 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
model_client = self._model_client
model_client_stream = self._model_client_stream
reflect_on_tool_use = self._reflect_on_tool_use
max_tool_iterations = self._max_tool_iterations
tool_call_summary_format = self._tool_call_summary_format
tool_call_summary_formatter = self._tool_call_summary_formatter
output_content_type = self._output_content_type
format_string = self._output_content_type_format
# STEP 1: Add new user/handoff messages to the model context
await self._add_messages_to_context(
@ -892,11 +1010,12 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
model_client=model_client,
model_client_stream=model_client_stream,
reflect_on_tool_use=reflect_on_tool_use,
max_tool_iterations=max_tool_iterations,
tool_call_summary_format=tool_call_summary_format,
tool_call_summary_formatter=tool_call_summary_formatter,
output_content_type=output_content_type,
message_id=message_id,
format_string=format_string,
format_string=self._output_content_type_format,
):
yield output_event
@ -920,8 +1039,15 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
model_context: ChatCompletionContext,
agent_name: str,
) -> List[MemoryQueryEvent]:
"""
If memory modules are present, update the model context and return the events produced.
"""Update model context with memory content.
Args:
memory: Optional sequence of memory stores to query
model_context: Context to update with memory content
agent_name: Name of the agent for event tracking
Returns:
List of memory query events generated during update
"""
events: List[MemoryQueryEvent] = []
if memory:
@ -949,8 +1075,21 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
output_content_type: type[BaseModel] | None,
message_id: str,
) -> AsyncGenerator[Union[CreateResult, ModelClientStreamingChunkEvent], None]:
"""
Perform a model inference and yield either streaming chunk events or the final CreateResult.
"""Call the language model with given context and configuration.
Args:
model_client: Client for model inference
model_client_stream: Whether to stream responses
system_messages: System messages to include
model_context: Context containing message history
workbench: Available workbenches
handoff_tools: Tools for handling handoffs
agent_name: Name of the agent
cancellation_token: Token for cancelling operation
output_content_type: Optional type for structured output
Returns:
Generator yielding model results or streaming chunks
"""
all_messages = await model_context.get_messages()
llm_messages = cls._get_compatible_context(model_client=model_client, messages=system_messages + all_messages)
@ -1001,130 +1140,184 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
reflect_on_tool_use: bool,
tool_call_summary_format: str,
tool_call_summary_formatter: Callable[[FunctionCall, FunctionExecutionResult], str] | None,
max_tool_iterations: int,
output_content_type: type[BaseModel] | None,
message_id: str,
format_string: str | None = None,
) -> AsyncGenerator[BaseAgentEvent | BaseChatMessage | Response, None]:
"""
Handle final or partial responses from model_result, including tool calls, handoffs,
and reflection if needed.
and reflection if needed. Supports tool call loops when enabled.
"""
# If direct text response (string)
if isinstance(model_result.content, str):
# Use the passed message ID for the final message
if output_content_type:
content = output_content_type.model_validate_json(model_result.content)
yield Response(
chat_message=StructuredMessage[output_content_type]( # type: ignore[valid-type]
content=content,
source=agent_name,
models_usage=model_result.usage,
format_string=format_string,
id=message_id,
),
inner_messages=inner_messages,
)
else:
yield Response(
chat_message=TextMessage(
content=model_result.content,
source=agent_name,
models_usage=model_result.usage,
id=message_id,
),
inner_messages=inner_messages,
)
return
# Tool call loop implementation with streaming support
current_model_result = model_result
# This variable is needed for the final summary/reflection step
executed_calls_and_results: List[Tuple[FunctionCall, FunctionExecutionResult]] = []
# Otherwise, we have function calls
assert isinstance(model_result.content, list) and all(
isinstance(item, FunctionCall) for item in model_result.content
)
# STEP 4A: Yield ToolCallRequestEvent
tool_call_msg = ToolCallRequestEvent(
content=model_result.content,
source=agent_name,
models_usage=model_result.usage,
)
event_logger.debug(tool_call_msg)
inner_messages.append(tool_call_msg)
yield tool_call_msg
# STEP 4B: Execute tool calls
# Use a queue to handle streaming results from tool calls.
stream = asyncio.Queue[BaseAgentEvent | BaseChatMessage | None]()
async def _execute_tool_calls(
function_calls: List[FunctionCall],
) -> List[Tuple[FunctionCall, FunctionExecutionResult]]:
results = await asyncio.gather(
*[
cls._execute_tool_call(
tool_call=call,
workbench=workbench,
handoff_tools=handoff_tools,
agent_name=agent_name,
cancellation_token=cancellation_token,
stream=stream,
for loop_iteration in range(max_tool_iterations):
# If direct text response (string), we're done
if isinstance(current_model_result.content, str):
# Use the passed message ID for the final message
if output_content_type:
content = output_content_type.model_validate_json(current_model_result.content)
yield Response(
chat_message=StructuredMessage[output_content_type]( # type: ignore[valid-type]
content=content,
source=agent_name,
models_usage=current_model_result.usage,
format_string=format_string,
id=message_id,
),
inner_messages=inner_messages,
)
for call in function_calls
]
else:
yield Response(
chat_message=TextMessage(
content=current_model_result.content,
source=agent_name,
models_usage=current_model_result.usage,
id=message_id,
),
inner_messages=inner_messages,
)
return
# Otherwise, we have function calls
assert isinstance(current_model_result.content, list) and all(
isinstance(item, FunctionCall) for item in current_model_result.content
)
# Signal the end of streaming by putting None in the queue.
stream.put_nowait(None)
return results
task = asyncio.create_task(_execute_tool_calls(model_result.content))
# STEP 4A: Yield ToolCallRequestEvent
tool_call_msg = ToolCallRequestEvent(
content=current_model_result.content,
source=agent_name,
models_usage=current_model_result.usage,
)
event_logger.debug(tool_call_msg)
inner_messages.append(tool_call_msg)
yield tool_call_msg
while True:
event = await stream.get()
if event is None:
# End of streaming, break the loop.
# STEP 4B: Execute tool calls with streaming support
# Use a queue to handle streaming results from tool calls.
stream = asyncio.Queue[BaseAgentEvent | BaseChatMessage | None]()
async def _execute_tool_calls(
function_calls: List[FunctionCall],
stream_queue: asyncio.Queue[BaseAgentEvent | BaseChatMessage | None],
) -> List[Tuple[FunctionCall, FunctionExecutionResult]]:
results = await asyncio.gather(
*[
cls._execute_tool_call(
tool_call=call,
workbench=workbench,
handoff_tools=handoff_tools,
agent_name=agent_name,
cancellation_token=cancellation_token,
stream=stream_queue,
)
for call in function_calls
]
)
# Signal the end of streaming by putting None in the queue.
stream_queue.put_nowait(None)
return results
task = asyncio.create_task(_execute_tool_calls(current_model_result.content, stream))
while True:
event = await stream.get()
if event is None:
# End of streaming, break the loop.
break
if isinstance(event, BaseAgentEvent) or isinstance(event, BaseChatMessage):
yield event
inner_messages.append(event)
else:
raise RuntimeError(f"Unexpected event type: {type(event)}")
# Wait for all tool calls to complete.
executed_calls_and_results = await task
exec_results = [result for _, result in executed_calls_and_results]
# Yield ToolCallExecutionEvent
tool_call_result_msg = ToolCallExecutionEvent(
content=exec_results,
source=agent_name,
)
event_logger.debug(tool_call_result_msg)
await model_context.add_message(FunctionExecutionResultMessage(content=exec_results))
inner_messages.append(tool_call_result_msg)
yield tool_call_result_msg
# STEP 4C: Check for handoff
handoff_output = cls._check_and_handle_handoff(
model_result=current_model_result,
executed_calls_and_results=executed_calls_and_results,
inner_messages=inner_messages,
handoffs=handoffs,
agent_name=agent_name,
)
if handoff_output:
yield handoff_output
return
# STEP 4D: Check if we should continue the loop.
# If we are on the last iteration, break to the summary/reflection step.
if loop_iteration == max_tool_iterations - 1:
break
if isinstance(event, BaseAgentEvent) or isinstance(event, BaseChatMessage):
yield event
inner_messages.append(event)
else:
raise RuntimeError(f"Unexpected event type: {type(event)}")
# Wait for all tool calls to complete.
executed_calls_and_results = await task
exec_results = [result for _, result in executed_calls_and_results]
# Continue the loop: make another model call using _call_llm
next_model_result: Optional[CreateResult] = None
async for llm_output in cls._call_llm(
model_client=model_client,
model_client_stream=model_client_stream,
system_messages=system_messages,
model_context=model_context,
workbench=workbench,
handoff_tools=handoff_tools,
agent_name=agent_name,
cancellation_token=cancellation_token,
output_content_type=output_content_type,
message_id=message_id, # Use same message ID for consistency
):
if isinstance(llm_output, CreateResult):
next_model_result = llm_output
else:
# Streaming chunk event
yield llm_output
# Yield ToolCallExecutionEvent
tool_call_result_msg = ToolCallExecutionEvent(
content=exec_results,
source=agent_name,
)
event_logger.debug(tool_call_result_msg)
await model_context.add_message(FunctionExecutionResultMessage(content=exec_results))
inner_messages.append(tool_call_result_msg)
yield tool_call_result_msg
assert next_model_result is not None, "No model result was produced in tool call loop."
current_model_result = next_model_result
# STEP 4C: Check for handoff
handoff_output = cls._check_and_handle_handoff(
model_result=model_result,
executed_calls_and_results=executed_calls_and_results,
inner_messages=inner_messages,
handoffs=handoffs,
agent_name=agent_name,
)
if handoff_output:
yield handoff_output
return
# Yield thought event if present
if current_model_result.thought:
thought_event = ThoughtEvent(content=current_model_result.thought, source=agent_name)
yield thought_event
inner_messages.append(thought_event)
# STEP 4D: Reflect or summarize tool results
# Add the assistant message to the model context (including thought if present)
await model_context.add_message(
AssistantMessage(
content=current_model_result.content,
source=agent_name,
thought=getattr(current_model_result, "thought", None),
)
)
# After the loop, reflect or summarize tool results
if reflect_on_tool_use:
async for reflection_response in cls._reflect_on_tool_use_flow(
system_messages=system_messages,
model_client=model_client,
model_client_stream=model_client_stream,
model_context=model_context,
workbench=workbench,
handoff_tools=handoff_tools,
agent_name=agent_name,
inner_messages=inner_messages,
output_content_type=output_content_type,
cancellation_token=cancellation_token,
):
yield reflection_response
else:
@ -1136,6 +1329,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
tool_call_summary_formatter=tool_call_summary_formatter,
agent_name=agent_name,
)
return
@staticmethod
def _check_and_handle_handoff(
@ -1145,9 +1339,17 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
handoffs: Dict[str, HandoffBase],
agent_name: str,
) -> Optional[Response]:
"""
Detect handoff calls, generate the HandoffMessage if needed, and return a Response.
If multiple handoffs exist, only the first is used.
"""Check for and handle any handoff requests in the model result.
Args:
model_result: Result from model inference
executed_calls_and_results: List of executed tool calls and their results
inner_messages: List of messages generated during processing
handoffs: Dictionary of available handoff configurations
agent_name: Name of the agent
Returns:
Optional response containing handoff message if handoff detected
"""
handoff_reqs = [
call for call in model_result.content if isinstance(call, FunctionCall) and call.name in handoffs
@ -1217,9 +1419,12 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
model_client: ChatCompletionClient,
model_client_stream: bool,
model_context: ChatCompletionContext,
workbench: Sequence[Workbench],
handoff_tools: List[BaseTool[Any, Any]],
agent_name: str,
inner_messages: List[BaseAgentEvent | BaseChatMessage],
output_content_type: type[BaseModel] | None,
cancellation_token: CancellationToken,
) -> AsyncGenerator[Response | ModelClientStreamingChunkEvent | ThoughtEvent, None]:
"""
If reflect_on_tool_use=True, we do another inference based on tool results
@ -1237,6 +1442,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
async for chunk in model_client.create_stream(
llm_messages,
json_output=output_content_type,
cancellation_token=cancellation_token,
):
if isinstance(chunk, CreateResult):
reflection_result = chunk
@ -1247,7 +1453,9 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
else:
raise RuntimeError(f"Invalid chunk type: {type(chunk)}")
else:
reflection_result = await model_client.create(llm_messages, json_output=output_content_type)
reflection_result = await model_client.create(
llm_messages, json_output=output_content_type, cancellation_token=cancellation_token
)
if not reflection_result or not isinstance(reflection_result.content, str):
raise RuntimeError("Reflect on tool use produced no valid text response.")
@ -1305,19 +1513,16 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
normal_tool_calls = [(call, result) for call, result in executed_calls_and_results if call.name not in handoffs]
def default_tool_call_summary_formatter(call: FunctionCall, result: FunctionExecutionResult) -> str:
return tool_call_summary_format
summary_formatter = tool_call_summary_formatter or default_tool_call_summary_formatter
tool_call_summaries = [
summary_formatter(call, result).format(
return tool_call_summary_format.format(
tool_name=call.name,
arguments=call.arguments,
result=result.content,
is_error=result.is_error,
)
for call, result in normal_tool_calls
]
summary_formatter = tool_call_summary_formatter or default_tool_call_summary_formatter
tool_call_summaries = [summary_formatter(call, result) for call, result in normal_tool_calls]
tool_call_summary = "\n".join(tool_call_summaries)
return Response(
@ -1461,6 +1666,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
else None,
model_client_stream=self._model_client_stream,
reflect_on_tool_use=self._reflect_on_tool_use,
max_tool_iterations=self._max_tool_iterations,
tool_call_summary_format=self._tool_call_summary_format,
structured_message_factory=self._structured_message_factory.dump_component()
if self._structured_message_factory
@ -1492,6 +1698,7 @@ class AssistantAgent(BaseChatAgent, Component[AssistantAgentConfig]):
system_message=config.system_message,
model_client_stream=config.model_client_stream,
reflect_on_tool_use=config.reflect_on_tool_use,
max_tool_iterations=config.max_tool_iterations,
tool_call_summary_format=config.tool_call_summary_format,
output_content_type=output_content_type,
output_content_type_format=format_string,

File diff suppressed because it is too large Load Diff

View File

@ -1,202 +1,222 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0" version="25.0.3">
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0" version="26.0.6">
<diagram name="Page-1" id="bkX10E6zblEP7POKMJXw">
<mxGraphModel dx="1768" dy="1089" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<mxGraphModel dx="775" dy="621" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="NpWbz43RdM9-YawIMZhB-93" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;" vertex="1" parent="1">
<mxGeometry x="180" y="220" width="490" height="800" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-93" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;" parent="1" vertex="1">
<mxGeometry x="180" y="220" width="500" height="890" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-90" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" vertex="1" parent="1">
<mxGeometry x="210" y="856" width="430" height="143" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-90" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" parent="1" vertex="1">
<mxGeometry x="210" y="951" width="430" height="143" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-100" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-83" target="NpWbz43RdM9-YawIMZhB-84">
<mxCell id="NpWbz43RdM9-YawIMZhB-100" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-83" target="NpWbz43RdM9-YawIMZhB-84" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-83" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-83" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" parent="1" vertex="1">
<mxGeometry x="210" y="248" width="430" height="61" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-118" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-84" target="NpWbz43RdM9-YawIMZhB-85">
<mxCell id="NpWbz43RdM9-YawIMZhB-118" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-84" target="NpWbz43RdM9-YawIMZhB-85" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-84" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-84" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" parent="1" vertex="1">
<mxGeometry x="210" y="329" width="430" height="90" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-122" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-85" target="NpWbz43RdM9-YawIMZhB-70">
<mxCell id="NpWbz43RdM9-YawIMZhB-122" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="NpWbz43RdM9-YawIMZhB-85" target="NpWbz43RdM9-YawIMZhB-70" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-85" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-85" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" parent="1" vertex="1">
<mxGeometry x="210" y="438" width="430" height="110" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-123" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-87" target="NpWbz43RdM9-YawIMZhB-111">
<mxCell id="NpWbz43RdM9-YawIMZhB-123" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="NpWbz43RdM9-YawIMZhB-87" target="NpWbz43RdM9-YawIMZhB-111" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-87" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-87" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;dashed=1;" parent="1" vertex="1">
<mxGeometry x="210" y="616" width="430" height="114" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-78" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-90" target="NpWbz43RdM9-YawIMZhB-51">
<mxCell id="NpWbz43RdM9-YawIMZhB-78" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-90" target="NpWbz43RdM9-YawIMZhB-51" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-50" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="230" y="910" width="120" height="80" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-50" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="1005" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-35" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-35" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="646" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-3" target="NpWbz43RdM9-YawIMZhB-12">
<mxCell id="NpWbz43RdM9-YawIMZhB-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-3" target="NpWbz43RdM9-YawIMZhB-12" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-3" value="New Messages" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-3" value="New Messages" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="245" y="170" width="90" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-68" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=1;exitDx=0;exitDy=-15;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-6" target="NpWbz43RdM9-YawIMZhB-19">
<mxCell id="NpWbz43RdM9-YawIMZhB-68" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=1;exitDx=0;exitDy=-15;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-6" target="NpWbz43RdM9-YawIMZhB-19" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-6" value="Memory" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-6" value="Memory" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
<mxGeometry x="520" y="340" width="90" height="69" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-30" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.13;exitY=0.77;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-10" target="NpWbz43RdM9-YawIMZhB-29">
<mxCell id="NpWbz43RdM9-YawIMZhB-30" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.13;exitY=0.77;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-10" target="NpWbz43RdM9-YawIMZhB-29" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-10" value="Model Client" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-10" value="Model Client" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
<mxGeometry x="490" y="438" width="150" height="100" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-89" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=60;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-11" target="NpWbz43RdM9-YawIMZhB-35">
<mxCell id="NpWbz43RdM9-YawIMZhB-89" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0;exitDx=0;exitDy=60;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-11" target="NpWbz43RdM9-YawIMZhB-35" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-11" value="Tools" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;darkOpacity2=0.1;fillColor=#ffe6cc;strokeColor=#d79b00;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-11" value="Tools" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;darkOpacity2=0.1;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="1" vertex="1">
<mxGeometry x="505" y="636" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-12" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-12" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="259" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-17" value="1. Add New Messages to Context" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-17" value="1. Add New Messages to Context" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="370" y="263.5" width="190" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-19" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-19" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="349" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-22" value="2. Update Context" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-22" value="2. Update Context" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="370" y="359" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-24" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-24" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="349" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0.25;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-27" target="NpWbz43RdM9-YawIMZhB-10">
<mxCell id="NpWbz43RdM9-YawIMZhB-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0.25;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-27" target="NpWbz43RdM9-YawIMZhB-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-31" value="3. Chat Completion" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-31" value="3. Chat Completion" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="370" y="475" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-32" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-32" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="626" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-33" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-33" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="626" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-34" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-34" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="626" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-38" value="4. Tool Execution" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-38" value="4. Tool Execution" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="370" y="666" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.13;exitY=0.77;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-41" target="NpWbz43RdM9-YawIMZhB-50">
<mxCell id="NpWbz43RdM9-YawIMZhB-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.13;exitY=0.77;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-41" target="NpWbz43RdM9-YawIMZhB-50" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="370" y="950" as="targetPoint" />
<mxPoint x="370" y="1045" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-41" value="Model Client" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="490" y="870" width="150" height="100" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-41" value="Model Client" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
<mxGeometry x="490" y="965" width="150" height="100" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0.25;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-49" target="NpWbz43RdM9-YawIMZhB-41">
<mxCell id="NpWbz43RdM9-YawIMZhB-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0.25;entryDx=0;entryDy=0;entryPerimeter=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-49" target="NpWbz43RdM9-YawIMZhB-41" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="370" y="910" as="sourcePoint" />
<mxPoint x="370" y="1005" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-45" value="5. Chat Completion (Reflect on Tool Use)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="355" y="905" width="130" height="30" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-45" value="5. Chat Completion (Reflect on Tool Use)" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="355" y="1000" width="130" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-46" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="230" y="890" width="120" height="80" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-46" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="985" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-47" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="230" y="870" width="120" height="80" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-47" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="965" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-48" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="230" y="870" width="120" height="60" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-48" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="965" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-49" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxGeometry x="230" y="870" width="120" height="40" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-49" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="965" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-51" value="Response&lt;div&gt;(Text)&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="395" y="1040" width="60" height="30" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-51" value="Response&lt;div&gt;(Text)&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="395" y="1135" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-53" value="Response&amp;nbsp;&lt;div&gt;(Tool Result Summary)&lt;/div&gt;" style="text;html=1;align=right;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="10" y="806" width="140" height="30" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-53" value="Response&amp;nbsp;&lt;div&gt;(Tool Result Summary)&lt;/div&gt;" style="text;html=1;align=right;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="10" y="888" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-58" value="Response&amp;nbsp;&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;(Text Message)&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=right;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-58" value="Response&amp;nbsp;&lt;div&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;(Text Message)&lt;/span&gt;&lt;/div&gt;" style="text;html=1;align=right;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="10" y="569" width="140" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-67" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=15;entryPerimeter=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-24" target="NpWbz43RdM9-YawIMZhB-6">
<mxCell id="NpWbz43RdM9-YawIMZhB-67" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=15;entryPerimeter=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-24" target="NpWbz43RdM9-YawIMZhB-6" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-29" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-29" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="455" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-26" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-26" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="455" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-27" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-27" value="Model Context" style="shape=document;whiteSpace=wrap;html=1;boundedLbl=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="230" y="455" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-71" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-70" target="NpWbz43RdM9-YawIMZhB-58">
<mxCell id="NpWbz43RdM9-YawIMZhB-71" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="NpWbz43RdM9-YawIMZhB-70" target="NpWbz43RdM9-YawIMZhB-58" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-126" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-126" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="422.75" y="599" as="sourcePoint" />
<mxPoint x="424" y="616" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-70" value="Tool Call Detected?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-70" value="Tool Call Detected?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="367.5" y="569" width="112.5" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-77" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-74" target="NpWbz43RdM9-YawIMZhB-53">
<mxCell id="NpWbz43RdM9-YawIMZhB-77" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-74" target="NpWbz43RdM9-YawIMZhB-53" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-124" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-74" target="NpWbz43RdM9-YawIMZhB-90">
<mxCell id="NpWbz43RdM9-YawIMZhB-124" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-74" target="NpWbz43RdM9-YawIMZhB-90" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-74" value="Reflect on Tool Use?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="365.5" y="806" width="120" height="30" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-74" value="Reflect on Tool Use?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="365.5" y="888" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-106" value="No" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-106" value="No" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="230" y="560" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-107" value="No" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="230" y="790" width="60" height="30" as="geometry" />
<mxCell id="NpWbz43RdM9-YawIMZhB-107" value="No" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="230" y="877" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-110" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=30;entryPerimeter=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-34" target="NpWbz43RdM9-YawIMZhB-11">
<mxCell id="NpWbz43RdM9-YawIMZhB-110" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=30;entryPerimeter=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-34" target="NpWbz43RdM9-YawIMZhB-11" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-113" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-111" target="NpWbz43RdM9-YawIMZhB-112">
<mxCell id="NpWbz43RdM9-YawIMZhB-113" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="NpWbz43RdM9-YawIMZhB-111" target="NpWbz43RdM9-YawIMZhB-112" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-117" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-111" target="NpWbz43RdM9-YawIMZhB-74">
<mxCell id="NpWbz43RdM9-YawIMZhB-117" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="ajhwLJHiypqY-D0M9j8j-1" target="NpWbz43RdM9-YawIMZhB-74" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-111" value="Handoff Detected?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=1;" vertex="1" parent="1">
<mxCell id="ajhwLJHiypqY-D0M9j8j-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="NpWbz43RdM9-YawIMZhB-111" target="ajhwLJHiypqY-D0M9j8j-1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-111" value="Handoff Detected?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=1;" parent="1" vertex="1">
<mxGeometry x="370.75" y="754" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-112" value="Response&lt;div&gt;(Handoff)&lt;/div&gt;" style="text;html=1;align=right;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-112" value="Response&lt;div&gt;(Handoff)&lt;/div&gt;" style="text;html=1;align=right;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="90" y="754" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-115" value="Yes" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-115" value="Yes" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="230" y="744" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="NpWbz43RdM9-YawIMZhB-127" value="Assistant Agent" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxCell id="NpWbz43RdM9-YawIMZhB-127" value="Assistant Agent" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="700" y="218" width="109.25" height="30" as="geometry" />
</mxCell>
<mxCell id="ajhwLJHiypqY-D0M9j8j-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="ajhwLJHiypqY-D0M9j8j-1" target="NpWbz43RdM9-YawIMZhB-85">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="660" y="835" />
<mxPoint x="660" y="493" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="ajhwLJHiypqY-D0M9j8j-1" value="Maximum Tool Iterations Reached?" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="325" y="820" width="201" height="30" as="geometry" />
</mxCell>
<mxCell id="ajhwLJHiypqY-D0M9j8j-3" value="Yes" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="420.75" y="850" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="ajhwLJHiypqY-D0M9j8j-5" value="No" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="550" y="808" width="60" height="30" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 59 KiB