-
Notifications
You must be signed in to change notification settings - Fork 3k
AgentTask hardcodes exclude_function_call=True when merging chat context back to parent agent, causing loss of tool call history #5284
Description
Bug Description
When an AgentTask completes, the __await_impl finally block merges the sub-task's chat_ctx back into the parent agent's chat_ctx with a hardcoded exclude_function_call=True:
agents/livekit-agents/livekit/agents/voice/agent.py
Lines 893 to 899 in 0ed8e6b
| merged_chat_ctx = old_agent.chat_ctx.merge( | |
| self.chat_ctx, exclude_function_call=True, exclude_instructions=True | |
| ) | |
| # set the chat_ctx directly, `session._update_activity` will sync it to the rt_session if needed | |
| old_agent._chat_ctx.items[:] = merged_chat_ctx.items | |
| # await old_agent.update_chat_ctx(merged_chat_ctx) |
merged_chat_ctx = old_agent.chat_ctx.merge(
self.chat_ctx, exclude_function_call=True, exclude_instructions=True
)
# set the chat_ctx directly, [session._update_activity](cci:1://file:///Users/tony/src/python-core/.venv/lib/python3.11/site-packages/livekit/agents/voice/agent_session.py:1107:4-1181:63) will sync it to the rt_session if needed
old_agent._chat_ctx.items[:] = merged_chat_ctx.itemsThis unconditionally strips all function_call and function_call_output items from the sub-task's context when merging back to the parent. There is no parameter or hook to preserve them.
Impact
- Tool call history is permanently lost when an AgentTask completes. Any data retrieved via tool calls during the sub-task (e.g., MCP tool results, API responses, database lookups) is removed from the parent agent's context.
- The parent's LLM session is synced with the stripped context during
resume() → _start_session(), before the caller has any opportunity to intervene. For RealtimeModel, this meansrt_session.update_chat_ctx()is called with a context missing all function call history. - No workaround within the framework: the merge directly mutates
old_agent._chat_ctx.items[:], bypassingupdate_chat_ctx. Callers can manually callupdate_chat_ctxafter await task returns, but by that point the Realtime session has already been synced with the filtered context.
Expected Behavior
The merge behavior should be configurable. Users should be able to preserve function call history from sub-tasks when it's needed for context continuity (e.g., when users can switch between tasks and resume previous ones).
Reproduction Steps
- Create an Agent (parent) with tool-calling capabilities
- Create an AgentTask (sub-task) that executes tool calls during its lifecycle (e.g., MCP tools, function tools that return data)
- Complete the sub-task via self.complete(result)
- Observe the parent agent's chat_ctx after the task returns — all function_call and function_call_output items from the sub-task are missing
class MySubTask(AgentTask[str]):
async def on_enter(self):
# Tool calls happen during sub-task lifecycle
# e.g., MCP tools fetch data, function tools execute actions
...
self.complete("done")
# In parent agent's function tool:
result = await MySubTask(instructions="...", chat_ctx=self.chat_ctx.copy(), tools=[...])
# At this point, parent's chat_ctx has lost all function_call/function_call_output
# items from the sub-task
Operating System
macOS 15, all Linux platforms
Models Used
No response
Package Versions
livekit-agents==1.4.3 (Commit: 0ed8e6b9b04971b88d9fcecf099dcdf2f94d11d4)Session/Room/Call IDs
No response
Proposed Solution
Add a parameter to AgentTask (or complete()) to control the merge behavior:
class AgentTask(Agent, Generic[TaskResult_T]):
def __init__(self, *, merge_exclude_function_call: bool = True, ...):
self._merge_exclude_function_call = merge_exclude_function_call
...Then in __await_impl's finally block:
merged_chat_ctx = old_agent.chat_ctx.merge(
self.chat_ctx,
exclude_function_call=self._merge_exclude_function_call,
exclude_instructions=True,
)This preserves backward compatibility (default True) while allowing users to opt in to preserving function call history.
Additional Context
No response
Screenshots and Recordings
No response