A Go SDK for building LiveKit agents that are fully compliant with the LiveKit agent protocol and lifecycle. This SDK enables you to create autonomous agents that can join LiveKit rooms, process media streams, and interact with participants.
- ✅ Full implementation of LiveKit Agent Protocol v1
- ✅ Automatic reconnection and error handling
- ✅ Support for all job types (Room, Publisher, Participant)
- ✅ Load balancing and worker management
- ✅ WebSocket connection with JSON/Protobuf support
- ✅ Graceful shutdown and job cleanup
- ✅ Helper utilities for common agent tasks
go get github.com/am-sokolov/livekit-agent-sdk-gopackage main
import (
"context"
"log"
"github.com/am-sokolov/livekit-agent-sdk-go/pkg/agent"
"github.com/livekit/protocol/livekit"
lksdk "github.com/livekit/server-sdk-go/v2"
)
func main() {
// Create a handler using the simple handler helper
handler := &agent.SimpleUniversalHandler{
JobRequestFunc: func(ctx context.Context, job *livekit.Job) (bool, *agent.JobMetadata) {
// Accept all jobs
return true, &agent.JobMetadata{
ParticipantIdentity: "my-agent",
ParticipantName: "My Agent",
}
},
JobAssignedFunc: func(ctx context.Context, jobCtx *agent.JobContext) error {
log.Printf("Agent joined room: %s", jobCtx.Room.Name())
// Your agent logic here
<-ctx.Done()
return nil
},
JobTerminatedFunc: func(ctx context.Context, jobID string) {
log.Printf("Job terminated: %s", jobID)
},
}
// Create worker options
opts := agent.WorkerOptions{
AgentName: "my-agent",
JobType: livekit.JobType_JT_ROOM,
MaxJobs: 5,
}
// Create and start the universal worker
worker := agent.NewUniversalWorker("ws://localhost:7880", "devkey", "secret", handler, opts)
ctx := context.Background()
if err := worker.Start(ctx); err != nil {
log.Fatal(err)
}
// Run until interrupted
select {}
}The SDK supports three types of agents:
Launched when a room is created with agent dispatch configuration. Has access to all room events and participants.
opts := agent.WorkerOptions{
JobType: livekit.JobType_JT_ROOM,
MaxJobs: 5, // Handle up to 5 concurrent rooms
}Launched for media publishing jobs. Can generate and publish audio/video content to rooms.
opts := agent.WorkerOptions{
JobType: livekit.JobType_JT_PUBLISHER,
MaxJobs: 10, // Handle multiple concurrent publishing jobs
}Launched for individual participant monitoring and interaction. Provides personalized services per participant.
opts := agent.WorkerOptions{
JobType: livekit.JobType_JT_PARTICIPANT,
MaxJobs: 20, // Handle many participants concurrently
}Implement the UniversalHandler interface for full control:
type MyAgent struct {
agent.BaseHandler
}
func (a *MyAgent) OnJobRequest(ctx context.Context, job *livekit.Job) (bool, *agent.JobMetadata) {
// Decide whether to accept the job
return true, &agent.JobMetadata{
ParticipantIdentity: "my-agent-" + job.Id,
ParticipantName: "My Agent",
ParticipantMetadata: `{"version": "1.0"}`,
}
}
func (a *MyAgent) OnJobAssigned(ctx context.Context, jobCtx *agent.JobContext) error {
// Access the room and job details
log.Printf("Handling job %s in room %s", jobCtx.Job.Id, jobCtx.Room.Name())
// Handle the job
return nil
}
func (a *MyAgent) OnJobTerminated(ctx context.Context, jobID string) {
// Cleanup
}
// Optional: Handle participant events
func (a *MyAgent) OnParticipantJoined(ctx context.Context, participant *lksdk.RemoteParticipant) {
log.Printf("Participant joined: %s", participant.Identity())
}opts := agent.WorkerOptions{
AgentName: "my-agent", // Agent identifier (must match dispatch config)
Namespace: "production", // For multi-tenant isolation
JobType: livekit.JobType_JT_ROOM, // Single job type per worker
Permissions: &livekit.ParticipantPermission{
CanPublish: true,
CanSubscribe: true,
},
MaxJobs: 10, // Maximum concurrent jobs
Logger: customLogger, // Custom logger
}Since room callbacks cannot be modified after connection, use polling patterns for monitoring:
func monitorRoom(ctx context.Context, room *lksdk.Room) {
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
knownParticipants := make(map[string]bool)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// Check for new participants
for _, p := range room.GetRemoteParticipants() {
if !knownParticipants[p.Identity()] {
knownParticipants[p.Identity()] = true
// Handle new participant
log.Printf("New participant: %s", p.Identity())
}
}
}
}
}For multi-worker deployments:
loadBalancer := agent.NewLoadBalancer()
// Update worker status
loadBalancer.UpdateWorker("worker1", 0.5, 5, 10)
// Get least loaded worker
worker := loadBalancer.GetLeastLoadedWorker()Agents only receive jobs when rooms are created with agent dispatch configuration. Simply starting an agent and joining a room won't trigger job dispatch.
Note: LiveKit server in dev mode fully supports agent dispatch. Make sure to include the Agents field when creating rooms.
// Create a room with agent dispatch enabled
room, err := roomClient.CreateRoom(context.Background(), &livekit.CreateRoomRequest{
Name: "my-room",
Agents: []*livekit.RoomAgentDispatch{
{
AgentName: "my-agent", // Must match worker's AgentName
Metadata: "optional metadata",
},
},
})-
Start the LiveKit server in dev mode:
docker run --rm -p 7880:7880 livekit/livekit-server --dev
-
Run your agent:
export LIVEKIT_URL="ws://localhost:7880" export LIVEKIT_API_KEY="devkey" export LIVEKIT_API_SECRET="secret" go run main.go
-
Create a room with agent dispatch:
room, err := roomClient.CreateRoom(context.Background(), &livekit.CreateRoomRequest{ Name: "test-room", Agents: []*livekit.RoomAgentDispatch{{ AgentName: "my-agent", // Must match your agent's name }}, })
-
The agent will automatically receive and process the job.
The repository includes several complete, working examples that demonstrate different agent types and use cases:
A basic agent that monitors room activity and collects analytics.
cd examples/simple-room-agent
export LIVEKIT_URL="ws://localhost:7880"
export LIVEKIT_API_KEY="devkey"
export LIVEKIT_API_SECRET="secret"
go run .Features:
- Room event monitoring with polling pattern
- Participant tracking and statistics
- Data message publishing
- Graceful shutdown handling
Demonstrates publishing audio and video content to LiveKit rooms.
cd examples/media-publisher-agent
export LIVEKIT_URL="ws://localhost:7880"
export LIVEKIT_API_KEY="devkey"
export LIVEKIT_API_SECRET="secret"
go run .Features:
- Audio tone generation (sine waves)
- Video pattern generation (color bars, etc.)
- Multiple publishing modes (audio/video/both)
- Interactive control via data messages
Advanced HLS recording agent with GStreamer-based media pipeline.
cd examples/publisher-hls-agent
export LIVEKIT_URL="ws://localhost:7880"
export LIVEKIT_API_KEY="devkey"
export LIVEKIT_API_SECRET="secret"
go run .Features:
- HLS playlist generation and recording
- Multi-codec support (H.264, Opus, VP8)
- S3-compatible storage integration
- Real-time segment upload
Demonstrates the universal worker pattern with dynamic job handling.
cd examples/universal-worker-demo
export LIVEKIT_URL="ws://localhost:7880"
export LIVEKIT_API_KEY="devkey"
export LIVEKIT_API_SECRET="secret"
go run .Example configuration for deploying agents to LiveKit Cloud.
cd examples/livekit-cloud-example
go run .Minimal example for testing and debugging agent behavior.
cd examples/minimal-repro
go run .Each example can be run independently with proper LiveKit server configuration:
# Start LiveKit server
docker run --rm -p 7880:7880 livekit/livekit-server --dev
# Run any example
cd examples/<example-name>
export LIVEKIT_URL="ws://localhost:7880"
export LIVEKIT_API_KEY="devkey"
export LIVEKIT_API_SECRET="secret"
go run .A comprehensive test script is provided to verify all examples build correctly:
cd examples
./test-all-examples.shThis will build each example to ensure they compile successfully.
The SDK implements the complete LiveKit Agent Protocol:
- WebSocket Connection: Establishes secure connection to LiveKit server
- Worker Registration: Registers capabilities and receives unique worker ID
- Job Discovery: Receives job availability requests based on agent type
- Job Assignment: Accepts jobs and receives authentication tokens
- Room Participation: Joins rooms as a special participant
- Status Updates: Reports job progress and worker load
- Graceful Termination: Handles job cleanup and disconnection
This SDK implements the latest LiveKit Agent Protocol with some important differences from older documentation:
-
JobHandler Interface: Uses three separate methods instead of a single callback:
OnJobRequest()- Decide whether to accept a jobOnJobAssigned()- Handle the assigned jobOnJobTerminated()- Clean up when job ends
-
Worker Options:
JobType(singular) instead ofJobTypesarrayMaxJobsinstead ofMaxConcurrentJobs
-
Room Monitoring: Callbacks cannot be set after room connection. Use polling patterns instead.
-
Track Access: Use
participant.TrackPublications()instead ofparticipant.Tracks()
This SDK is fully compliant with:
- LiveKit Agent Protocol v1
- WebSocket communication (JSON/Protobuf)
- Worker lifecycle management
- Job state machine
- Load-based routing
- Graceful shutdown
The SDK provides robust error handling:
- Automatic reconnection on connection loss
- Job failure reporting
- Timeout management
- Graceful degradation
For detailed developer documentation, see the docs directory:
- Getting Started Guide
- Concepts and Architecture
- Job Handling
- API Reference
- Advanced Features
- Media Processing
- Migration Guide
- Troubleshooting
Contributions are welcome! Please ensure:
- Code follows Go conventions
- Tests pass
- Documentation is updated
Author: Alexey Sokolov Apache License 2.0. See LICENSE