-
Notifications
You must be signed in to change notification settings - Fork 2.6k
improve EndCallTool #4563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
improve EndCallTool #4563
Conversation
📝 WalkthroughWalkthroughIntroduces a full-featured EndCallTool with lifecycle hooks, delayed shutdown and optional room deletion; adds Toolset event dataclasses for tool-called/completed; and updates the voice-agent example to use an on_enter-driven RealtimeAgent, EndCallTool integration, and simplified session.start with new TTS. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Agent as Agent
participant EndCall as EndCallTool
participant Session as AgentSession
participant JobCtx as JobContext
participant RoomMgr as RoomManager
Client->>Agent: invoke end-call tool
Agent->>EndCall: _end_call(ctx)
EndCall->>Agent: emit ToolCalledEvent(ctx, args)
EndCall->>Agent: perform end_instructions (TTS/LLM)
EndCall->>Session: request session shutdown
Session->>JobCtx: emit CloseEvent(reason)
JobCtx->>EndCall: call _on_session_close(ev)
EndCall->>RoomMgr: schedule room deletion if delete_room=True
EndCall->>Agent: emit ToolCompletedEvent(ctx, output)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used📓 Path-based instructions (1)**/*.py📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (1)📚 Learning: 2026-01-22T03:28:16.289ZApplied to files:
🧬 Code graph analysis (1)livekit-agents/livekit/agents/beta/tools/end_call.py (4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
🔇 Additional comments (5)
✏️ Tip: You can disable this entire section by setting Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
examples/voice_agents/session_close_callback.py (1)
19-19: DuplicateAgentServerinstantiation.
server = AgentServer()appears twice (lines 19 and 39). The second instantiation on line 39 overwrites the first, making line 19 dead code.🐛 Proposed fix
Remove one of the duplicate declarations:
server = AgentServer() class MyAgent(Agent): ... - -server = AgentServer() - `@server.rtc_session`() async def entrypoint(ctx: JobContext):Also applies to: 39-39
🧹 Nitpick comments (1)
examples/voice_agents/session_close_callback.py (1)
35-36: Missingawaitforgenerate_reply.The
on_entermethod callsself.session.generate_reply()withoutawait. Looking at thegenerate_replysignature inagent_session.py, it returns aSpeechHandlesynchronously, so this is technically valid. However, for consistency and to potentially handle any returned handle, consider being explicit about the intent.If the intent is fire-and-forget, the current code works. If you want to ensure the greeting completes before proceeding, consider:
async def on_enter(self) -> None: - self.session.generate_reply(instructions="say hello to the user") + handle = self.session.generate_reply(instructions="say hello to the user") + await handle.wait_for_playout()
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
examples/voice_agents/session_close_callback.pylivekit-agents/livekit/agents/beta/tools/end_call.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
livekit-agents/livekit/agents/beta/tools/end_call.pyexamples/voice_agents/session_close_callback.py
🧬 Code graph analysis (2)
livekit-agents/livekit/agents/beta/tools/end_call.py (3)
livekit-agents/livekit/agents/job.py (3)
get_job_context(56-63)delete_room(423-447)_delete_room(431-442)livekit-agents/livekit/agents/voice/events.py (2)
CloseEvent(221-225)RunContext(32-81)examples/voice_agents/session_close_callback.py (1)
on_end_call(24-28)
examples/voice_agents/session_close_callback.py (3)
livekit-agents/livekit/agents/voice/agent_session.py (3)
AgentSession(135-1324)generate_reply(904-957)tools(429-430)livekit-agents/livekit/agents/voice/events.py (2)
RunContext(32-81)session(48-49)livekit-agents/livekit/agents/beta/tools/end_call.py (2)
tools(82-83)EndCallTool(30-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: livekit-plugins-cartesia
- GitHub Check: livekit-plugins-inworld
- GitHub Check: type-check (3.13)
- GitHub Check: livekit-plugins-deepgram
- GitHub Check: livekit-plugins-groq
- GitHub Check: livekit-plugins-openai
- GitHub Check: type-check (3.9)
- GitHub Check: unit-tests
🔇 Additional comments (9)
livekit-agents/livekit/agents/beta/tools/end_call.py (7)
1-7: LGTM!The imports are well-organized and include all necessary types for the implementation:
AwaitableandCallablefor type hints,CloseEventandRunContextfrom the voice events module.
9-23: LGTM!The
END_CALL_INSTRUCTIONSconstant provides clear, well-structured guidance for the LLM on when to invoke the end call tool, with appropriate do/don't scenarios.
26-27: LGTM!The default callback provides sensible behavior by generating a goodbye message with
tool_choice="none"to prevent recursive tool calls.
31-55: LGTM!The constructor is well-designed with sensible defaults. The
function_toolwrapper correctly combines the base instructions with any extra instructions provided.
67-79: LGTM!The
_on_session_closehandler correctly:
- Retrieves the job context
- Conditionally registers a shutdown callback for room deletion
- Shuts down the job context with the close reason
The separation of concerns between session close and job shutdown is well-designed.
81-83: LGTM!The
toolsproperty correctly returns the configured end call tool.
57-65: Use the correct API:ctx.wait_for_playout()instead ofctx.speech_handle.wait_for_playout().The proposed fix has a critical API error. Calling
ctx.speech_handle.wait_for_playout()from within a function tool raisesRuntimeErrorwith the message: "cannot callSpeechHandle.wait_for_playout()from inside the function tool ... that owns this SpeechHandle." TheSpeechHandleclass explicitly directs to useRunContext.wait_for_playout()instead for this use case.Additionally,
ctx.session.shutdown()with its defaultdrain=Trueparameter already awaits the completion of the current speech before closing the session, so the goodbye message should not be interrupted. If you still want to add an explicit wait for clarity, useawait ctx.wait_for_playout().Likely an incorrect or invalid review comment.
examples/voice_agents/session_close_callback.py (2)
1-10: LGTM!Imports are well-organized and include the new
RunContexttype needed for theon_end_callcallback.
42-80: LGTM!The session setup and
on_closehandler are well-implemented. The handler correctly iterates through all chat history item types and provides informative output.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@examples/voice_agents/session_close_callback.py`:
- Around line 33-34: The on_enter async method calls
self.session.generate_reply(...) which returns an awaitable SpeechHandle but is
not awaited; update on_enter to await the result (i.e., await
self.session.generate_reply(...)) so the greeting completes before the handler
continues, ensuring the speech is enqueued and executed as intended.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
examples/voice_agents/session_close_callback.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
examples/voice_agents/session_close_callback.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: type-check (3.13)
- GitHub Check: type-check (3.9)
- GitHub Check: livekit-plugins-cartesia
- GitHub Check: livekit-plugins-inworld
- GitHub Check: livekit-plugins-elevenlabs
- GitHub Check: livekit-plugins-openai
- GitHub Check: livekit-plugins-deepgram
- GitHub Check: unit-tests
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@examples/voice_agents/session_close_callback.py`:
- Line 5: Remove the unused RunContext import from the import list in
session_close_callback.py: edit the line importing from livekit.agents (which
currently includes Agent, AgentServer, AgentSession, CloseEvent, JobContext,
RunContext, cli) and delete RunContext so the import list only includes used
symbols (Agent, AgentServer, AgentSession, CloseEvent, JobContext, cli); this
will resolve the Ruff F401 unused-import failure.
♻️ Duplicate comments (1)
examples/voice_agents/session_close_callback.py (1)
27-28: Awaitgenerate_replyso the greeting actually runs.
Line 28 returns an awaitable; withoutawait, the greeting may never execute.✅ Proposed fix
- self.session.generate_reply(instructions="say hello to the user") + await self.session.generate_reply(instructions="say hello to the user")
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
examples/voice_agents/session_close_callback.pylivekit-agents/livekit/agents/beta/tools/end_call.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
examples/voice_agents/session_close_callback.pylivekit-agents/livekit/agents/beta/tools/end_call.py
🧬 Code graph analysis (1)
examples/voice_agents/session_close_callback.py (4)
livekit-agents/livekit/agents/voice/agent.py (5)
Agent(34-642)tools(107-113)session(635-642)instructions(99-104)tts(534-544)livekit-agents/livekit/agents/voice/agent_session.py (7)
AgentSession(135-1324)tools(429-430)generate_reply(904-957)tts(1264-1265)start(442-453)start(456-467)start(469-715)livekit-agents/livekit/agents/voice/events.py (3)
CloseEvent(221-225)RunContext(32-81)session(48-49)livekit-agents/livekit/agents/beta/tools/end_call.py (2)
tools(84-85)EndCallTool(26-85)
🪛 GitHub Actions: CI
examples/voice_agents/session_close_callback.py
[error] 5-5: F401 'livekit.agents.RunContext' imported but unused
🪛 GitHub Check: ruff
examples/voice_agents/session_close_callback.py
[failure] 5-5: Ruff (F401)
examples/voice_agents/session_close_callback.py:5:86: F401 livekit.agents.RunContext imported but unused
🔇 Additional comments (6)
livekit-agents/livekit/agents/beta/tools/end_call.py (4)
27-53: Clean EndCallTool wiring and config flow.
The constructor cleanly captures configuration and builds the tool description.
55-67: End-call execution flow looks solid.
The try/finally ensures shutdown even if end instructions fail.
69-81: Session-close handling is clear and safe.
Room deletion is correctly gated and shutdown reason is propagated.
83-85: Tools exposure is straightforward.
Returning a single configured tool here is appropriate.examples/voice_agents/session_close_callback.py (2)
21-25: EndCallTool integration looks good.
Usingend_instructionshere aligns with the new API.
36-43: Verify ElevenLabs TTS configuration for this example.
Line 39 switches totts="elevenlabs"—please confirm the provider/credentials are available in your dev environment so the example doesn’t fail at runtime.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Co-authored-by: David Zhao <dz@livekit.io>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@livekit-agents/livekit/agents/beta/tools/end_call.py`:
- Around line 30-55: The initializer uses undefined names: change the parameter
extra_description to extra_instructions (or update all uses to match the
declared name) so self._extra_instructions is set from the actual arg, and
replace or define the missing END_CALL_INSTRUCTIONS constant—either
import/rename to the existing module-level constant or add a new
END_CALL_INSTRUCTIONS string constant—and use that in the function_tool
description; update the docstring param name to match the final parameter name
and ensure self._end_call_tool uses the corrected identifiers (references:
__init__, self._extra_instructions, self._end_call_tool, END_CALL_INSTRUCTIONS).
♻️ Duplicate comments (1)
examples/voice_agents/session_close_callback.py (1)
27-28: Await the greeting generation to ensure it actually runs.
generate_replyis awaitable; withoutawait, the greeting may not complete before the handler continues. (Duplicate of earlier feedback.)✅ Proposed fix
async def on_enter(self) -> None: - self.session.generate_reply(instructions="say hello to the user") + await self.session.generate_reply(instructions="say hello to the user")
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
examples/voice_agents/session_close_callback.pylivekit-agents/livekit/agents/beta/tools/end_call.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
examples/voice_agents/session_close_callback.pylivekit-agents/livekit/agents/beta/tools/end_call.py
🧬 Code graph analysis (2)
examples/voice_agents/session_close_callback.py (4)
livekit-agents/livekit/agents/voice/agent.py (5)
Agent(34-642)tools(107-113)on_enter(206-208)session(635-642)instructions(99-104)livekit-agents/livekit/agents/voice/agent_session.py (2)
tools(429-430)generate_reply(904-957)livekit-agents/livekit/agents/voice/events.py (2)
CloseEvent(221-225)session(48-49)livekit-agents/livekit/agents/beta/tools/end_call.py (2)
tools(86-87)EndCallTool(26-87)
livekit-agents/livekit/agents/beta/tools/end_call.py (3)
livekit-agents/livekit/agents/job.py (4)
get_job_context(56-63)delete_room(423-447)_delete_room(431-442)add_shutdown_callback(358-375)livekit-agents/livekit/agents/llm/tool_context.py (2)
Toolset(41-46)info(142-143)livekit-agents/livekit/agents/voice/events.py (2)
CloseEvent(221-225)RunContext(32-81)
🪛 GitHub Check: ruff
livekit-agents/livekit/agents/beta/tools/end_call.py
[failure] 54-54: Ruff (F821)
livekit-agents/livekit/agents/beta/tools/end_call.py:54:53: F821 Undefined name extra_instructions
[failure] 54-54: Ruff (F821)
livekit-agents/livekit/agents/beta/tools/end_call.py:54:28: F821 Undefined name END_CALL_INSTRUCTIONS
[failure] 48-48: Ruff (F821)
livekit-agents/livekit/agents/beta/tools/end_call.py:48:36: F821 Undefined name extra_instructions
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: unit-tests
- GitHub Check: livekit-plugins-groq
- GitHub Check: livekit-plugins-deepgram
- GitHub Check: livekit-plugins-openai
- GitHub Check: livekit-plugins-inworld
- GitHub Check: livekit-plugins-cartesia
- GitHub Check: type-check (3.13)
- GitHub Check: type-check (3.9)
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@livekit-agents/livekit/agents/beta/tools/end_call.py`:
- Around line 73-75: The fire-and-forget task created and stored in
self._shutdown_session_task for _delayed_session_shutdown(ctx) can swallow
exceptions; update the creation to attach exception handling (e.g., use
task.add_done_callback to log exceptions from task.exception()) or run it inside
an asyncio.TaskGroup/await it during shutdown. Locate the code that sets
self._shutdown_session_task and either wrap the coroutine in a try/except and
schedule that wrapper, add a done-callback that logs task.exception()
(referencing _delayed_session_shutdown and self._shutdown_session_task), or
migrate to asyncio.TaskGroup to ensure exceptions are surfaced and the task is
cancelled/awaited on cleanup.
- Around line 63-76: In _end_call's inner function _on_speech_done, stop
accessing the private attribute ctx.session._activity.llm and use the public
property ctx.session.llm instead; update the conditional that checks
isinstance(llm, RealtimeModel) and llm.capabilities.auto_tool_reply_generation
to reference the new llm variable, leaving the rest of the logic (creating
self._shutdown_session_task via self._delayed_session_shutdown(ctx) or calling
ctx.session.shutdown()) unchanged.
♻️ Duplicate comments (2)
examples/voice_agents/session_close_callback.py (2)
49-51: Missingawaitongenerate_replycall inRealtimeAgent.on_enter.Same issue as
MyAgent- thegenerate_replycall should be awaited.🔧 Proposed fix
async def on_enter(self) -> None: - self.session.generate_reply(instructions="say hello to the user") + await self.session.generate_reply(instructions="say hello to the user")
32-34: Missingawaitongenerate_replycall.
generate_replyreturns an awaitableSpeechHandle. Withoutawait, the greeting may not execute as intended. This applies to bothMyAgent.on_enter(line 33) andRealtimeAgent.on_enter(line 50).🔧 Proposed fix
async def on_enter(self) -> None: - self.session.generate_reply(instructions="say hello to the user") + await self.session.generate_reply(instructions="say hello to the user")
🧹 Nitpick comments (1)
livekit-agents/livekit/agents/beta/tools/end_call.py (1)
41-47: Line exceeds 100 characters.Per coding guidelines, line 43 exceeds the 100-character maximum.
🔧 Proposed fix
Args: extra_description: Additional description to add to the end call tool. - delete_room: Whether to delete the room when the user ends the call. deleting the room disconnects all remote users, including SIP callers. + delete_room: Whether to delete the room when the user ends the call. Deleting + the room disconnects all remote users, including SIP callers. end_instructions: Instructions to generate a reply when the user ends the call if provided. on_tool_called: Callback to call when the tool is called. on_tool_completed: Callback to call when the tool is completed.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
examples/voice_agents/session_close_callback.pylivekit-agents/livekit/agents/beta/tools/end_call.pylivekit-agents/livekit/agents/llm/tool_context.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
livekit-agents/livekit/agents/beta/tools/end_call.pylivekit-agents/livekit/agents/llm/tool_context.pyexamples/voice_agents/session_close_callback.py
🧬 Code graph analysis (1)
livekit-agents/livekit/agents/llm/tool_context.py (1)
livekit-agents/livekit/agents/voice/events.py (1)
RunContext(32-81)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: livekit-plugins-deepgram
- GitHub Check: livekit-plugins-openai
- GitHub Check: type-check (3.9)
- GitHub Check: type-check (3.13)
- GitHub Check: unit-tests
🔇 Additional comments (5)
livekit-agents/livekit/agents/llm/tool_context.py (2)
24-31: LGTM! Proper use of TYPE_CHECKING to avoid circular imports.The forward reference pattern for
RunContextis correctly implemented, preventing runtime circular dependencies while maintaining type safety for static analysis.
45-53: LGTM! Well-structured event dataclasses.The nested
ToolCalledEventandToolCompletedEventdataclasses provide a clean interface for tool lifecycle hooks with appropriate type annotations.livekit-agents/livekit/agents/beta/tools/end_call.py (2)
98-114: LGTM! Proper timeout and cleanup handling.The
_delayed_session_shutdownmethod correctly handles the timeout case, cleans up the event handler in thefinallyblock, and ensures the session is always shut down.
116-129: LGTM! Clean session close handling.The
_on_session_closemethod properly orchestrates room deletion and job shutdown with the callback pattern.examples/voice_agents/session_close_callback.py (1)
36-47: LGTM! Clean demonstration ofon_tool_completedcallback.The
RealtimeAgentclass effectively demonstrates the new callback feature, using it to provide a custom goodbye message when the tool completes.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| self._shutdown_session_task = asyncio.create_task( | ||
| self._delayed_session_shutdown(ctx) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fire-and-forget task may silently swallow exceptions.
The task created here is stored but never awaited or cancelled. If _delayed_session_shutdown raises an exception, it will be silently lost. Consider adding exception handling or using asyncio.TaskGroup if available.
🔧 Proposed fix - add exception logging
+ async def _shutdown_with_error_handling() -> None:
+ try:
+ await self._delayed_session_shutdown(ctx)
+ except Exception:
+ logger.exception("error during delayed session shutdown")
+
else:
self._shutdown_session_task = asyncio.create_task(
- self._delayed_session_shutdown(ctx)
+ _shutdown_with_error_handling()
)🤖 Prompt for AI Agents
In `@livekit-agents/livekit/agents/beta/tools/end_call.py` around lines 73 - 75,
The fire-and-forget task created and stored in self._shutdown_session_task for
_delayed_session_shutdown(ctx) can swallow exceptions; update the creation to
attach exception handling (e.g., use task.add_done_callback to log exceptions
from task.exception()) or run it inside an asyncio.TaskGroup/await it during
shutdown. Locate the code that sets self._shutdown_session_task and either wrap
the coroutine in a try/except and schedule that wrapper, add a done-callback
that logs task.exception() (referencing _delayed_session_shutdown and
self._shutdown_session_task), or migrate to asyncio.TaskGroup to ensure
exceptions are surfaced and the task is cancelled/awaited on cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@livekit-agents/livekit/agents/beta/tools/end_call.py`:
- Line 65: Replace the private call to current_agent._get_activity_or_raise()
with the public AgentSession property: use ctx.session.llm instead of
ctx.session.current_agent._get_activity_or_raise().llm; update the reference in
end_call.py (the line assigning llm_v) to read from ctx.session.llm so you rely
on the public LLM resolution API and avoid coupling to the private
_get_activity_or_raise() method.
♻️ Duplicate comments (1)
livekit-agents/livekit/agents/beta/tools/end_call.py (1)
76-78: Fire-and-forget task may silently swallow exceptions.The task is stored but never awaited or given a done-callback, so any exception raised in
_delayed_session_shutdownwill be silently lost. Consider adding exception handling.Proposed fix - add exception logging
else: + async def _shutdown_with_logging() -> None: + try: + await self._delayed_session_shutdown(ctx) + except Exception: + logger.exception("error during delayed session shutdown") + self._shutdown_session_task = asyncio.create_task( - self._delayed_session_shutdown(ctx) + _shutdown_with_logging() )
🧹 Nitpick comments (1)
livekit-agents/livekit/agents/beta/tools/end_call.py (1)
41-46: Docstring line exceeds 100-character limit.Line 43 is ~147 characters. Per coding guidelines, wrap it to stay within 100 characters.
Suggested fix
Args: extra_description: Additional description to add to the end call tool. - delete_room: Whether to delete the room when the user ends the call. deleting the room disconnects all remote users, including SIP callers. + delete_room: Whether to delete the room when the user ends the call. Deleting the + room disconnects all remote users, including SIP callers. end_instructions: Instructions to generate a reply when the user ends the call if provided. on_tool_called: Callback to call when the tool is called. on_tool_completed: Callback to call when the tool is completed.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
livekit-agents/livekit/agents/beta/tools/end_call.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Format code with ruff
Run ruff linter and auto-fix issues
Run mypy type checker in strict mode
Maintain line length of 100 characters maximum
Ensure Python 3.9+ compatibility
Use Google-style docstrings
Files:
livekit-agents/livekit/agents/beta/tools/end_call.py
🧬 Code graph analysis (1)
livekit-agents/livekit/agents/beta/tools/end_call.py (5)
livekit-agents/livekit/agents/job.py (1)
get_job_context(56-63)livekit-agents/livekit/agents/llm/tool_context.py (11)
Tool(34-35)Toolset(44-59)function_tool(189-194)function_tool(198-203)function_tool(207-213)function_tool(217-223)function_tool(226-268)ToolCalledEvent(46-48)ToolCompletedEvent(51-53)info(155-156)tools(57-59)livekit-agents/livekit/agents/voice/events.py (3)
CloseEvent(221-225)RunContext(32-81)speech_handle(52-53)livekit-agents/livekit/agents/voice/speech_handle.py (2)
SpeechHandle(16-251)add_done_callback(163-164)livekit-agents/livekit/agents/voice/agent.py (1)
_get_activity_or_raise(346-351)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: unit-tests
🔇 Additional comments (5)
livekit-agents/livekit/agents/beta/tools/end_call.py (5)
1-9: LGTM!Imports are well-organized and all appear to be used in the implementation.
11-25: LGTM!The description provides clear, structured guidance for the LLM on when to invoke the end call tool.
118-131: LGTM!The session close handler correctly manages job context lifecycle—conditionally scheduling room deletion and properly propagating the close reason.
133-135: LGTM!The
toolsproperty correctly exposes the function tool as required by theToolsetinterface.
100-116: Code is correct as written.The delayed shutdown logic is well-structured with proper cleanup in the
finallyblock.SpeechHandleimplements__await__, making line 111'sawait speech_handlevalid and correct.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
chenghao-mou
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there might be a race condition when using a realtime model:
12:07:43.460 DEBUG livekit.agents executing tool {"function": "end_call", "arguments": "{}", "speech_id": "speech_253993d3812c"}
12:07:43.462 DEBUG livekit.agents end_call tool called
12:07:43.463 DEBUG livekit.agents tools execution completed {"speech_id": "speech_253993d3812c"}
12:07:43.652 DEBUG livekit.…ns.google send task finished.
Agent Session closed, reason: CloseReason.USER_INITIATED
====================
Chat History:
agent_handoff: None -> my_agent
assistant: Hello! How can I help you today?
function_call: end_call, arguments: {}
user: Hey, I'm just testing the to and call two. Oh, so I'm just going to say goodbye.
end_call: 'thanks the user for calling and tell them goodbye'
assistant: Let me check
====================
I didn't hear anything about the last message when using Google Realtime model (1/3 tests)
and if I tell the realtime model to "say goodbye and call the end call tool", it will get stuck:
12:22:31.971 DEBUG livekit.agents executing tool {"function": "end_call", "arguments": "{}", "speech_id": "speech_a6eed8cff32f"}
12:22:31.975 DEBUG livekit.agents end_call tool called
12:22:31.977 DEBUG livekit.agents tools execution completed {"speech_id": "speech_a6eed8cff32f"}
12:22:36.980 WARNI… livekit.agents tool reply timed out, shutting down session
| ctx.session.off("speech_created", _on_speech_created) | ||
| ctx.session.shutdown() | ||
|
|
||
| def _on_session_close(self, ev: CloseEvent) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we can clean up the _shutdown_session_task here?
| This is the final action the agent can take. | ||
| Once called, no further interaction is possible with the user. | ||
| Don't generate any other text or response when the tool is called. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line might not be needed if we allow tool reply?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is to prevent a text message alongside the tool call, the tool response shouldn't be affected.
I saw the same issue sometimes when using gemini realtime model, the tool reply is |
extra_descriptionandon_tool_called,on_tool_completedSummary by CodeRabbit
New Features
Updates
✏️ Tip: You can customize this high-level summary in your review settings.