whats is egress and ingress?
# ask-ai
r
whats is egress and ingress?
t
Thanks for reaching out, Henryk. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — you're asking what "ingress" and "egress" mean. Here's a simple explanation in the LiveKit context. • Ingress — importing media into a LiveKit room from an external source. Examples: RTMP/RTMPS, WHIP, SRT, HLS/MP4 or other HTTP streams. Ingress streams appear as a participant (kind: INGRESS) and can be transcoded so WebRTC clients can receive them. • Egress — exporting or recording media from a LiveKit room to an external destination. Examples: recording to MP4 or HLS, streaming out to YouTube/Twitch via RTMP, or exporting a participant’s track. Egress runs as a separate service and egress outputs appear as an egress participant (kind: EGRESS). Would you like examples or steps for setting up ingress (adding an RTMP stream) or egress (recording/streaming out)? Sources: Stream import | LiveKit docs | Rooms, participants, and tracks | LiveKit docs | Recording and export | LiveKit docs | Universal Egress
r
Ok, im asking because im looking at what is the best way to get a transcript from a call. I have an agent that is hosted in the livekit cloud.
t
Thanks — you're asking the best way to get a transcript from a call where your agent is hosted in LiveKit Cloud. I’ll respond to that. What you can do (recommended) • Use an STT-enabled LiveKit Agent in the room. Agents can run speech-to-text with supported providers (Deepgram, AssemblyAI, ElevenLabs Scribe, Cartesia Ink, or STT plugins) and emit real-time transcripts via the agent session. • Listen for the
user_input_transcribed
event in the agent session to receive final transcripts, or read the
lk.transcription
text stream on the frontend to display live transcriptions. Why this is best • Real-time transcripts during the call. • Transcripts are delivered as events/text streams (no need to record + post-process). • Easy to log, save to DB, or forward to downstream services. Alternatives (less ideal) • Egress the audio (record MP4/HLS) and run an external STT on the file. This works for offline transcripts but is not real-time and adds complexity. Quick questions so I can give exact setup steps 1. Do you want real-time live transcripts or post-call transcripts? 2. Which language(s) do you need transcribed? 3. Are you using Node.js or Python for your agent code? 4. Do you have a preferred STT provider (Deepgram, AssemblyAI, ElevenLabs, Cartesia, or another)? Tell me those and I’ll give concise setup steps or example code. Sources: Transcriber | LiveKit docs | Deepgram STT | LiveKit docs | AssemblyAI STT | LiveKit docs | ElevenLabs STT | LiveKit docs | Cartesia STT | LiveKit docs
r
post call transcripts, is this possible for real-time speech-to-speech mdoels?
t
Thanks — you’re asking whether you can get post-call transcripts when using real-time speech-to-speech models. Quick answer: yes, but I need one clarification to give exact steps. Could you confirm which of these you mean? • You want to record the call and produce a transcript after the call ends (post-call) — using audio captured during a real-time speech-to-speech session. • You want the speech-to-speech model itself to output a final transcript after its realtime conversion (i.e., the model produces both audio response and a final text transcript). Also please tell me: 1. Which STT/STS provider or model are you using (Deepgram, AssemblyAI, ElevenLabs Scribe, Cartesia, other)? 2. Do you want a single combined transcript for the whole call or per-speaker/per-segment transcripts? 3. Are you using Node.js or Python for your agent implementation? With those details I’ll give concise setup steps (recording + egress vs agent STT events) and example code.
r
I was thinking of using the google or xAI speech to speech models, i guess record the call and do a post call transcript
t
Thanks — you want to record the call and run a post‑call transcript with Google or xAI speech models. That’s a solid approach. Here’s a concise plan and the steps to implement it with LiveKit. Plan overview • Use LiveKit Egress to record the call (room-level or per-track) to an audio file (MP4/MP3/WAV or HLS). • After the call ends, send the recorded audio file to your chosen speech-to-text (STT) service (Google Speech-to-Text or xAI) to create the transcript. • Optionally split or diarize the audio beforehand if you need per‑speaker transcripts. Steps 1) Start a recording (Egress) • Use Room Composite Egress for a single file of the whole room or Track/Participant Egress if you want separate files per participant. • Configure output format (e.g., MP4/MP3/WAV) and storage destination (S3 or an HTTP-accessible location). • Egress runs independently so it won’t affect real‑time performance. 2) Retrieve the recorded file • After egress completes you’ll get the output location/URL. Download the audio file or stream it to your backend for processing. 3) (Optional) Preprocess the audio • Convert to the sample rate/encoding required by your STT (e.g., 16 kHz WAV or FLAC). • If you need speaker-separated transcripts, either: ◦ Use separate track recordings (recommended), or ◦ Run speaker diarization in the STT or a diarization tool. 4) Send audio to the STT model • Google Speech-to-Text: ◦ Upload audio to Cloud Storage or send bytes directly. ◦ For long files use longRunningRecognize (async) with proper config (encoding, sampleRateHertz, languageCode, diarizationOptions if needed). ◦ Receive transcription when job finishes. • xAI / other models: ◦ Follow the provider’s async/long‑form transcription APIs (upload or point to a URL), then poll/get the result. 5) Post-process & store • Combine segments, apply punctuation/formatting, attach timestamps, and store in DB or file storage. • Optionally feed final transcript back into your app or agent logs. Best practices & tips • Use room composite egress for a single consolidated file; use track egress for per-speaker clarity. • Prefer lossless or uncompressed formats (WAV/FLAC) when possible for best STT accuracy. • Use async transcription APIs for long recordings to avoid timeouts. • If you need speaker labels, record per-track or enable diarization in the STT provider. • Ensure audio sample rate and encoding match STT requirements to avoid lower accuracy. If you want, I can: • Provide a short example egress request (Node or REST) to record the room. • Show an example Google longRunningRecognize request for post-call transcription. • Outline a minimal pipeline (download → convert → upload → transcribe → store). Which of these would you like next (egress example, Google STT example, xAI example, or diarization guidance)?
r
so i use an egress inside a room to record the call, then send the file to a storage, then fro mmy storage transcribe it?
t
Yes — that workflow is correct. Quick confirmation and a few concise notes: 1. Record with Egress in the room • Use Room Composite Egress for one consolidated file, or Track/Participant Egress for per-speaker files. 1. Store the output • Write the egress output to S3 or another HTTP-accessible storage. Egress returns the file URL when complete. 1. Transcribe from storage • Send the stored audio file (or a signed URL) to your chosen STT (Google Speech-to-Text or xAI) using their async/long‑running transcription APIs for long recordings. Downloading first and converting to required format/sample rate is optional but recommended. Important tips • Use WAV/FLAC or lossless formats for best accuracy. • Prefer per-track egress or STT diarization if you need speaker labels. • Use async transcription for long files to avoid timeouts. Would you like a short example for: (A) creating egress (Node/REST), (B) Google longRunningRecognize request, or (C) audio preprocessing commands?
r
This is my current code:
Copy code
import asyncio
import logging

