Skip to content

Commit 65ad517

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feat/deepgram-with-cloudflare
# Conflicts: # livekit-plugins/livekit-plugins-deepgram/livekit/plugins/deepgram/stt.py
2 parents 8cf03bd + 8627da8 commit 65ad517

263 files changed

Lines changed: 10874 additions & 1687 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/avatar/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
livekit-agents[evals]>=1.5.7
1+
livekit-agents[evals]>=1.6
22
livekit-plugins-lemonslice>=1.5.7
33
livekit-plugins-silero>=1.5.7
44
livekit-plugins-turn-detector>=1.5.7

examples/avatar_agents/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ These providers work with pre-configured avatars using unique avatar identifiers
1414
- **[BitHuman](./bithuman/)** (Cloud mode) - [Platform](https://bithuman.ai/) | [Integration Guide](https://sdk.docs.bithuman.ai/#/preview/livekit-cloud-plugin)
1515
- **[LemonSlice](./lemonslice/)** - [Platform](https://www.lemonslice.com/) | [Integration Guide](https://lemonslice.com/docs/self-managed/livekit-agent-integration)
1616
- **[LiveAvatar](./liveavatar/)** - [Platform](https://www.liveavatar.com/)
17+
- **[Protoface](./protoface/)** - [Platform](https://protoface.com/)
1718
- **[Simli](./simli/)** - [Platform](https://app.simli.com/)
1819
- **[Tavus](./tavus/)** - [Platform](https://www.tavus.io/)
1920
- **[TruGen](./trugen/)** - [Platform](https://app.trugen.ai/)

examples/avatar_agents/lemonslice/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,31 @@ export LIVEKIT_URL="..."
2222
```bash
2323
python examples/avatar_agents/lemonslice/agent_worker.py dev
2424
```
25+
26+
## Third-party video meeting platforms (Zoom, Meet, Teams, Webex)
27+
28+
Use `agent_worker_meeting.py` to send the avatar into a third-party video meeting.
29+
The avatar joins the call, listens to meeting audio, and responds through the meeting
30+
relay.
31+
32+
Set the meeting URL via job metadata when dispatching the agent. For password-protected
33+
meetings, include the password in the URL (for example, Zoom links use a `pwd` query
34+
parameter):
35+
36+
```json
37+
{
38+
"meeting_url": "https://zoom.us/j/123456789?pwd=abcdef",
39+
"bot_name": "LemonSlice Avatar",
40+
"listen_to_meeting_chat": true
41+
}
42+
```
43+
44+
For local testing, you can also set `MEETING_URL` (and optionally `MEETING_BOT_NAME`
45+
or `LISTEN_TO_MEETING_CHAT`) in the environment instead of job metadata.
46+
`LISTEN_TO_MEETING_CHAT` accepts `true`/`false` or `1`/`0`.
47+
48+
```bash
49+
export MEETING_URL="https://zoom.us/j/123456789?pwd=abcdef"
50+
export LISTEN_TO_MEETING_CHAT="false"
51+
python examples/avatar_agents/lemonslice/agent_worker_meeting.py dev
52+
```
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import json
2+
import logging
3+
import os
4+
from typing import Any
5+
6+
from dotenv import load_dotenv
7+
8+
from livekit.agents import (
9+
Agent,
10+
AgentServer,
11+
AgentSession,
12+
JobContext,
13+
TurnHandlingOptions,
14+
cli,
15+
inference,
16+
)
17+
from livekit.plugins import lemonslice
18+
19+
logger = logging.getLogger("lemonslice-avatar-meeting-example")
20+
logger.setLevel(logging.INFO)
21+
22+
load_dotenv()
23+
24+
25+
server = AgentServer()
26+
27+
28+
def _optional_bool(value: Any, *, default: bool) -> bool:
29+
"""Parse a bool from job metadata (JSON) or an environment variable (string)."""
30+
if value is None:
31+
return default
32+
if isinstance(value, bool):
33+
return value
34+
if isinstance(value, (int, float)):
35+
return bool(value)
36+
if isinstance(value, str):
37+
normalized = value.strip().lower()
38+
if normalized in ("1", "true"):
39+
return True
40+
if normalized in ("0", "false"):
41+
return False
42+
raise ValueError(f"invalid boolean value: {value!r}")
43+
raise ValueError(f"invalid boolean value: {value!r}")
44+
45+
46+
@server.rtc_session()
47+
async def entrypoint(ctx: JobContext):
48+
await ctx.connect()
49+
50+
session = AgentSession(
51+
stt=inference.STT("deepgram/nova-3"),
52+
llm=inference.LLM("google/gemini-2.5-flash"),
53+
tts=inference.TTS("cartesia/sonic-3"),
54+
turn_handling=TurnHandlingOptions(
55+
interruption={
56+
"resume_false_interruption": False,
57+
},
58+
),
59+
)
60+
61+
lemonslice_image_url = os.getenv("LEMONSLICE_IMAGE_URL")
62+
if lemonslice_image_url is None:
63+
raise ValueError("LEMONSLICE_IMAGE_URL must be set")
64+
avatar = lemonslice.AvatarSession(
65+
agent_image_url=lemonslice_image_url,
66+
agent_prompt="Be expressive in your movements and use your hands while talking.",
67+
)
68+
await avatar.start(session, room=ctx.room)
69+
70+
meta = json.loads(ctx.job.metadata) if ctx.job.metadata else {}
71+
meeting_url = meta.get("meeting_url") or os.getenv("MEETING_URL")
72+
if not meeting_url:
73+
raise ValueError("Set meeting_url in job metadata or MEETING_URL env var")
74+
75+
join_kwargs: dict[str, str | bool] = {
76+
"listen_to_meeting_chat": _optional_bool(
77+
meta.get("listen_to_meeting_chat", os.getenv("LISTEN_TO_MEETING_CHAT")),
78+
default=True,
79+
),
80+
}
81+
bot_name = meta.get("bot_name") or os.getenv("MEETING_BOT_NAME")
82+
if bot_name:
83+
join_kwargs["bot_name"] = bot_name
84+
85+
await avatar.join_meeting(meeting_url, **join_kwargs)
86+
87+
agent = Agent(instructions="Talk to me!")
88+
89+
await session.start(
90+
agent=agent,
91+
room=ctx.room,
92+
room_options=avatar.room_options(),
93+
)
94+
95+
session.generate_reply(instructions="say hello to the user")
96+
97+
98+
if __name__ == "__main__":
99+
cli.run_app(server)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# LiveKit Protoface Avatar Agent
2+
3+
This example demonstrates how to create a realtime [Protoface](https://protoface.com/)
4+
avatar with LiveKit Agents.
5+
6+
## Usage
7+
8+
- Update the environment:
9+
10+
```bash
11+
# Protoface config. PROTOFACE_AVATAR_ID is optional and defaults to av_stock_001.
12+
export PROTOFACE_API_KEY="..."
13+
export PROTOFACE_AVATAR_ID="av_stock_001"
14+
15+
# Google config
16+
export GOOGLE_API_KEY="..."
17+
18+
# LiveKit config
19+
export LIVEKIT_API_KEY="..."
20+
export LIVEKIT_API_SECRET="..."
21+
export LIVEKIT_URL="..."
22+
```
23+
24+
- Start the agent worker:
25+
26+
```bash
27+
python examples/avatar_agents/protoface/agent_worker.py dev
28+
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import logging
2+
import os
3+
4+
from dotenv import load_dotenv
5+
6+
from livekit.agents import Agent, AgentServer, AgentSession, JobContext, cli
7+
from livekit.plugins import google, protoface
8+
9+
logger = logging.getLogger("protoface-avatar-example")
10+
logger.setLevel(logging.INFO)
11+
12+
load_dotenv()
13+
14+
server = AgentServer()
15+
16+
17+
@server.rtc_session()
18+
async def entrypoint(ctx: JobContext):
19+
session = AgentSession(
20+
llm=google.realtime.RealtimeModel(voice="Charon"),
21+
resume_false_interruption=False,
22+
)
23+
24+
avatar = protoface.AvatarSession(
25+
avatar_id=os.getenv("PROTOFACE_AVATAR_ID", protoface.DEFAULT_STOCK_AVATAR_ID),
26+
)
27+
await avatar.start(session, room=ctx.room)
28+
29+
await session.start(
30+
agent=Agent(instructions="Talk to me!"),
31+
room=ctx.room,
32+
)
33+
34+
35+
if __name__ == "__main__":
36+
cli.run_app(server)

examples/drive-thru/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
livekit-agents>=1.5.7
1+
livekit-agents>=1.6
22
livekit-plugins-silero>=1.5.7
33
livekit-plugins-turn-detector>=1.5.7
44
python-dotenv>=1.0.0

examples/frontdesk/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
livekit-agents[evals]>=1.5.7
1+
livekit-agents[evals]>=1.6
22
livekit-plugins-silero>=1.5.7
33
livekit-plugins-turn-detector>=1.5.7
44
python-dotenv>=1.0.0

examples/healthcare/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
livekit-agents>=1.5.7
1+
livekit-agents>=1.6
22
livekit-plugins-openai>=1.5.7
33
livekit-plugins-silero>=1.5.7
44
openai>=1.0.0

examples/hotel_receptionist/README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ name 'Smith', code 'HTL-AB12'`.
2121
## Architecture
2222

2323
```
24-
agent.py — HotelReceptionistAgent + every intent tool
25-
workflows.py — AgentTask subclasses (consent / booking flows / verify)
26-
hotel_db.py — HotelDB (apsw) + schema + views + pricing + dispute policy
27-
ui_view.py — SQLite changeset streamer for the playground
28-
fake_data/seed.py — manual seed script (writes fake_data/hotel.db)
24+
agent.py — HotelReceptionistAgent + tool mixins
25+
tools_*.py — Tool mixins: rooms, restaurant, services
26+
book_*.py — AgentTask subclasses for booking flows
27+
hotel_db.py — HotelDB (apsw) + schema + views + pricing + dispute policy
28+
instructions.py — Prompt instructions and routing rules
29+
ui_view.py — SQLite changeset streamer for the playground
30+
fake_data/seed.py — manual seed script (writes fake_data/hotel.db)
2931
```
3032

3133
The LLM never owns money values. `book_room` computes the total

0 commit comments

Comments
 (0)