Skip to content

Conversation

@longcw
Copy link
Contributor

@longcw longcw commented Jan 20, 2026

  1. close the job process and delete room after AgentSession closed
  2. add extra_description and on_end

Summary by CodeRabbit

  • New Features

    • Configurable End Call tool that can run a final-instruction callback (on_end) and optionally delete the room when a session closes.
    • Agents can use an on-enter hook to send a greeting when a session starts.
  • Updates

    • Session start API simplified (fewer parameters).
    • Default text-to-speech provider switched to cartesia/sonic-3.

✏️ Tip: You can customize this high-level summary in your review settings.

@chenghao-mou chenghao-mou requested a review from a team January 20, 2026 02:44
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

Adds a new EndCallTool class implementing end-call lifecycle with configurable on_end behavior and optional room deletion; updates the voice agent example to use on_enter, EndCallTool(on_end=...), switches TTS to "cartesia/sonic-3", and changes session.start to await session.start(agent=..., room=...).

Changes

Cohort / File(s) Summary
EndCallTool implementation
livekit-agents/livekit/agents/beta/tools/end_call.py
New EndCallTool class (subclass of Toolset) with constructor params extra_description: str = "", delete_room: bool = True, `on_end: str
Example update
examples/voice_agents/session_close_callback.py
Replaces on_exit with on_enter; instantiates EndCallTool(on_end=...) (instead of previous EndCallTool()); updates TTS provider to "cartesia/sonic-3"; changes session.start(...) call to await session.start(agent=MyAgent(), room=ctx.room) and removes obsolete room-delete/debug code and comments.

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant EndCallTool
  participant Session as AgentSession
  participant JobCtx as JobContext
  participant RoomMgr as RoomManager

  Agent->>EndCallTool: call end_call tool (_end_call)
  EndCallTool->>Session: register session-close listener
  EndCallTool->>Agent: execute on_end (string reply or await callable with RunContext)
  Agent-->>EndCallTool: on_end completes
  EndCallTool->>Session: request shutdown
  Session->>JobCtx: emit CloseEvent(reason)
  JobCtx->>EndCallTool: invoke _on_session_close(ev)
  EndCallTool->>RoomMgr: if delete_room true, schedule delete-on-shutdown
  JobCtx->>Session: finalize shutdown/cleanup
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through functions, neat and spry,
EndCallTool whispers the softest goodbye.
Agents enter, speak, then gently part,
Rooms tidy up with a woolly heart.
Hooray — carrots for tidy art! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'improve EndCallTool' is partially related to the changeset. It refers to a real aspect of the change (EndCallTool), but does not capture the main improvements: adding lifecycle callbacks (on_enter/on_end), closing the job process, deleting the room after session close, and adding extra_instructions feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64c66e7 and 395614d.

📒 Files selected for processing (2)
  • examples/voice_agents/session_close_callback.py
  • 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:

  • examples/voice_agents/session_close_callback.py
  • livekit-agents/livekit/agents/beta/tools/end_call.py
🧬 Code graph analysis (2)
examples/voice_agents/session_close_callback.py (2)
livekit-agents/livekit/agents/beta/tools/end_call.py (2)
  • tools (82-83)
  • EndCallTool (26-83)
examples/voice_agents/basic_agent.py (1)
  • on_enter (40-44)
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 (1)
  • Toolset (41-46)
livekit-agents/livekit/agents/voice/events.py (2)
  • CloseEvent (221-225)
  • 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). (1)
  • GitHub Check: livekit-plugins-deepgram
🔇 Additional comments (6)
livekit-agents/livekit/agents/beta/tools/end_call.py (4)

27-53: Clean EndCallTool initialization and wiring.

The constructor and tool registration read clearly and align with the updated API surface.


55-65: Verify goodbye audio completes before shutdown.

If session.generate_reply(...) returns before playout finishes, the immediate session.shutdown() could cut off the farewell. Please confirm the await behavior; if it doesn’t wait for playout, consider awaiting the returned handle’s playout completion (or the equivalent) before shutdown.


67-79: Shutdown + optional room deletion flow is clear.

The close listener and shutdown callback sequencing look coherent for the intended lifecycle.


81-83: LGTM.

Exposing the configured tool via the property is straightforward.

examples/voice_agents/session_close_callback.py (2)

5-5: Example agent wiring looks good.

EndCallTool usage and the on-enter greeting are coherent for the updated lifecycle.

Also applies to: 20-28


36-44: LGTM.

Awaiting session.start(...) clarifies startup flow in the example.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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: Duplicate AgentServer instantiation.

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: Missing await for generate_reply.

The on_enter method calls self.session.generate_reply() without await. Looking at the generate_reply signature in agent_session.py, it returns a SpeechHandle synchronously, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0722371 and 38a19e4.

📒 Files selected for processing (2)
  • examples/voice_agents/session_close_callback.py
  • 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
  • examples/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: Awaitable and Callable for type hints, CloseEvent and RunContext from the voice events module.


9-23: LGTM!

The END_CALL_INSTRUCTIONS constant 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_tool wrapper correctly combines the base instructions with any extra instructions provided.


67-79: LGTM!

The _on_session_close handler correctly:

  1. Retrieves the job context
  2. Conditionally registers a shutdown callback for room deletion
  3. 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 tools property correctly returns the configured end call tool.


57-65: Use the correct API: ctx.wait_for_playout() instead of ctx.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 raises RuntimeError with the message: "cannot call SpeechHandle.wait_for_playout() from inside the function tool ... that owns this SpeechHandle." The SpeechHandle class explicitly directs to use RunContext.wait_for_playout() instead for this use case.

Additionally, ctx.session.shutdown() with its default drain=True parameter 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, use await 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 RunContext type needed for the on_end_call callback.


42-80: LGTM!

The session setup and on_close handler 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 38a19e4 and 0c6b434.

📒 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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: Await generate_reply so the greeting actually runs.
Line 28 returns an awaitable; without await, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0c6b434 and 9b1e72b.

📒 Files selected for processing (2)
  • examples/voice_agents/session_close_callback.py
  • 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:

  • examples/voice_agents/session_close_callback.py
  • livekit-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.
Using end_instructions here aligns with the new API.


36-43: Verify ElevenLabs TTS configuration for this example.
Line 39 switches to tts="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.

longcw and others added 2 commits January 20, 2026 16:20
Co-authored-by: David Zhao <dz@livekit.io>
@longcw longcw requested a review from davidzhao January 20, 2026 08:23
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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_reply is awaitable; without await, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 64c66e7 and 4fd8b99.

📒 Files selected for processing (2)
  • examples/voice_agents/session_close_callback.py
  • 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:

  • examples/voice_agents/session_close_callback.py
  • livekit-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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants