Files
2026-07-13 13:39:38 +08:00

85 lines
3.1 KiB
Python

from __future__ import annotations
import datetime
import os
import platform
from livekit import api
from ..version import __version__
DEFAULT_INFERENCE_URL = "https://agent-gateway.livekit.cloud/v1"
STAGING_INFERENCE_URL = "https://agent-gateway.staging.livekit.cloud/v1"
HEADER_USER_AGENT = "User-Agent"
HEADER_ROOM_ID = "X-LiveKit-Room-ID"
HEADER_JOB_ID = "X-LiveKit-Job-ID"
HEADER_AGENT_ID = "X-LiveKit-Agent-ID"
HEADER_WORKER_TOKEN = "X-LiveKit-Worker-Token"
HEADER_INFERENCE_PROVIDER = "X-LiveKit-Inference-Provider"
HEADER_INFERENCE_PRIORITY = "X-LiveKit-Inference-Priority"
def get_default_inference_url() -> str:
"""Get the default inference URL based on the environment.
Priority:
1. LIVEKIT_INFERENCE_URL if set
2. If LIVEKIT_URL contains '.staging.livekit.cloud', use staging gateway
3. Otherwise, use production gateway
"""
inference_url = os.environ.get("LIVEKIT_INFERENCE_URL")
if inference_url:
return inference_url
livekit_url = os.environ.get("LIVEKIT_URL", "")
if ".staging.livekit.cloud" in livekit_url:
return STAGING_INFERENCE_URL
return DEFAULT_INFERENCE_URL
def get_inference_headers() -> dict[str, str]:
"""Build identification headers for inference requests.
Always includes User-Agent with SDK version and Python version.
Includes X-LiveKit-Room-ID, X-LiveKit-Job-ID, and X-LiveKit-Agent-ID
when running inside a job context (omitted in console mode or tests).
Includes X-LiveKit-Worker-Token when LIVEKIT_WORKER_TOKEN is set (hosted agents).
"""
headers: dict[str, str] = {
HEADER_USER_AGENT: (f"LiveKit Agents/{__version__} (python {platform.python_version()})"),
}
try:
from ..job import get_job_context
ctx = get_job_context()
if isinstance(room_sid := ctx.job.room.sid, str) and room_sid:
headers[HEADER_ROOM_ID] = room_sid
if isinstance(job_id := ctx.job.id, str) and job_id:
headers[HEADER_JOB_ID] = job_id
# for hosted agents where job context is always present
if worker_token := os.getenv("LIVEKIT_WORKER_TOKEN"):
headers[HEADER_WORKER_TOKEN] = worker_token
# ctx.agent resolves to room.local_participant, which raises until the room
# is connected (STT/TTS may open their websockets before ctx.connect()).
# isconnected() is the codebase-standard readiness guard (see
# utils/participant.py); local_participant is set once on connect and never
# cleared, so the access below won't raise once isconnected() is True.
if ctx.room.isconnected() and isinstance(agent_sid := ctx.agent.sid, str) and agent_sid:
headers[HEADER_AGENT_ID] = agent_sid
except RuntimeError:
pass
return headers
def create_access_token(api_key: str | None, api_secret: str | None, ttl: float = 600) -> str:
grant = api.access_token.InferenceGrants(perform=True)
return (
api.AccessToken(api_key, api_secret)
.with_identity("agent")
.with_inference_grants(grant)
.with_ttl(datetime.timedelta(seconds=ttl))
.to_jwt()
)