tall-terabyte-97980
06/13/2025, 1:37 AMself.session.chat_ctx
which does not exist instead it has self.session._chat_ctx
.
What does exist though is self.chat_ctx
millions-winter-96505
06/14/2025, 2:49 AMstraight-smartphone-65394
06/15/2025, 1:37 AMthousands-night-79795
06/16/2025, 9:46 AMproud-boots-10188
06/16/2025, 6:47 PMfast-plastic-44892
06/18/2025, 12:38 PMlate-businessperson-58140
06/18/2025, 3:05 PMhallowed-bear-57167
06/19/2025, 11:36 AMmany-helmet-9770
06/20/2025, 11:32 AMconst receiver = new WebhookReceiver(process.env.LIVEKIT_API_KEY!, process.env.LIVEKIT_API_SECRET!);
// Middleware to handle 'application/webhook+json' content type
const webhookJsonParser = express.json({
type: ['application/json', 'application/webhook+json']
});
<http://router.post|router.post>('/lk-webhook', webhookJsonParser, async (req, res) => {
try {
console.log('Received webhook with content type:', req.get('content-type'));
console.log('Authorization:', req.get('Authorization'));
const event = await receiver.receive(req.body, req.get('Authorization'));
console.log('Webhook event:', event);
res.status(200).json({ message: 'ok' });
} catch (ex) {
console.error('Error processing webhook:', ex);
res.status(500).json({
message: 'Failed to handle webhook',
error: ex instanceof Error ? ex.message : 'Unknown error'
});
}
});
I triple checked my API key, both in my env key and the one I used while creating webhook are the samelimited-address-91743
06/20/2025, 3:45 PMproud-boots-10188
06/27/2025, 12:23 AMminiature-spring-29693
06/27/2025, 5:29 PMlk room list
CLI command, I'm seeing that the departure timeout is 20 seconds. I tried this with different names & i keep getting 20 seconds for departure timeout.miniature-spring-29693
06/27/2025, 5:29 PMboundless-branch-10227
06/29/2025, 10:02 PMbusy-fish-13948
06/30/2025, 12:12 PMpurple-leather-32430
07/01/2025, 10:13 AMlemon-elephant-62047
07/04/2025, 7:21 PMroomCreate: false
.
Issue:
Even with roomCreate: false
, participants still auto-create rooms on join. Also, there's no way to check if a room already exists since RoomServiceClient
isn’t available on Cloud.
Right now I’m tracking room names manually on the backend. Is there a better way to handle this on LiveKit Cloud?
Thanks!numerous-whale-53652
07/06/2025, 6:01 PMbreezy-action-29677
07/09/2025, 4:16 PMfew-analyst-46
07/10/2025, 4:04 AMfull-hydrogen-48173
07/11/2025, 3:06 AMbulky-fish-79784
07/12/2025, 10:00 PMchilly-camera-39410
07/13/2025, 5:09 AM[bad_optional_access.cc : 39] RAW: Bad optional access
[1] 93326 abort python mre.py
And here is also a minimal reproduction example
import livekit
import livekit.api
import livekit.rtc
import google.genai
import google.genai.live
import asyncio
import numpy
import cv2
import base64
import av
import time
import json
import os
import websockets_proxy
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
async def getLiveKitAPI():
return livekit.api.LiveKitAPI(f"<https://www.xiaokang00010.top:6212>", "token_here", "token_here")
userToken = livekit.api.AccessToken(
"token_here", "token_here").with_identity(
'user').with_name("Jerry Chou").with_grants(livekit.api.VideoGrants(room_join=True, room="testroom")).to_jwt()
botToken = livekit.api.AccessToken(
"token_here", "token_here").with_identity(
'model').with_name("Awwa").with_grants(livekit.api.VideoGrants(room_join=True, room="testroom")).to_jwt()
# livekit api is in this file, so we can't put this logic into createRtSession
async def f():
await (await getLiveKitAPI()).room.create_room(create=livekit.api.CreateRoomRequest(name="testroom", empty_timeout=10*60, max_participants=2))
asyncio.get_event_loop().run_until_complete(f())
print("User token: ", userToken)
print("Bot token: ", botToken)
class MRE:
def __init__(self, name = "Gemini"):
self.name = name
async def chatRealtime(self):
buffer = ''
while True:
async for response in self.llmSession.receive():
if response.text is None:
# a turn is finished
break
print(f"Recved {len(response.text)}")
buffer += response.text
print("End of turn ", buffer, self.llmSession._ws.close_code, self.llmSession._ws.close_reason)
buffer = ''
async def start(self, loop = new_loop):
if os.getenv("HTTP_PROXY"):
proxy = websockets_proxy.Proxy.from_url(os.getenv("HTTP_PROXY"))
def fake_connect(*args, **kwargs):
return websockets_proxy.proxy_connect(*args, proxy=proxy, **kwargs)
google.genai.live.connect = fake_connect
print("Preparing for launch...")
client = google.genai.Client(http_options={'api_version': 'v1alpha'})
model_id = "gemini-2.0-flash-exp"
config = {"response_modalities": ["TEXT"]}
self.llmPreSession = client.aio.live.connect(model=model_id, config=config)
self.llmSession: google.genai.live.AsyncSession = await self.llmPreSession.__aenter__()
self.chatRoom = livekit.rtc.Room(loop)
asyncio.ensure_future(self.chatRealtime())
@self.chatRoom.on("track_subscribed")
def on_track_subscribed(track: livekit.rtc.Track, publication: livekit.rtc.RemoteTrackPublication, participant: livekit.rtc.RemoteParticipant):
print(f"track subscribed: {publication.sid}")
if track.kind == livekit.rtc.TrackKind.KIND_VIDEO:
print('running video stream...')
asyncio.ensure_future(self.receiveVideoStream(
livekit.rtc.VideoStream(track)))
elif track.kind == livekit.rtc.TrackKind.KIND_AUDIO:
print('running voice activity detection...')
asyncio.ensure_future(
self.forwardAudioStream(livekit.rtc.AudioStream(track), publication.mime_type))
@self.chatRoom.on("track_unsubscribed")
def on_track_unsubscribed(track: livekit.rtc.Track, publication: livekit.rtc.RemoteTrackPublication, participant: livekit.rtc.RemoteParticipant):
print(f"track unsubscribed: {publication.sid}")
@self.chatRoom.on("participant_connected")
def on_participant_connected(participant: livekit.rtc.RemoteParticipant):
print(f"participant connected: {
participant.identity} {participant.sid}")
@self.chatRoom.on("participant_disconnected")
def on_participant_disconnected(participant: livekit.rtc.RemoteParticipant):
print(
f"participant disconnected: {
participant.sid} {participant.identity}"
)
self.terminateSession()
@self.chatRoom.on("connected")
def on_connected() -> None:
print("connected")
print("Connecting to LiveKit...")
await self.chatRoom.connect(f"<wss://www.xiaokang00010.top:6212>", botToken)
print("Connected to LiveKit.")
# publish track
# audio
# audioSource = livekit.rtc.AudioSource(
# 48000, 1)
# self.broadcastAudioTrack = livekit.rtc.LocalAudioTrack.create_audio_track(
# "stream_track", audioSource)
# publication_audio = await self.chatRoom.local_participant.publish_track(
# self.broadcastAudioTrack, livekit.rtc.TrackPublishOptions(source=livekit.rtc.TrackSource.SOURCE_MICROPHONE, red=False))
# import threading
# self.audioBroadcastingThread = threading.Thread(
# target=self.runBroadcastingLoop, args=(audioSource,))
# self.audioBroadcastingThread.start()
video_source = livekit.rtc.VideoSource(640, 480)
self.broadcastVideoTrack = livekit.rtc.LocalVideoTrack.create_video_track(
"stream_track", video_source)
publication_video = await self.chatRoom.local_participant.publish_track(
self.broadcastVideoTrack, livekit.rtc.TrackPublishOptions(source=livekit.rtc.TrackSource.SOURCE_CAMERA, red=False))
print("Published video track.")
print("Waiting for participants to join...")
while True:
if self.llmSession:
print("Test", self.llmSession._ws.close_code, self.llmSession._ws.close_reason)
await asyncio.sleep(1)
def runBroadcastingLoop(self, audioSource) -> None:
"""
Start the loop for broadcasting missions.
Returns:
None
"""
print('starting broadcasting loop')
new_loop = asyncio.new_event_loop()
new_loop.run_until_complete(self.broadcastAudioLoop(audioSource))
def generateEmptyAudioFrame(self) -> livekit.rtc.AudioFrame:
"""
Generate an empty audio frame.
Returns:
livekit.rtc.AudioFrame: empty audio frame
"""
amplitude = 32767 # for 16-bit audio
samples_per_channel = 480 # 10ms at 48kHz
time = numpy.arange(samples_per_channel) / \
48000
total_samples = 0
audio_frame = livekit.rtc.AudioFrame.create(
48000, 1, samples_per_channel)
audio_data = numpy.frombuffer(audio_frame.data, dtype=numpy.int16)
time = (total_samples + numpy.arange(samples_per_channel)) / \
48000
wave = numpy.int16(0)
numpy.copyto(audio_data, wave)
# logger.Logger.log('done1')
return audio_frame
async def receiveVideoStream(self, stream: livekit.rtc.VideoStream):
async for frame in stream:
img = frame.frame.convert(
livekit.rtc.VideoBufferType.BGRA).data.tobytes()
img_np = numpy.frombuffer(img, dtype=numpy.uint8).reshape(
frame.frame.height,
frame.frame.width,
4
)
# convert to jpeg
# resize the image so as to save the token
scaler = frame.frame.width / 1280
new_width, new_height = (int(
frame.frame.width // scaler), int(frame.frame.height // scaler))
cv2.resize(img_np, (new_width, new_height))
encoded, buffer = cv2.imencode('.jpg', img_np)
await self.llmSession.send({"data": base64.b64encode(buffer.tobytes()).decode(), "mime_type": "image/jpeg"})
async def forwardAudioStream(self, stream: livekit.rtc.AudioStream, mime_type: str):
frames = 0
last_sec = time.time()
last_sec_frames = 0
limit_to_send = 100
data_chunk = b''
async for frame in stream:
last_sec_frames += 1
frames += 1
avFrame = av.AudioFrame.from_ndarray(numpy.frombuffer(frame.frame.remix_and_resample(16000, 1).data, dtype=numpy.int16).reshape(frame.frame.num_channels, -1), layout='mono', format='s16')
data_chunk += avFrame.to_ndarray().tobytes()
if frames % limit_to_send == 0:
await self.llmSession.send({"data": data_chunk, "mime_type": "audio/pcm"})
data_chunk = b''
if time.time() - last_sec > 1:
last_sec = time.time()
print(f"forwardAudioStream: last second: {last_sec_frames} frames, num_channels: {frame.frame.num_channels}, sample_rate: {frame.frame.sample_rate}, limit_to_send: {limit_to_send}")
last_sec_frames = 0
async def broadcastAudioLoop(self, source: livekit.rtc.AudioSource, frequency: int = 1000):
print('broadcasting audio...')
while True:
await source.capture_frame(self.generateEmptyAudioFrame())
mre = MRE()
asyncio.ensure_future(mre.start(new_loop))
new_loop.run_forever()
My livekit version and OS information
❯ pip freeze | grep livekit
livekit==1.0.11
livekit-api==1.0.3
livekit-protocol==1.0.4
❯ uname -a
Darwin Yoimiyas-MacBook-Air.local 25.0.0 Darwin Kernel Version 25.0.0: Mon Jun 30 22:07:46 PDT 2025; root:xnu-12377.0.132.0.2~69/RELEASE_ARM64_T8103 arm64
Since I can not find any helpful suggestions from the official GitHub repo issues and prs, I first thought it would be a misconfiguration of my own code. However, it reoccured stablely on both my Linux server and my M1 Macbook Air. So I thought it might be a bug.swift-fireman-54201
07/14/2025, 10:26 AMlittle-activity-13208
07/15/2025, 3:41 PMawait ctx.api.room.update_room_metadata(
api.UpdateRoomMetadataRequest(
room=ctx.room.name,
metadata=json.dumps(updated_metadata)
)
)
# await fetch_until_success(lambda room_info: room_info.metadata == json.dumps(updated_metadata))
print("waiting")
await asyncio.sleep(5)
print("done waiting")
await ctx.api.room.delete_room(
api.DeleteRoomRequest(
room=ctx.room.name,
)
)
Fetching room from the api directly, however, does show the updated metadata, as the below code checks the two metadata as identical:
async def fetch_room_info():
"""Fetch room information from the LiveKit server"""
ctx = get_job_context()
room_info = await ctx.api.room.list_rooms(
api.ListRoomsRequest(names=[ctx.room.name])
)
return room_info.rooms[0]
async def fetch_until_success(end_condition: Callable[[Any], bool], max_retries: int = 10):
"""Fetch room information from the LiveKit server until the end condition is met"""
for _ in range(max_retries):
room_info = await fetch_room_info()
print(f"Room info: {room_info}")
if end_condition(room_info):
print(f"End condition met: {room_info}")
return room_info, True
await asyncio.sleep(1)
return None, False
Any help would be appreciated, thanks!fierce-zoo-69908
07/15/2025, 3:42 PMwide-tailor-66933
07/16/2025, 5:24 AMasync def yeah():
async with api.LiveKitAPI(url=cfg.LIVEKIT_URL, api_key=cfg.LIVEKIT_API_KEY, api_secret=cfg.LIVEKIT_API_SECRET) as lkapi:
rooms_response = await lkapi.room.list_rooms(api.ListRoomsRequest())
container = {}
for room in rooms_response.rooms:
container[room.name] = room.num_participants
print(room.name, room.num_participants)
return container
Why do the number of participants fluctuate, usually changing from 0 to 2 or 2 to 0? Any reason why? My application relies on this to check empty room and turns out the API response fluctuates a lot. From my simple observation, the first 10 rooms always returns nonzero, the rest is zero. If I query with names, api.ListRoomsRequest(names=["room-123"])
, this one never fluctuateshundreds-dawn-90573
07/16/2025, 6:03 AMsdp_offer
and we can create a sdp answer and send them a request to accept the call.
I just wanted to know if it will be possible to create webrtc
connection that can accept the sdp_offer
from WhatsApp and stream audio back n forth b/w caller and agent (Livekit agent)
I don’t have much idea about webrtc, so just asking if anyone else has done something like this previously.
Thanks.many-helmet-9770
07/16/2025, 6:58 AMcuddly-eye-42352
07/17/2025, 10:36 AMcanSubscribeData