from dotenv import load_dotenv
from livekit import agents, api
from livekit.agents import (
    Agent,
    AgentSession,
    JobContext,
    RunContext,
    function_tool,
    get_job_context,
)
from livekit.plugins import xai

logger = logging.getLogger("xai-telephony-agent")

load_dotenv(".env.local")


async def hangup_call():
    """Delete the room to end the call for all participants."""
    ctx = get_job_context()
    if ctx is None:
        return
    await ctx.api.room.delete_room(
        api.DeleteRoomRequest(room=ctx.room.name)
    )


class Assistant(Agent):
    def __init__(self) -> None:
        super().__init__(
            instructions="""You are playful & mischievous and on a phone call.
                Your responses should be conversational and without any complex formatting or punctuation 
                including emojis, asterisks, or other symbols.
                When the user says goodbye or wants to end the call, use the hang_up tool.
                Keep the responses short (under like 60 words)."""
        )

    @function_tool
    async def hang_up(self, ctx: RunContext):
        """Hang up the phone call. Use when the user says goodbye or wants to end the call."""
        <http://logger.info|logger.info>("Hang up tool called - initiating call termination")
        
        # Give a moment for any pending audio to finish
        await asyncio.sleep(0.5)
        
        # Delete room to end the SIP call
        await hangup_call()
        
        <http://logger.info|logger.info>("Room deleted - call ended")


async def entrypoint(ctx: JobContext):
    await ctx.connect()
    
    <http://logger.info|logger.info>(f"Call started - Room: {ctx.room.name}")

    model = xai.realtime.RealtimeModel(
        voice="Ara",
    )

    session = AgentSession(
        llm=model,
    )

    await session.start(
        agent=Assistant(),
        room=ctx.room,
    )

    # Wait for session to be fully ready before greeting
    await asyncio.sleep(2)

    # Try to greet, but don't fail the call if it times out
    try:
        await session.generate_reply(
            instructions="Answer the phone warmly, like 'Hello! Thanks for calling. How can I help you today?'"
        )
    except Exception as e:
        logger.warning(f"Initial greeting failed: {e} - waiting for user to speak first")


if __name__ == "__main__":
    agents.cli.run_app(
        agents.WorkerOptions(
            entrypoint_fnc=entrypoint,
            agent_name="test_agent",  # Must match dispatch rule!
        )
    )
