lmcache-cli 0.4.5.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. lmcache/__init__.py +84 -0
  2. lmcache/_version.py +24 -0
  3. lmcache/cli/__init__.py +1 -0
  4. lmcache/cli/commands/__init__.py +34 -0
  5. lmcache/cli/commands/base.py +157 -0
  6. lmcache/cli/commands/bench/__init__.py +557 -0
  7. lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
  8. lmcache/cli/commands/bench/engine_bench/config.py +245 -0
  9. lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
  10. lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
  11. lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
  12. lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
  13. lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
  14. lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
  15. lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
  16. lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
  17. lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
  18. lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
  19. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
  20. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
  21. lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
  22. lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
  23. lmcache/cli/commands/describe.py +310 -0
  24. lmcache/cli/commands/kvcache.py +133 -0
  25. lmcache/cli/commands/mock.py +75 -0
  26. lmcache/cli/commands/ping.py +113 -0
  27. lmcache/cli/commands/query/__init__.py +155 -0
  28. lmcache/cli/commands/query/prompt.py +134 -0
  29. lmcache/cli/commands/query/request.py +357 -0
  30. lmcache/cli/commands/server.py +99 -0
  31. lmcache/cli/commands/tool/__init__.py +63 -0
  32. lmcache/cli/commands/tool/cache_simulator.py +113 -0
  33. lmcache/cli/commands/trace/__init__.py +505 -0
  34. lmcache/cli/commands/trace/dispatch.py +249 -0
  35. lmcache/cli/commands/trace/driver.py +372 -0
  36. lmcache/cli/commands/trace/stats.py +289 -0
  37. lmcache/cli/documents/lmcache.txt +11 -0
  38. lmcache/cli/main.py +42 -0
  39. lmcache/cli/metrics/__init__.py +29 -0
  40. lmcache/cli/metrics/formatter.py +171 -0
  41. lmcache/cli/metrics/handler.py +94 -0
  42. lmcache/cli/metrics/metrics.py +161 -0
  43. lmcache/cli/metrics/section.py +77 -0
  44. lmcache/connections.py +173 -0
  45. lmcache/integration/__init__.py +2 -0
  46. lmcache/integration/base_service_factory.py +165 -0
  47. lmcache/integration/request_telemetry/__init__.py +1 -0
  48. lmcache/integration/request_telemetry/base.py +51 -0
  49. lmcache/integration/request_telemetry/factory.py +113 -0
  50. lmcache/integration/request_telemetry/fastapi.py +109 -0
  51. lmcache/integration/request_telemetry/noop.py +35 -0
  52. lmcache/integration/sglang/__init__.py +2 -0
  53. lmcache/integration/sglang/sglang_adapter.py +326 -0
  54. lmcache/integration/sglang/utils.py +39 -0
  55. lmcache/integration/vllm/__init__.py +1 -0
  56. lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
  57. lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
  58. lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
  59. lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
  60. lmcache/integration/vllm/utils.py +433 -0
  61. lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
  62. lmcache/integration/vllm/vllm_service_factory.py +339 -0
  63. lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
  64. lmcache/logging.py +107 -0
  65. lmcache/native_storage_ops.pyi +230 -0
  66. lmcache/non_cuda_equivalents.py +1424 -0
  67. lmcache/observability.py +1958 -0
  68. lmcache/storage_backend/serde/__init__.py +1 -0
  69. lmcache/storage_backend/serde/cachegen_basics.py +210 -0
  70. lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
  71. lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
  72. lmcache/storage_backend/serde/serde.py +75 -0
  73. lmcache/tools/__init__.py +1 -0
  74. lmcache/tools/cache_simulator/README.md +392 -0
  75. lmcache/tools/cache_simulator/__init__.py +1 -0
  76. lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
  77. lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
  78. lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
  79. lmcache/tools/cache_simulator/lru_cache.py +124 -0
  80. lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
  81. lmcache/tools/cache_simulator/simulator.py +795 -0
  82. lmcache/tools/controller_benchmark/README.md +161 -0
  83. lmcache/tools/controller_benchmark/__init__.py +1 -0
  84. lmcache/tools/controller_benchmark/__main__.py +331 -0
  85. lmcache/tools/controller_benchmark/benchmark.py +660 -0
  86. lmcache/tools/controller_benchmark/config.py +44 -0
  87. lmcache/tools/controller_benchmark/constants.py +10 -0
  88. lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
  89. lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
  90. lmcache/tools/controller_benchmark/handlers/base.py +47 -0
  91. lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
  92. lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
  93. lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
  94. lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
  95. lmcache/tools/controller_benchmark/handlers/register.py +56 -0
  96. lmcache/tools/mp_status_viewer/__init__.py +1 -0
  97. lmcache/tools/mp_status_viewer/__main__.py +95 -0
  98. lmcache/usage_context.py +417 -0
  99. lmcache/utils.py +665 -0
  100. lmcache/v1/__init__.py +2 -0
  101. lmcache/v1/api_server/__init__.py +2 -0
  102. lmcache/v1/api_server/__main__.py +537 -0
  103. lmcache/v1/basic_check.py +112 -0
  104. lmcache/v1/cache_controller/__init__.py +9 -0
  105. lmcache/v1/cache_controller/commands/__init__.py +15 -0
  106. lmcache/v1/cache_controller/commands/base.py +35 -0
  107. lmcache/v1/cache_controller/commands/full_sync.py +49 -0
  108. lmcache/v1/cache_controller/config.py +176 -0
  109. lmcache/v1/cache_controller/controller_manager.py +535 -0
  110. lmcache/v1/cache_controller/controllers/__init__.py +11 -0
  111. lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
  112. lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
  113. lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
  114. lmcache/v1/cache_controller/executor.py +463 -0
  115. lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
  116. lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
  117. lmcache/v1/cache_controller/frontend/static/index.html +234 -0
  118. lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
  119. lmcache/v1/cache_controller/full_sync_sender.py +475 -0
  120. lmcache/v1/cache_controller/locks.py +149 -0
  121. lmcache/v1/cache_controller/message.py +828 -0
  122. lmcache/v1/cache_controller/observability.py +208 -0
  123. lmcache/v1/cache_controller/utils.py +679 -0
  124. lmcache/v1/cache_controller/worker.py +665 -0
  125. lmcache/v1/cache_engine.py +2058 -0
  126. lmcache/v1/cache_interface.py +19 -0
  127. lmcache/v1/check/__init__.py +74 -0
  128. lmcache/v1/check/check_mode_gen.py +86 -0
  129. lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
  130. lmcache/v1/check/check_mode_test_remote.py +155 -0
  131. lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
  132. lmcache/v1/check/utils.py +571 -0
  133. lmcache/v1/compute/__init__.py +2 -0
  134. lmcache/v1/compute/attention/__init__.py +0 -0
  135. lmcache/v1/compute/attention/abstract.py +39 -0
  136. lmcache/v1/compute/attention/flash_attn.py +129 -0
  137. lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
  138. lmcache/v1/compute/attention/metadata.py +85 -0
  139. lmcache/v1/compute/attention/utils.py +14 -0
  140. lmcache/v1/compute/blend/__init__.py +7 -0
  141. lmcache/v1/compute/blend/blender.py +168 -0
  142. lmcache/v1/compute/blend/metadata.py +34 -0
  143. lmcache/v1/compute/blend/utils.py +63 -0
  144. lmcache/v1/compute/models/__init__.py +0 -0
  145. lmcache/v1/compute/models/base.py +141 -0
  146. lmcache/v1/compute/models/llama.py +9 -0
  147. lmcache/v1/compute/models/qwen3.py +24 -0
  148. lmcache/v1/compute/models/utils.py +68 -0
  149. lmcache/v1/compute/positional_encoding.py +199 -0
  150. lmcache/v1/config.py +848 -0
  151. lmcache/v1/config_base.py +848 -0
  152. lmcache/v1/distributed/api.py +248 -0
  153. lmcache/v1/distributed/config.py +321 -0
  154. lmcache/v1/distributed/error.py +64 -0
  155. lmcache/v1/distributed/eviction.py +192 -0
  156. lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
  157. lmcache/v1/distributed/eviction_policy/factory.py +27 -0
  158. lmcache/v1/distributed/eviction_policy/lru.py +244 -0
  159. lmcache/v1/distributed/eviction_policy/noop.py +50 -0
  160. lmcache/v1/distributed/internal_api.py +170 -0
  161. lmcache/v1/distributed/l1_manager.py +835 -0
  162. lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
  163. lmcache/v1/distributed/l2_adapters/base.py +360 -0
  164. lmcache/v1/distributed/l2_adapters/config.py +385 -0
  165. lmcache/v1/distributed/l2_adapters/factory.py +205 -0
  166. lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
  167. lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
  168. lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
  169. lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
  170. lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
  171. lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
  172. lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
  173. lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
  174. lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
  175. lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
  176. lmcache/v1/distributed/memory_manager.py +179 -0
  177. lmcache/v1/distributed/storage_controller.py +39 -0
  178. lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
  179. lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
  180. lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
  181. lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
  182. lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
  183. lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
  184. lmcache/v1/distributed/storage_manager.py +532 -0
  185. lmcache/v1/event_manager.py +145 -0
  186. lmcache/v1/exceptions/__init__.py +16 -0
  187. lmcache/v1/gpu_connector/__init__.py +126 -0
  188. lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
  189. lmcache/v1/gpu_connector/gpu_ops.py +85 -0
  190. lmcache/v1/gpu_connector/hpu_connector.py +326 -0
  191. lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
  192. lmcache/v1/gpu_connector/utils.py +890 -0
  193. lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
  194. lmcache/v1/health_monitor/__init__.py +1 -0
  195. lmcache/v1/health_monitor/base.py +587 -0
  196. lmcache/v1/health_monitor/checks/__init__.py +1 -0
  197. lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
  198. lmcache/v1/health_monitor/constants.py +36 -0
  199. lmcache/v1/internal_api_server/__init__.py +0 -0
  200. lmcache/v1/internal_api_server/api_registry.py +59 -0
  201. lmcache/v1/internal_api_server/api_server.py +120 -0
  202. lmcache/v1/internal_api_server/common/__init__.py +1 -0
  203. lmcache/v1/internal_api_server/common/env_api.py +22 -0
  204. lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
  205. lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
  206. lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
  207. lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
  208. lmcache/v1/internal_api_server/common/thread_api.py +63 -0
  209. lmcache/v1/internal_api_server/controller/__init__.py +1 -0
  210. lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
  211. lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
  212. lmcache/v1/internal_api_server/utils.py +43 -0
  213. lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
  214. lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
  215. lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
  216. lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
  217. lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
  218. lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
  219. lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
  220. lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
  221. lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
  222. lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
  223. lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
  224. lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
  225. lmcache/v1/kv_layer_groups.py +267 -0
  226. lmcache/v1/lazy_memory_allocator.py +284 -0
  227. lmcache/v1/lookup_client/__init__.py +25 -0
  228. lmcache/v1/lookup_client/abstract_client.py +77 -0
  229. lmcache/v1/lookup_client/async_lookup_message.py +50 -0
  230. lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
  231. lmcache/v1/lookup_client/factory.py +251 -0
  232. lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
  233. lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
  234. lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
  235. lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
  236. lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
  237. lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
  238. lmcache/v1/lookup_client/record_strategies/base.py +327 -0
  239. lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
  240. lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
  241. lmcache/v1/manager.py +539 -0
  242. lmcache/v1/memory_management.py +2619 -0
  243. lmcache/v1/metadata.py +114 -0
  244. lmcache/v1/mp_observability/AGENTS.override.md +21 -0
  245. lmcache/v1/mp_observability/README.md +204 -0
  246. lmcache/v1/mp_observability/config.py +340 -0
  247. lmcache/v1/mp_observability/event.py +100 -0
  248. lmcache/v1/mp_observability/event_bus.py +313 -0
  249. lmcache/v1/mp_observability/otel_init.py +145 -0
  250. lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
  251. lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
  252. lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
  253. lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
  254. lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
  255. lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
  256. lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
  257. lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
  258. lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
  259. lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
  260. lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
  261. lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
  262. lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
  263. lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
  264. lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
  265. lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
  266. lmcache/v1/mp_observability/trace/__init__.py +50 -0
  267. lmcache/v1/mp_observability/trace/codecs.py +255 -0
  268. lmcache/v1/mp_observability/trace/decorator.py +147 -0
  269. lmcache/v1/mp_observability/trace/format.py +132 -0
  270. lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
  271. lmcache/v1/mp_observability/trace/reader.py +167 -0
  272. lmcache/v1/mp_observability/trace/recorder.py +300 -0
  273. lmcache/v1/multiprocess/__init__.py +0 -0
  274. lmcache/v1/multiprocess/affinity_pool.py +102 -0
  275. lmcache/v1/multiprocess/blend_server_v2.py +891 -0
  276. lmcache/v1/multiprocess/config.py +253 -0
  277. lmcache/v1/multiprocess/custom_types.py +281 -0
  278. lmcache/v1/multiprocess/futures.py +194 -0
  279. lmcache/v1/multiprocess/gpu_context.py +511 -0
  280. lmcache/v1/multiprocess/http_server.py +235 -0
  281. lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
  282. lmcache/v1/multiprocess/mq.py +732 -0
  283. lmcache/v1/multiprocess/protocol.py +86 -0
  284. lmcache/v1/multiprocess/protocols/README.md +213 -0
  285. lmcache/v1/multiprocess/protocols/__init__.py +127 -0
  286. lmcache/v1/multiprocess/protocols/base.py +89 -0
  287. lmcache/v1/multiprocess/protocols/blend.py +109 -0
  288. lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
  289. lmcache/v1/multiprocess/protocols/controller.py +53 -0
  290. lmcache/v1/multiprocess/protocols/debug.py +34 -0
  291. lmcache/v1/multiprocess/protocols/engine.py +146 -0
  292. lmcache/v1/multiprocess/protocols/observability.py +39 -0
  293. lmcache/v1/multiprocess/server.py +1134 -0
  294. lmcache/v1/multiprocess/session.py +190 -0
  295. lmcache/v1/multiprocess/token_hasher.py +441 -0
  296. lmcache/v1/offload_server/__init__.py +17 -0
  297. lmcache/v1/offload_server/abstract_server.py +37 -0
  298. lmcache/v1/offload_server/message.py +30 -0
  299. lmcache/v1/offload_server/zmq_server.py +122 -0
  300. lmcache/v1/periodic_thread.py +579 -0
  301. lmcache/v1/pin_monitor.py +246 -0
  302. lmcache/v1/plugin/__init__.py +0 -0
  303. lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
  304. lmcache/v1/protocol.py +317 -0
  305. lmcache/v1/rpc/__init__.py +17 -0
  306. lmcache/v1/rpc/transport.py +105 -0
  307. lmcache/v1/rpc/zmq_transport.py +213 -0
  308. lmcache/v1/rpc_utils.py +165 -0
  309. lmcache/v1/server/__init__.py +2 -0
  310. lmcache/v1/server/__main__.py +170 -0
  311. lmcache/v1/server/storage_backend/__init__.py +21 -0
  312. lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
  313. lmcache/v1/server/storage_backend/local_backend.py +75 -0
  314. lmcache/v1/server/utils.py +21 -0
  315. lmcache/v1/standalone/__init__.py +1 -0
  316. lmcache/v1/standalone/__main__.py +583 -0
  317. lmcache/v1/standalone/manager.py +80 -0
  318. lmcache/v1/standalone/standalone_service_factory.py +86 -0
  319. lmcache/v1/storage_backend/__init__.py +313 -0
  320. lmcache/v1/storage_backend/abstract_backend.py +445 -0
  321. lmcache/v1/storage_backend/audit_backend.py +233 -0
  322. lmcache/v1/storage_backend/batched_message_sender.py +222 -0
  323. lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
  324. lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
  325. lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
  326. lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
  327. lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
  328. lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
  329. lmcache/v1/storage_backend/connector/__init__.py +443 -0
  330. lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
  331. lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
  332. lmcache/v1/storage_backend/connector/base_connector.py +379 -0
  333. lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
  334. lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
  335. lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
  336. lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
  337. lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
  338. lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
  339. lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
  340. lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
  341. lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
  342. lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
  343. lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
  344. lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
  345. lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
  346. lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
  347. lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
  348. lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
  349. lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
  350. lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
  351. lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
  352. lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
  353. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
  354. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
  355. lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
  356. lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
  357. lmcache/v1/storage_backend/gds_backend.py +1199 -0
  358. lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
  359. lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
  360. lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
  361. lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
  362. lmcache/v1/storage_backend/local_disk_backend.py +656 -0
  363. lmcache/v1/storage_backend/maru_backend.py +734 -0
  364. lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
  365. lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
  366. lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
  367. lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
  368. lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
  369. lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
  370. lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
  371. lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
  372. lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
  373. lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
  374. lmcache/v1/storage_backend/p2p_backend.py +788 -0
  375. lmcache/v1/storage_backend/path_sharder.py +117 -0
  376. lmcache/v1/storage_backend/pd_backend.py +646 -0
  377. lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
  378. lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
  379. lmcache/v1/storage_backend/remote_backend.py +624 -0
  380. lmcache/v1/storage_backend/resp_client.py +227 -0
  381. lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
  382. lmcache/v1/storage_backend/storage_manager.py +1352 -0
  383. lmcache/v1/system_detection.py +110 -0
  384. lmcache/v1/token_database.py +551 -0
  385. lmcache/v1/transfer_channel/__init__.py +83 -0
  386. lmcache/v1/transfer_channel/abstract.py +285 -0
  387. lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
  388. lmcache/v1/transfer_channel/nixl_channel.py +639 -0
  389. lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
  390. lmcache/v1/transfer_channel/transfer_utils.py +63 -0
  391. lmcache/v1/utils/__init__.py +1 -0
  392. lmcache/v1/utils/bloom_filter.py +109 -0
  393. lmcache/v1/utils/cache_utils.py +125 -0
  394. lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
  395. lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
  396. lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
  397. lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
  398. lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
  399. lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,69 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """StorageManager metrics subscriber — OTel counters for SM events."""
