agents icon indicating copy to clipboard operation
agents copied to clipboard

JobRequest- req.room.metadata is null in livekit-agents==0.8.9 and 0.8.10 on self hosted cluster.

Open Test-isom opened this issue 1 year ago • 5 comments

For LiveKit agent plugin versions livekit-agents==0.8.8, we get room metadata in

async def request_fnc(req: JobRequest) -> None:

in req.room.metadata as value what we set while creating room, but in version 0.8.9 and 0.8.10 room metadata is empty.This is creating issue as we decide whether to accept or reject job by an worker based on metadata.

For debugging this further we setup another Kubernetes cluster with latest version of LiveKit server(1.7.2) and on that we faced same issue, but when we try same agent with a cloud url we are getting data for req.room.metadata in that case in request_fnc function.All this works fine upto version 0.8.8, and after that it works fine only against cloud deployments.

While debugging further we noticed that when worker registers against self hosted LiveKit server we get—

{"message": "registered worker", "level": "INFO", "id": "AW_RUiaBy8tZiJG", "server_info": "version: "1.7.2"\nprotocol: 14\nnode_id: "ND_YoMMvbptFrzu"\nagent_protocol: 1\n", "timestamp": "2024-09-06T13:38:17.174800+00:00"}

And on cloud we get- {"message": "registered worker", "level": "INFO", "id": "AW_9WpsyqqF9Zjp", "server_info": "edition: Cloud\nversion: "1.7.2"\nprotocol: 15\nregion: "India"\nnode_id: "NC_DBLR1A_auoA9TfuCnyV"\n", "timestamp": "2024-09-06T09:19:44.927947+00:00”}

Is it due to protocol difference 14 and 15?

Test-isom avatar Sep 06 '24 13:09 Test-isom

I am facing the same issue. Did you find a solution to this?

umarniz avatar Oct 18 '24 17:10 umarniz

Actually it was an issue on my end.

My approach where metadata is correctly passed to the agent request_func:

  • NextJS App (UI + Server)
  • LiveKit Agent (Python) -> this is where request_func is called

In the NextJS App I create an API to 'createRoom'. This API. creates a room, sets the metadata and generates a token for the user:

Example route.ts


import { RoomServiceClient, AccessToken } from 'livekit-server-sdk';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const apiKey = process.env.LIVEKIT_API_KEY;
  const apiSecret = process.env.LIVEKIT_API_SECRET;
  const wsUrl = process.env.LIVEKIT_WS_URL;

  if (!apiKey || !apiSecret || !wsUrl) {
    console.error('LiveKit environment variables are not set');
    return NextResponse.json({ error: 'Server misconfigured' }, { status: 500 });
  }

  const roomService = new RoomServiceClient(wsUrl, apiKey, apiSecret);

  try {
    const roomId = crypto.randomUUID()
   
      const room = await roomService.createRoom({
        name: roomId,
        metadata: JSON.stringify({ type: 'chat' }),
      });

      // Create an access token
      const at = new AccessToken(apiKey, apiSecret, {
        identity: 'user_id',
        ttl: '10m', // Token expires after 10 minutes
      });
      at.addGrant({ roomJoin: true, room: roomId });

      const token = await at.toJwt();

    return NextResponse.json({ room, token });
  } catch (error) {
    console.error('Error creating room and token:', error);
    return NextResponse.json({ error: 'Failed to create room and token' }, { status: 500 });
  }
}

Now I consume this client side like this:


'use client'

import { LiveKitRoom } from '@livekit/components-react'
import { Room } from 'livekit-client'
import { useEffect, useState } from 'react'

export interface LKRoomProps {
  token: string
  url: string
  children: React.ReactNode
}

export function LKRoom({ token, url, children }: LKRoomProps) {
  const [room, setRoom] = useState<Room | null>(null);

  useEffect(() => {
    const setupRoom = async () => {
        const response = await fetch('/api/create-room', { method: 'POST' });
        const data = await response.json();

        const newRoom = new Room();
        await newRoom.connect(url, data['token']);
        setRoom(newRoom);
    }
    
    setupRoom();
  }, [url, token]);

  if (!room) return null;

  return (
    <LiveKitRoom serverUrl={url} token={token} connect={true} room={room}>
      {children}
    </LiveKitRoom>
  );
}

umarniz avatar Oct 18 '24 18:10 umarniz

I have same issues is there any update on this ?

firattamurlc avatar Oct 27 '24 22:10 firattamurlc

same issue. meta data always nil. livekit-agents==0.10.2

2024-10-28 17:43:35,485 - INFO voice-agent - connecting to room test-room22222, rtc.Room(sid=unknown, name=test-room22222, metadata=, connection_state=1) {"pid": 87126, "job_id": "AJ_DNYdmSAqkeU8"}

chunyeah avatar Oct 28 '24 09:10 chunyeah

As a workaround I tried to set metadata to participant and it works:

token = (
    api.AccessToken(self.livekit_api_key, self.livekit_api_secret)
    .with_identity(identity)
    .with_name(room_name)
    .with_grants(
        api.VideoGrants(
            room_join=True,
            room=room_name,
            room_record=True,
        )
    )
    .with_metadata(metadata_str)
    .to_jwt()
)

firattamurlc avatar Oct 28 '24 12:10 firattamurlc

This is still happening in Feb of 2024 -- would love to fix this ASAP.

I have a usecase where I won't have a user joining the room

VictorAny avatar Feb 24 '25 22:02 VictorAny

@firattamurlc and then, how do you access and give this metadata to the agent under JobContext?

binarycombinatrixcoder avatar Mar 20 '25 03:03 binarycombinatrixcoder

@firattamurlc How do you read the metadata? Can't access the metadata in the room metadata, the participant metadata or in the jobcontext...

hollandmwx avatar May 27 '25 07:05 hollandmwx

I also ran into this issue with reading room metadata.

In my case, the problem was that the metadata is not immediately available before connecting to the room. The fix was to call: await ctx.connect() before trying to read ctx.room.metadata. After connecting, the metadata is accessible as expected.

Here’s a simplified example of how I handled it:

# Create room with metadata
room = await lkapi.room.create_room(
    api.CreateRoomRequest(
        name=room_name,
        empty_timeout=empty_timeout,
        max_participants=max_participants,
        metadata=metadata_json,  # 📌 关键:存储 metadata
    )
)

# Agent entrypoint
async def entrypoint(ctx: JobContext):
    try:
        await ctx.connect()
        if not ctx.room.metadata:
            raise ValueError("房间 metadata 为空,无法加载配置")

        # Use ctx.room.metadata here
        config = json.loads(ctx.room.metadata)
        ...
    except Exception as e:
        logger.error(f"加载房间配置失败: {e}")

After adding await ctx.connect(), everything worked correctly.

xiesiyang avatar Oct 31 '25 08:10 xiesiyang

thanks for sharing the solution @xiesiyang !

davidzhao avatar Nov 04 '25 06:11 davidzhao