what do i need to add to it to send a recording to egress?
t
Thanks — you want to start an egress recording from your agent and save the output for post-call transcription. Below is a concise plan and a minimal Python example showing where to start a Room Composite egress, wait for completion, and retrieve the output URL. I’ll assume you want a single consolidated file (Room Composite) stored to an S3-compatible location — tell me if you prefer per-track files or a different storage target. What to add (high-level) • Import the LiveKit egress API client. • Start a RoomComposite egress when the call begins (or at any point). • Listen for egress job completion and read the output URL(s) from the response. • Save that URL (or download the file) for post-call transcription. Minimal example to integrate into your existing agent code • Insert this helper and usage into your file.
Copy code
from livekit import api
import uuid
import asyncio

# Helper to start a Room Composite egress
async def start_room_composite_egress(api_client, room_name, s3_bucket, s3_key_prefix):
    job_id = str(uuid.uuid4())
    request = api.StartEgressRequest(
        room_composite=api.RoomCompositeEgressRequest(
            room=room_name,
            layout=api.RoomCompositeEgressRequest.Layout.TIMELINE,  # or GRID, CUSTOM
            format=api.RoomCompositeEgressRequest.Format.MP4,      # choose MP4/MP3/WAV/HLS
            # output to S3
            output=api.EgressOutput(
                s3=api.S3Output(
                    bucket=s3_bucket,
                    key_prefix=s3_key_prefix,
                )
            ),
        )
    )
    resp = await api_client.egress.start_egress(request)
    return resp

# Helper to poll for egress info (or use webhook/event system if you have one)
async def wait_for_egress_completion(api_client, egress_id, poll_interval=2):
    while True:
        status = await api_client.egress.get_egress(api.GetEgressRequest(egress_id=egress_id))
        if status.state in (api.EgressState.COMPLETED, api.EgressState.FAILED):
            return status
        await asyncio.sleep(poll_interval)
Where to call it in your agent • After you connect and the call starts in
entrypoint
, start egress. Example insertion:
Copy code
# after await ctx.connect() and room available
    s3_bucket = "my-bucket"
    s3_prefix = f"recordings/{ctx.room.name}/{int(time.time())}"

    # start egress
    egress_resp = await start_room_composite_egress(ctx.api, ctx.room.name, s3_bucket, s3_prefix)
    egress_id = egress_resp.egress_id

    # optionally keep track of egress_id to stop later or poll for completion
    # ... later, after call ends, wait for completion and get output URL(s)
    egress_status = await wait_for_egress_completion(ctx.api, egress_id)
    if egress_status.state == api.EgressState.COMPLETED:
        # egress_status.output_info contains URLs or S3 locations
        print("Egress completed:", egress_status.output_info)
    else:
        print("Egress failed:", egress_status.error)
Notes and tips • Choose RoomComposite for a single file; use Track/Participant egress if you want separate per-speaker files. • Configure format to WAV/FLAC for best STT accuracy. • If you use S3, ensure IAM credentials/bucket permissions are configured for LiveKit cloud egress or provide a presigned upload configuration. • You can stop an ongoing egress with
api.egress.stop_egress
using the egress_id if you need to end recording early. • Alternatively, rely on egress webhooks/events (if available in your setup) instead of polling. Next step Do you want a complete ready-to-run snippet that plugs into your current file (including imports, where to place calls, and example S3 config), or do you prefer an example that records separate tracks for speaker diarization?
r
what S3 buckets? supabase? AWS?
t
Good question — both are possible. Quick summary and a clarifying question so I can give exact config/code: Options • AWS S3: Fully supported. Use S3Output in the egress request; provide bucket name and key_prefix. Ensure LiveKit Cloud has permission to write (IAM role or presigned/upload options depending on your plan). • Supabase Storage: Supabase exposes an S3-compatible storage API and can work if you can provide an S3-compatible endpoint and credentials. Alternatively, use Supabase file upload APIs from your backend after downloading the egress file URL. • Other HTTP storage: You can point egress to any HTTP-accessible destination (presigned URLs) or store locally and then upload to a provider of your choice. Clarifying question Which storage do you want to use — AWS S3, Supabase, or another provider? If AWS S3, do you have an S3 bucket and credentials ready (IAM policy or presigned approach)?
r
so how od i set up my files in my project to do this? i need a supabase set up?
t
Short answer: LiveKit Egress cannot write directly to Supabase Storage. Use an S3‑compatible bucket (AWS S3, Cloudflare R2, Backblaze, etc.) for Egress, then copy the file into Supabase if you want it there. If you want, I can give a concise end‑to‑end example. Which do you prefer for the intermediate bucket: AWS S3 or Cloudflare R2? Sources: Egress output types | LiveKit docs | Egress API | LiveKit docs