T
Telephony SOPKnowledge Base
Search
← All topics

LiveWire — LiveKit-Compatible API on SignalWire

runnable

LiveWire is a LiveKit-compatible API surface from SignalWire — drop-in for code written against `livekit.agents`. Covers AgentServer/Agent/AgentSession, the rtc_session decorator, function_tool, JobContext.connect, no-op interrupt semantics, and the lift-and-shift migration from LiveKit code.

signalwirelivewirelivekitcompatibilityagents-sdk
Agent trigger phrases: LiveWire SignalWire · LiveKit compatibility SignalWire · livekit.agents drop in · function_tool SignalWire · rtc_session entrypoint · migrate LiveKit to SignalWire

LiveWire — LiveKit-Compatible API

LiveWire is a thin compatibility layer that exposes a LiveKit-compatible API surface on top of SignalWire infrastructure. Code written against livekit.agents lifts and shifts with a single import change. Under the hood it generates a SignalWire AI agent and runs the standard SWML/SWAIG stack.

The point: if you have existing LiveKit agent code, you can run it on SignalWire without rewriting the agent logic.

Drop-in import swap

# Before (LiveKit)
from livekit.agents import Agent, AgentSession, function_tool

# After (LiveWire on SignalWire)
from signalwire.livewire import (
    Agent, AgentSession, AgentServer, JobContext,
    function_tool, run_app,
)

Existing Agent, AgentSession, and @function_tool code remains intact.

Hello-world LiveWire agent

from signalwire.livewire import (
    Agent, AgentSession, AgentServer, JobContext, run_app,
)

server = AgentServer()

@server.rtc_session()
async def entrypoint(ctx: JobContext):
    await ctx.connect()
    agent = Agent(instructions="You are a helpful assistant.")
    session = AgentSession()
    await session.start(agent, room=ctx.room)
    session.say("Welcome! How can I help you today?")

run_app(server)

server.rtc_session() is the LiveKit-compatible decorator that registers your entrypoint. run_app(server) boots the uvicorn server.

Tools via @function_tool

from signalwire.livewire import (
    Agent, AgentSession, AgentServer, JobContext, function_tool, run_app,
)

@function_tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"Sunny in {city}"

server = AgentServer()

@server.rtc_session()
async def entrypoint(ctx: JobContext):
    await ctx.connect()
    agent = Agent(instructions="You help with weather.", tools=[get_weather])
    session = AgentSession()
    await session.start(agent, room=ctx.room)

run_app(server)

@function_tool returns LiveKit-compatible tool metadata; LiveWire registers it under the hood as a SWAIG function.

What LiveWire maps to under the hood

| LiveKit concept | SignalWire equivalent | |---|---| | Agent.instructions | SWML ai.prompt.text | | Agent(tools=[...]) | SWAIG function definitions | | AgentSession.start() | SWML document generation + serve | | session.say(text) | Initial greeting or ai_message | | session.generate_reply(instructions=...) | Queue text via ai_message | | session.interrupt() | No-op — SignalWire handles barge-in automatically | | ctx.connect() | Bind the call to the session |

session.interrupt() is a no-op for API compatibility. SignalWire's control plane manages barge-in based on the agent's allow_interruptions setting. You don't need to call it explicitly.

Lifecycle hooks

Subclass Agent to add on_enter and on_exit hooks.

class GreeterAgent(Agent):
    async def on_enter(self):
        if self.session:
            self.session.say("Welcome! I just started up.")

    async def on_exit(self):
        # Cleanup, save state, notify CRM, etc.
        pass

@server.rtc_session()
async def entrypoint(ctx: JobContext):
    await ctx.connect()
    agent = GreeterAgent(instructions="You are a friendly greeter.")
    session = AgentSession()
    await session.start(agent, room=ctx.room)

say semantics

  • Text queued before session.start() is included as the initial greeting in the generated SWML document.
  • Text queued after session.start() is spoken as soon as the agent is ready.
session.say("Welcome!")             # initial greeting if before start
await session.start(agent, room=ctx.room)
session.say("Did you call about your order?")   # spoken when AI is ready

generate_reply

session.generate_reply(
    instructions="Please introduce yourself to the caller and ask how you can help."
)

On SignalWire the prompt handles generation automatically; instructions (when provided) is queued as additional text.

When LiveWire is the right call

| Situation | Use | |---|---| | Existing LiveKit agent codebase | LiveWire — minimal migration. | | New build, want full SignalWire feature surface | Native AgentBase — direct SWML, full SWAIG control. | | Need cross-platform agent that targets both | LiveWire — same code runs on both stacks. |

LiveWire trades feature breadth for compatibility. If you don't have existing LiveKit code, native AgentBase is the better path.

Anti-patterns

  • Mixing LiveWire and native AgentBase in the same module — pick one.
  • Calling session.interrupt() and expecting it to do something — it's a no-op.
  • Building a multi-context flow in LiveWire — use ContextBuilder on a native agent instead.
  • Skipping await ctx.connect() — the session never binds to the call.

See also