-
Notifications
You must be signed in to change notification settings - Fork 3.7k
feat(telemetry): trace dispatch, startup, and shutdown #7131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
ce56e76
feat(telemetry): trace dispatch, startup, and shutdown
davidzhao e88d177
telemetry: tag the job shutdown reason as PII
davidzhao a7ca05d
telemetry: keep the job shutdown reason untagged
davidzhao 460228b
telemetry: copy SIP attributes as lk.sip.*, tag only the end user's n…
davidzhao 07fd9a5
telemetry: tolerate stand-in sessions when RoomIO emits session events
davidzhao a5c3d4f
telemetry: parent room_connect and wait_for_participant to agent_session
davidzhao 52d21f4
tests: drop a stale allowlist comment left by the eou_wait rename
davidzhao 404af68
telemetry: replay the dispatch wait as a job_dispatch span under agen…
davidzhao 8358db1
avoid blocking during shutdown
davidzhao c088ba6
telemetry(dispatch): carry the stage timestamps as doubles and report…
davidzhao f1c22bf
telemetry: keep startup spans out of the ambient context, preload the…
davidzhao c453056
ipc: preload the openai SDK resources tree at process warm-up
davidzhao aa13d48
telemetry: parent job_shutdown to the session, pin user_turn to the r…
davidzhao 4bf0a47
telemetry: the job trace is the unit; drop the session-keyed workarounds
davidzhao 926e020
telemetry: prepare the trace pipeline at job start, hold early spans,…
davidzhao 0cd0ac7
ipc: warm the httpx SSL context at process start
davidzhao 05009c7
ipc: construct the local end-of-turn model once at process warm-up
davidzhao 3bb752c
telemetry: flush held spans off the event loop; restore the caller's …
davidzhao 0b570ed
ipc: one warm-up module for the forkserver and for spawned job processes
davidzhao 1a9f218
ipc: end the job span and run cleanup even when shutdown raises; stam…
davidzhao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| """Side-effect module: the framework's own one-time warm-up, run once per process image. | ||
|
|
||
| Each step is lazy one-time work that would otherwise happen inside the first job, on the | ||
| event loop, and show up as a 100-500 ms ``event_loop_blocked`` at session start that no user | ||
| code caused. Everything here is fork-safe (imports, a ``dlopen``, model weights, a cached | ||
| SSL context): no threads and no event loop are created. | ||
|
|
||
| Where it runs decides how often it costs: | ||
|
|
||
| - with the ``forkserver`` start method (Linux) the worker lists this module in | ||
| ``set_forkserver_preload``, ahead of ``_preload_freeze``, so it runs once in the forkserver | ||
| and every job process inherits the result copy-on-write; | ||
| - with ``spawn`` (macOS, Windows) each job process imports it while it warms up, before any | ||
| job is assigned. | ||
|
|
||
| The job process always imports it: under a forkserver the module is already in | ||
| ``sys.modules`` and the import is a no-op, so there is no start-method check anywhere. | ||
|
|
||
| Failures are logged at debug level only: the first real use reports a proper error. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import time | ||
| from collections.abc import Callable | ||
| from typing import Any | ||
|
|
||
| from ..log import logger | ||
|
|
||
|
|
||
| def _step(name: str, fnc: Callable[[], Any]) -> None: | ||
| started = time.perf_counter() | ||
| try: | ||
| fnc() | ||
| except Exception: | ||
| logger.debug("could not preload %s", name, exc_info=True) | ||
| return | ||
| logger.debug("preloaded %s", name, extra={"elapsed": round(time.perf_counter() - started, 3)}) | ||
|
|
||
|
|
||
| def _av() -> None: | ||
| import av # noqa: F401 | ||
|
|
||
|
|
||
| def _local_inference_models() -> None: | ||
| # the VAD and the turn detector's local end-of-turn model: constructing them later in a | ||
| # job is free once these singletons exist (~25 ms of GIL-held CPU otherwise) | ||
| import livekit.local_inference as li | ||
|
|
||
| li.init_vad() | ||
| li.init_eot() | ||
|
|
||
|
|
||
| def _rtc_native_library() -> None: | ||
| # the dlopen (~150-350 ms). The runtime itself (FfiClient.instance) starts threads, so it | ||
| # stays per process; with the library already mapped it takes a few milliseconds | ||
| from livekit.rtc._ffi_client import get_ffi_lib | ||
|
|
||
| get_ffi_lib() | ||
|
|
||
|
|
||
| def _openai_resources() -> None: | ||
| # the openai SDK, which livekit.agents.inference is built on, imports its whole resources | ||
| # tree on the first client attribute access (~300-550 ms) | ||
| import openai.resources # noqa: F401 | ||
|
|
||
|
|
||
| def _httpx_client() -> None: | ||
| # the first AsyncClient in a process image pays ~40 ms (the SSL context from the CA bundle | ||
| # among other lazy setup); later ones take a few milliseconds. The inference LLM, STT and | ||
| # TTS each build one | ||
| import httpx | ||
|
|
||
| httpx.AsyncClient() | ||
|
|
||
|
|
||
| _step("av", _av) | ||
| _step("the local inference models", _local_inference_models) | ||
| _step("the livekit-rtc native library", _rtc_native_library) | ||
| _step("the openai SDK resources", _openai_resources) | ||
| _step("the httpx client", _httpx_client) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we move this into the try block so a cancellation doesn't skip clean up?