4
+
5
+ # Future
6
+ from __future__ import annotations
7
+
8
+ # Third Party
9
+ from opentelemetry import metrics
10
+
11
+ # First Party
12
+ from lmcache.v1.mp_observability.event import Event, EventType
13
+ from lmcache.v1.mp_observability.event_bus import EventCallback, EventSubscriber
14
+
15
+
16
+ class SMMetricsSubscriber(EventSubscriber):
17
+ """Maintains OTel counters for StorageManager operations.
18
+
19
+ Metric parity with the old ``StorageManagerStatsLogger``:
20
+ - ``lmcache_mp.sm_read_requests`` — SM read (prefetch) requests
21
+ - ``lmcache_mp.sm_read_succeed_keys`` — keys that were cache hits
22
+ - ``lmcache_mp.sm_read_failed_keys`` — keys that were cache misses
23
+ - ``lmcache_mp.sm_write_requests`` — SM write (reserve) requests
24
+ - ``lmcache_mp.sm_write_succeed_keys`` — keys successfully allocated
25
+ - ``lmcache_mp.sm_write_failed_keys`` — keys that failed allocation
26
+ """
27
+
28
+ def __init__(self) -> None:
29
+ meter = metrics.get_meter("lmcache.sm")
30
+ self._read_requests = meter.create_counter(
31
+ "lmcache_mp.sm_read_requests",
32
+ description="Total StorageManager read (prefetch) requests",
33
+ )
34
+ self._read_succeed = meter.create_counter(
35
+ "lmcache_mp.sm_read_succeed_keys",
36
+ description="Total keys that were cache hits in SM read",
37
+ )
38
+ self._read_failed = meter.create_counter(
39
+ "lmcache_mp.sm_read_failed_keys",
40
+ description="Total keys that were cache misses in SM read",
41
+ )
42
+ self._write_requests = meter.create_counter(
43
+ "lmcache_mp.sm_write_requests",
44
+ description="Total StorageManager write (reserve) requests",
45
+ )
46
+ self._write_succeed = meter.create_counter(
47
+ "lmcache_mp.sm_write_succeed_keys",
48
+ description="Total keys successfully allocated for write in SM",
49
+ )
50
+ self._write_failed = meter.create_counter(
51
+ "lmcache_mp.sm_write_failed_keys",
52
+ description="Total keys that failed allocation for write in SM",
53
+ )
54
+
55
+ def get_subscriptions(self) -> dict[EventType, EventCallback]:
56
+ return {
57
+ EventType.SM_READ_PREFETCHED: self._on_read_prefetched,
58
+ EventType.SM_WRITE_RESERVED: self._on_write_reserved,
59
+ }
60
+
61
+ def _on_read_prefetched(self, event: Event) -> None:
62
+ self._read_requests.add(1)
63
+ self._read_succeed.add(len(event.metadata["succeeded_keys"]))
64
+ self._read_failed.add(len(event.metadata["failed_keys"]))
65
+
66
+ def _on_write_reserved(self, event: Event) -> None:
67
+ self._write_requests.add(1)
68
+ self._write_succeed.add(len(event.metadata["succeeded_keys"]))
69
+ self._write_failed.add(len(event.metadata["failed_keys"]))
@@ -0,0 +1,12 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # First Party
4
+ from lmcache.v1.mp_observability.subscribers.tracing.mp_server import (
5
+ MPServerTracingSubscriber,
6
+ )
7
+ from lmcache.v1.mp_observability.subscribers.tracing.span_registry import (
8
+ SpanRegistry,
9
+ get_span_registry,
10
+ )
11
+
12
+ __all__ = ["MPServerTracingSubscriber", "SpanRegistry", "get_span_registry"]
@@ -0,0 +1,333 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """OTel tracing subscriber for MP server operations.
4
+
5
+ Creates a root ``"request"`` span per session wrapping all child spans.
6
+ Opens at ``MP_REQUEST_START``; closes at ``MP_SESSION_END``, deferred until
7
+ any in-flight GPU store/retrieve callbacks complete.
8
+
9
+ Accepts an optional :class:`~lmcache.v1.mp_observability.subscribers.tracing\
10
+ .span_registry.SpanRegistry` so other subscribers can nest spans under the
11
+ root or any child span — see
12
+ ``docs/design/observability/request-event-span.md`` for examples.
13
+ """
14
+
15
+ # Future
16
+ from __future__ import annotations
17
+
18
+ # Standard
19
+ from typing import Any
20
+
21
+ # First Party
22
+ from lmcache.logging import init_logger
23
+ from lmcache.v1.mp_observability.event import Event, EventType
24
+ from lmcache.v1.mp_observability.event_bus import EventCallback, EventSubscriber
25
+ from lmcache.v1.mp_observability.subscribers.tracing.span_registry import SpanRegistry
26
+
27
+ logger = init_logger(__name__)
28
+
29
+ try:
30
+ # Third Party
31
+ from opentelemetry import trace
32
+
33
+ _tracer = trace.get_tracer("lmcache_mp.server")
34
+ _HAS_OTEL = True
35
+ except ImportError:
36
+ _HAS_OTEL = False
37
+
38
+
39
+ class MPServerTracingSubscriber(EventSubscriber):
40
+ """Creates OTel spans from MP server events with a per-request root span.
41
+
42
+ Each session gets one root ``"request"`` span that nests all child spans
43
+ (``mp.lookup_prefetch``, ``mp.retrieve``, ``mp.store``) beneath it.
44
+
45
+ The root span is opened eagerly at ``MP_REQUEST_START`` and closed at
46
+ ``MP_SESSION_END``, with deferral if GPU stores are still in flight.
47
+ """
48
+
49
+ # Maps START event types to span names
50
+ _SPAN_NAMES: dict[EventType, str] = {
51
+ EventType.MP_STORE_START: "mp.store",
52
+ EventType.MP_RETRIEVE_START: "mp.retrieve",
53
+ EventType.MP_LOOKUP_PREFETCH_START: "mp.lookup_prefetch",
54
+ }
55
+
56
+ _END_TO_START: dict[EventType, EventType] = {
57
+ EventType.MP_STORE_END: EventType.MP_STORE_START,
58
+ EventType.MP_RETRIEVE_END: EventType.MP_RETRIEVE_START,
59
+ EventType.MP_LOOKUP_PREFETCH_END: EventType.MP_LOOKUP_PREFETCH_START,
60
+ }
61
+
62
+ # Logical registry names for child spans. When a child span is opened it
63
+ # is also stored under this name in the shared registry so that other
64
+ # subscribers (or future sub-span subscribers) can look up its context as
65
+ # a parent without coupling to this class.
66
+ _LOGICAL_NAMES: dict[EventType, str] = {
67
+ EventType.MP_STORE_START: "store",
68
+ EventType.MP_RETRIEVE_START: "retrieve",
69
+ EventType.MP_LOOKUP_PREFETCH_START: "lookup_prefetch",
70
+ }
71
+
72
+ def __init__(self, registry: SpanRegistry | None = None) -> None:
73
+ # Shared span registry. When a registry is provided, other subscribers
74
+ # that share the same instance can look up the root "request" span or
75
+ # any child span ("store", "retrieve", "lookup_prefetch") as a parent
76
+ # context. If None, a private registry is created so that the class
77
+ # remains usable without a shared registry.
78
+ self._registry = registry if registry is not None else SpanRegistry()
79
+
80
+ # session_id -> (span, start_event_type) for pending child spans
81
+ self._pending: dict[str, tuple[Any, EventType]] = {}
82
+
83
+ # session_id -> number of MP_STORE_SUBMITTED events without a
84
+ # matching MP_STORE_END; guards against SESSION_END racing GPU stores
85
+ self._pending_store_count: dict[str, int] = {}
86
+
87
+ # session_id -> number of MP_RETRIEVE_SUBMITTED events without a
88
+ # matching MP_RETRIEVE_END; guards against SESSION_END racing GPU retrieves
89
+ self._pending_retrieve_count: dict[str, int] = {}
90
+
91
+ # session_id -> SESSION_END timestamp saved when stores/retrieves are in
92
+ # flight; the last MP_STORE_END / MP_RETRIEVE_END uses this to close the
93
+ # root span
94
+ self._deferred_session_end_ts: dict[str, float] = {}
95
+
96
+ def get_subscriptions(self) -> dict[EventType, EventCallback]:
97
+ """Return the event-to-callback mapping for this subscriber."""
98
+ return {
99
+ EventType.MP_REQUEST_START: self._on_request_start,
100
+ EventType.MP_STORE_SUBMITTED: self._on_store_submitted,
101
+ EventType.MP_RETRIEVE_SUBMITTED: self._on_retrieve_submitted,
102
+ EventType.MP_SESSION_END: self._on_session_end,
103
+ EventType.MP_STORE_START: self._on_start,
104
+ EventType.MP_STORE_END: self._on_end,
105
+ EventType.MP_RETRIEVE_START: self._on_start,
106
+ EventType.MP_RETRIEVE_END: self._on_end,
107
+ EventType.MP_LOOKUP_PREFETCH_START: self._on_start,
108
+ EventType.MP_LOOKUP_PREFETCH_END: self._on_end,
109
+ }
110
+
111
+ # ------------------------------------------------------------------
112
+ # Public lifecycle
113
+ # ------------------------------------------------------------------
114
+
115
+ def shutdown(self) -> None:
116
+ """End all leaked spans on bus shutdown."""
117
+ for key, (span, _) in self._pending.items():
118
+ try:
119
+ span.end()
120
+ except Exception:
121
+ pass
122
+ self._pending.clear()
123
+
124
+ sessions = (
125
+ set(self._pending_store_count)
126
+ | set(self._pending_retrieve_count)
127
+ | set(self._deferred_session_end_ts)
128
+ | self._registry.all_session_ids()
129
+ )
130
+ for sid in sessions:
131
+ self._registry.clear_session(sid)
132
+ self._pending_store_count.clear()
133
+ self._pending_retrieve_count.clear()
134
+ self._deferred_session_end_ts.clear()
135
+
136
+ # ------------------------------------------------------------------
137
+ # Root span handlers
138
+ # ------------------------------------------------------------------
139
+
140
+ def _on_request_start(self, event: Event) -> None:
141
+ """Create the root span eagerly at true request arrival.
142
+
143
+ Args:
144
+ event: ``MP_REQUEST_START`` event with ``session_id`` set.
145
+ """
146
+ if not _HAS_OTEL:
147
+ return
148
+ self._get_or_create_request_span(event.session_id, event.timestamp)
149
+
150
+ def _on_store_submitted(self, event: Event) -> None:
151
+ """Increment the in-flight store counter for the session.
152
+
153
+ Called CPU-synchronously before the GPU store is enqueued, ensuring
154
+ the counter is non-zero before ``MP_SESSION_END`` can arrive.
155
+
156
+ Args:
157
+ event: ``MP_STORE_SUBMITTED`` event.
158
+ """
159
+ if not _HAS_OTEL:
160
+ return
161
+ sid = event.session_id
162
+ self._pending_store_count[sid] = self._pending_store_count.get(sid, 0) + 1
163
+
164
+ def _on_retrieve_submitted(self, event: Event) -> None:
165
+ """Increment the in-flight retrieve counter for the session.
166
+
167
+ Called CPU-synchronously before the GPU retrieve is enqueued, ensuring
168
+ the counter is non-zero before ``MP_SESSION_END`` can arrive.
169
+
170
+ Args:
171
+ event: ``MP_RETRIEVE_SUBMITTED`` event.
172
+ """
173
+ if not _HAS_OTEL:
174
+ return
175
+ sid = event.session_id
176
+ self._pending_retrieve_count[sid] = self._pending_retrieve_count.get(sid, 0) + 1
177
+
178
+ def _on_session_end(self, event: Event) -> None:
179
+ """Close the root span, or defer if GPU stores/retrieves are still in flight.
180
+
181
+ Args:
182
+ event: ``MP_SESSION_END`` event carrying the logical end timestamp.
183
+ """
184
+ if not _HAS_OTEL:
185
+ return
186
+ sid = event.session_id
187
+ if (
188
+ self._pending_store_count.get(sid, 0) == 0
189
+ and self._pending_retrieve_count.get(sid, 0) == 0
190
+ ):
191
+ self._close_request_span(sid, event.timestamp)
192
+ else:
193
+ # Stores/retrieves still in flight — save timestamp; the last
194
+ # MP_STORE_END / MP_RETRIEVE_END closes the root span
195
+ self._deferred_session_end_ts[sid] = event.timestamp
196
+
197
+ # ------------------------------------------------------------------
198
+ # Child span handlers
199
+ # ------------------------------------------------------------------
200
+
201
+ def _on_start(self, event: Event) -> None:
202
+ """Create a child span nested under the root span.
203
+
204
+ Falls back to ``_get_or_create_request_span`` if ``MP_REQUEST_START`` was
205
+ never emitted (e.g. store-only path with no lookup).
206
+
207
+ Args:
208
+ event: One of ``MP_STORE_START``, ``MP_RETRIEVE_START``, or
209
+ ``MP_LOOKUP_PREFETCH_START``.
210
+ """
211
+ if not _HAS_OTEL:
212
+ return
213
+ sid = event.session_id
214
+ _, root_ctx = self._get_or_create_request_span(sid, event.timestamp)
215
+
216
+ span_name = self._SPAN_NAMES[event.event_type]
217
+ span = _tracer.start_span(
218
+ span_name,
219
+ context=root_ctx,
220
+ start_time=int(event.timestamp * 1e9),
221
+ )
222
+ for k, v in event.metadata.items():
223
+ span.set_attribute(k, str(v))
224
+ span.set_attribute("session_id", sid)
225
+
226
+ key = f"{sid}:{event.event_type.value}"
227
+ self._pending[key] = (span, event.event_type)
228
+
229
+ # Also register in the shared registry so other subscribers can use
230
+ # this span as a parent context for nested sub-spans.
231
+ logical = self._LOGICAL_NAMES.get(event.event_type)
232
+ if logical:
233
+ self._registry.open(sid, logical, span, trace.set_span_in_context(span))
234
+
235
+ def _on_end(self, event: Event) -> None:
236
+ """Close a pending child span and handle store-count deferral.
237
+
238
+ On ``MP_STORE_END``: decrements the store counter; if it reaches zero
239
+ and a deferred session-end timestamp exists, closes the root span.
240
+
241
+ Args:
242
+ event: One of ``MP_STORE_END``, ``MP_RETRIEVE_END``, or
243
+ ``MP_LOOKUP_PREFETCH_END``.
244
+ """
245
+ if not _HAS_OTEL:
246
+ return
247
+ sid = event.session_id
248
+ start_type = self._END_TO_START[event.event_type]
249
+ key = f"{sid}:{start_type.value}"
250
+ entry = self._pending.pop(key, None)
251
+ if entry is None:
252
+ logger.debug(
253
+ "No pending span for %s session=%s",
254
+ event.event_type.value,
255
+ sid,
256
+ )
257
+ else:
258
+ span, _ = entry
259
+ for k, v in event.metadata.items():
260
+ span.set_attribute(k, str(v))
261
+ span.end(end_time=int(event.timestamp * 1e9))
262
+
263
+ # Remove the child span from the shared registry now that it is closed.
264
+ logical = self._LOGICAL_NAMES.get(start_type)
265
+ if logical:
266
+ self._registry.pop(sid, logical)
267
+
268
+ if event.event_type == EventType.MP_STORE_END:
269
+ if (count := self._pending_store_count.get(sid, 0)) > 0:
270
+ self._pending_store_count[sid] = count - 1
271
+ elif event.event_type == EventType.MP_RETRIEVE_END:
272
+ if (count := self._pending_retrieve_count.get(sid, 0)) > 0:
273
+ self._pending_retrieve_count[sid] = count - 1
274
+
275
+ if (
276
+ sid in self._deferred_session_end_ts
277
+ and self._pending_store_count.get(sid, 0) == 0
278
+ and self._pending_retrieve_count.get(sid, 0) == 0
279
+ ):
280
+ deferred_ts = self._deferred_session_end_ts.pop(sid)
281
+ self._close_request_span(sid, deferred_ts)
282
+
283
+ # ------------------------------------------------------------------
284
+ # Private helpers
285
+ # ------------------------------------------------------------------
286
+
287
+ def _get_or_create_request_span(
288
+ self, session_id: str, ts: float
289
+ ) -> tuple[Any, Any]:
290
+ """Return the root span and its OTel context, creating them if absent.
291
+
292
+ The span is stored in the registry under ``span_name="request"`` so
293
+ that other subscribers sharing the same :class:`SpanRegistry` can
294
+ retrieve the parent context via
295
+ ``registry.get_context(session_id, "request")``.
296
+
297
+ Args:
298
+ session_id: The request session identifier.
299
+ ts: Wall-clock timestamp (``time.time()``) to use as span start
300
+ if the root is created now.
301
+
302
+ Returns:
303
+ ``(root_span, root_otel_context)`` tuple.
304
+ """
305
+ entry = self._registry.get(session_id, "request")
306
+ if entry is not None:
307
+ return entry
308
+ root_span = _tracer.start_span(
309
+ "request",
310
+ start_time=int(ts * 1e9),
311
+ )
312
+ root_span.set_attribute("session_id", session_id)
313
+ root_ctx = trace.set_span_in_context(root_span)
314
+ self._registry.open(session_id, "request", root_span, root_ctx)
315
+ return root_span, root_ctx
316
+
317
+ def _close_request_span(self, session_id: str, end_ts: float) -> None:
318
+ """End the root span and clean up all per-session state.
319
+
320
+ Args:
321
+ session_id: The request session identifier.
322
+ end_ts: Wall-clock timestamp to stamp as the span end time.
323
+ """
324
+ entry = self._registry.pop(session_id, "request")
325
+ if entry is not None:
326
+ root_span, _ = entry
327
+ try:
328
+ root_span.end(end_time=int(end_ts * 1e9))
329
+ except Exception:
330
+ pass
331
+ self._pending_store_count.pop(session_id, None)
332
+ self._pending_retrieve_count.pop(session_id, None)
333
+ self._deferred_session_end_ts.pop(session_id, None)
@@ -0,0 +1,148 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """Shared OTel span registry for cross-subscriber context propagation.
4
+
5
+ :class:`SpanRegistry` stores active spans keyed by ``(session_id, span_name)``
6
+ so that multiple subscribers sharing the same instance can look up parent
7
+ span contexts without tight coupling.
8
+
9
+ All reads and writes happen on the single EventBus drain thread — no locking
10
+ is needed.
11
+ """
12
+
13
+ # Future
14
+ from __future__ import annotations
15
+
16
+ # Standard
17
+ from typing import Any
18
+
19
+ # First Party
20
+ from lmcache.logging import init_logger
21
+
22
+ logger = init_logger(__name__)
23
+
24
+
25
+ class SpanRegistry:
26
+ """Registry of active OTel spans, keyed by ``(session_id, span_name)``.
27
+
28
+ ``span_name`` is a logical identifier (e.g. ``"request"``, ``"retrieve"``,
29
+ ``"store"``) that decouples parent-lookup from the concrete OTel span name.
30
+
31
+ All methods must be called from the EventBus drain thread; no locking is
32
+ performed.
33
+
34
+ Example usage across two subscribers that share one registry::
35
+
36
+ registry = SpanRegistry()
37
+ bus.register_subscriber(MPServerTracingSubscriber(registry))
38
+ bus.register_subscriber(MyL1TracingSubscriber(registry))
39
+
40
+ # Inside MyL1TracingSubscriber._on_l1_start:
41
+ parent_ctx = registry.get_context(session_id, "retrieve") \\
42
+ or registry.get_context(session_id, "request")
43
+ span = tracer.start_span("l1.read", context=parent_ctx, ...)
44
+ """
45
+
46
+ def __init__(self) -> None:
47
+ # (session_id, span_name) -> (span, otel_context)
48
+ self._active: dict[tuple[str, str], tuple[Any, Any]] = {}
49
+
50
+ def open(self, session_id: str, span_name: str, span: Any, ctx: Any) -> None:
51
+ """Register an open span and its OTel context.
52
+
53
+ Args:
54
+ session_id: The request session identifier.
55
+ span_name: Logical name for the span (e.g. ``"request"``,
56
+ ``"retrieve"``).
57
+ span: The OTel span object.
58
+ ctx: The OTel context containing the span (from
59
+ ``trace.set_span_in_context(span)``).
60
+ """
61
+ self._active[(session_id, span_name)] = (span, ctx)
62
+
63
+ def get(self, session_id: str, span_name: str) -> tuple[Any, Any] | None:
64
+ """Return ``(span, context)`` for an open span, or ``None``.
65
+
66
+ Args:
67
+ session_id: The request session identifier.
68
+ span_name: Logical span name to look up.
69
+
70
+ Returns:
71
+ ``(span, otel_context)`` if the span is registered, else ``None``.
72
+ """
73
+ return self._active.get((session_id, span_name))
74
+
75
+ def get_context(self, session_id: str, span_name: str) -> Any | None:
76
+ """Return the OTel context for an open span, or ``None``.
77
+
78
+ Convenience wrapper over :meth:`get` for callers that only need the
79
+ context to pass as a ``context=`` argument when starting a child span.
80
+
81
+ Args:
82
+ session_id: The request session identifier.
83
+ span_name: Logical span name to look up.
84
+
85
+ Returns:
86
+ The OTel context, or ``None`` if the span is not registered.
87
+ """
88
+ entry = self._active.get((session_id, span_name))
89
+ return entry[1] if entry is not None else None
90
+
91
+ def pop(self, session_id: str, span_name: str) -> tuple[Any, Any] | None:
92
+ """Remove and return ``(span, context)``, or ``None`` if absent.
93
+
94
+ Args:
95
+ session_id: The request session identifier.
96
+ span_name: Logical span name to remove.
97
+
98
+ Returns:
99
+ ``(span, otel_context)`` if the span was registered, else ``None``.
100
+ """
101
+ return self._active.pop((session_id, span_name), None)
102
+
103
+ def all_session_ids(self) -> set[str]:
104
+ """Return the set of session IDs that have at least one open span.
105
+
106
+ Returns:
107
+ Set of session ID strings currently tracked in the registry.
108
+ """
109
+ return {k[0] for k in self._active}
110
+
111
+ def clear_session(self, session_id: str) -> None:
112
+ """End all open spans for *session_id* and remove them.
113
+
114
+ Intended for use in ``shutdown()`` to clean up leaked spans when the
115
+ EventBus stops unexpectedly.
116
+
117
+ Args:
118
+ session_id: The request session identifier to clean up.
119
+ """
120
+ keys = [k for k in self._active if k[0] == session_id]
121
+ for k in keys:
122
+ span, _ = self._active.pop(k)
123
+ try:
124
+ span.end()
125
+ except Exception:
126
+ logger.debug(
127
+ "SpanRegistry: error ending leaked span for session %s key %s",
128
+ session_id,
129
+ k[1],
130
+ )
131
+
132
+
133
+ _registry: SpanRegistry | None = None
134
+
135
+
136
+ def get_span_registry() -> SpanRegistry:
137
+ """Return the process-level singleton :class:`SpanRegistry`.
138
+
139
+ The registry is created on first call. Pass a fresh :class:`SpanRegistry`
140
+ directly to subscriber constructors when test isolation is needed.
141
+
142
+ Returns:
143
+ The shared :class:`SpanRegistry` instance.
144
+ """
145
+ global _registry
146
+ if _registry is None:
147
+ _registry = SpanRegistry()
148
+ return _registry
@@ -0,0 +1,50 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """Trace recording subsystem.
4
+
5
+ This package implements the **capture** half of the ``lmcache trace``
6
+ feature. Public API:
7
+
8
+ * :func:`~lmcache.v1.mp_observability.trace.decorator.enable_tracing` —
9
+ decorator that publishes a unified ``TRACE_CALL`` event on each call
10
+ to a decorated function (entry-only, inputs only).
11
+ * :class:`~lmcache.v1.mp_observability.trace.recorder.StorageTraceRecorder`
12
+ — :class:`EventSubscriber` that writes ``TRACE_CALL`` events to a
13
+ binary trace file.
14
+ * :class:`~lmcache.v1.mp_observability.trace.reader.TraceReader` —
15
+ streaming reader for inspection (replay lives in a separate package).
16
+
17
+ When tracing is disabled (the default), the decorator is a thin
18
+ wrapper around the original function; only a single boolean attribute
19
+ load is added per call.
20
+ """
21
+
22
+ # First Party
23
+ from lmcache.v1.mp_observability.trace.decorator import (
24
+ enable_tracing,
25
+ is_tracing_enabled,
26
+ publish_call_event,
27
+ set_tracing_enabled,
28
+ )
29
+ from lmcache.v1.mp_observability.trace.format import Header, Record
30
+ from lmcache.v1.mp_observability.trace.lifecycle import (
31
+ maybe_initialize_trace_recorder,
32
+ )
33
+ from lmcache.v1.mp_observability.trace.reader import TraceReader
34
+ from lmcache.v1.mp_observability.trace.recorder import (
35
+ StorageTraceRecorder,
36
+ TraceRecorder,
37
+ )
38
+
39
+ __all__ = [
40
+ "Header",
41
+ "Record",
42
+ "StorageTraceRecorder",
43
+ "TraceReader",
44
+ "TraceRecorder",
45
+ "enable_tracing",
46
+ "is_tracing_enabled",
47
+ "maybe_initialize_trace_recorder",
48
+ "publish_call_event",
49
+ "set_tracing_enabled",
50
+ ]