85 lines
3.1 KiB
Python
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()
|
|
)
|