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,100 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """Unified event model for the MP observability system."""
4
+
5
+ # Future
6
+ from __future__ import annotations
7
+
8
+ # Standard
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+ from typing import Any
12
+
13
+
14
+ class EventType(Enum):
15
+ """All observable event types in the MP system.
16
+
17
+ Naming convention: ``<COMPONENT>_<OPERATION>`` or
18
+ ``<COMPONENT>_<OPERATION>_<PHASE>`` for start/end pairs.
19
+ """
20
+
21
+ # L1 Manager events
22
+ L1_READ_RESERVED = "l1.read.reserved"
23
+ L1_READ_FINISHED = "l1.read.finished"
24
+ L1_WRITE_RESERVED = "l1.write.reserved"
25
+ L1_WRITE_FINISHED = "l1.write.finished"
26
+ L1_WRITE_FINISHED_AND_READ_RESERVED = "l1.write_finished_and_read_reserved"
27
+ L1_KEYS_EVICTED = "l1.keys.evicted"
28
+
29
+ # StorageManager events
30
+ SM_READ_PREFETCHED = "sm.read.prefetched"
31
+ SM_READ_PREFETCHED_FINISHED = "sm.read.prefetched_finished"
32
+ SM_WRITE_RESERVED = "sm.write.reserved"
33
+ SM_WRITE_FINISHED = "sm.write.finished"
34
+
35
+ # L2 Store Controller events
36
+ L2_STORE_SUBMITTED = "l2.store.submitted"
37
+ L2_STORE_COMPLETED = "l2.store.completed"
38
+
39
+ # L2 Prefetch Controller events
40
+ L2_PREFETCH_LOOKUP_SUBMITTED = "l2.prefetch.lookup.submitted"
41
+ L2_PREFETCH_LOOKUP_COMPLETED = "l2.prefetch.lookup.completed"
42
+ L2_PREFETCH_LOAD_SUBMITTED = "l2.prefetch.load.submitted"
43
+ L2_PREFETCH_LOAD_COMPLETED = "l2.prefetch.load.completed"
44
+
45
+ # MP Server request-level events (start/end pairs)
46
+ MP_STORE_START = "mp.store.start"
47
+ MP_STORE_END = "mp.store.end"
48
+ MP_RETRIEVE_START = "mp.retrieve.start"
49
+ MP_RETRIEVE_END = "mp.retrieve.end"
50
+ MP_LOOKUP_PREFETCH_START = "mp.lookup_prefetch.start"
51
+ MP_LOOKUP_PREFETCH_END = "mp.lookup_prefetch.end"
52
+
53
+ # Chunk hash logging events
54
+ MP_LOOKUP = "mp.lookup"
55
+
56
+ # MP Server lifecycle sentinels (CPU-synchronous)
57
+ MP_REQUEST_START = "mp.request.start"
58
+ MP_RETRIEVE_SUBMITTED = "mp.retrieve.submitted"
59
+ MP_STORE_SUBMITTED = "mp.store.submitted"
60
+ MP_SESSION_END = "mp.session.end"
61
+
62
+ # vLLM block allocation events
63
+ MP_VLLM_BLOCK_ALLOCATION = "mp.vllm.block_allocation"
64
+
65
+ # vLLM end session events
66
+ MP_VLLM_END_SESSION = "mp.vllm.end_session"
67
+
68
+ # Trace recording — unified function-call entry event used by the
69
+ # ``@enable_tracing`` decorator. Metadata layout:
70
+ # ``qualname`` (str): fully-qualified function name
71
+ # ``args`` (dict): name -> raw Python value (codec-encoded at
72
+ # record time by the recorder)
73
+ # ``t_mono`` (float): ``time.monotonic()`` captured at publish
74
+ # time, so it is comparable to
75
+ # ``Event.timestamp`` (wall-clock) even
76
+ # though the drain thread processes the
77
+ # event later
78
+ TRACE_CALL = "trace.call"
79
+
80
+
81
+ @dataclass
82
+ class Event:
83
+ """A single observable event in the MP system.
84
+
85
+ Attributes:
86
+ event_type: The type of event.
87
+ timestamp: Wall-clock time (``time.time()``) stamped by
88
+ ``EventBus.publish()`` at the moment it is called — not when the
89
+ drain thread processes the event. For CUDA host-callback events
90
+ this captures GPU-accurate timing.
91
+ metadata: Flat key-value payload. Contents depend on ``event_type``;
92
+ see the metadata contracts in
93
+ ``docs/design/v1/mp_observability/event-bus.md`` Section 2.7.
94
+ session_id: Caller-provided ID for correlating start/end pairs.
95
+ """
96
+
97
+ event_type: EventType
98
+ timestamp: float = 0.0
99
+ metadata: dict[str, Any] = field(default_factory=dict)
100
+ session_id: str = ""
@@ -0,0 +1,313 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """EventBus: unified pub/sub dispatcher for MP observability events."""
4
+
5
+ # Future
6
+ from __future__ import annotations
7
+
8
+ # Standard
9
+ from abc import ABC, abstractmethod
10
+ from collections import defaultdict
11
+ from dataclasses import dataclass
12
+ from typing import Any, Callable
13
+ import collections
14
+ import threading
15
+ import time
16
+
17
+ # First Party
18
+ from lmcache.logging import init_logger
19
+ from lmcache.v1.mp_observability.event import Event, EventType
20
+
21
+ try:
22
+ # Third Party
23
+ import torch # noqa: F401 — must be imported before lmcache.c_ops
24
+
25
+ # First Party
26
+ import lmcache.c_ops as _lmc_ops
27
+
28
+ _has_native_recorder = hasattr(_lmc_ops, "record_event_on_stream")
29
+ except ImportError:
30
+ _has_native_recorder = False
31
+
32
+ logger = init_logger(__name__)
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # Types
36
+ # ---------------------------------------------------------------------------
37
+
38
+ EventCallback = Callable[[Event], None]
39
+
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Config
43
+ # ---------------------------------------------------------------------------
44
+
45
+
46
+ @dataclass
47
+ class EventBusConfig:
48
+ """Configuration for the EventBus.
49
+
50
+ Attributes:
51
+ enabled: Whether the event bus is active. When disabled,
52
+ ``publish()`` is a no-op and the drain thread is not started.
53
+ max_queue_size: Maximum number of events in the queue. When the
54
+ queue is full, new events are silently dropped with a
55
+ rate-limited warning.
56
+ """
57
+
58
+ enabled: bool = True
59
+ max_queue_size: int = 10_000
60
+
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # Subscriber ABC
64
+ # ---------------------------------------------------------------------------
65
+
66
+
67
+ class EventSubscriber(ABC):
68
+ """Base class for per-component event subscribers.
69
+
70
+ Subclasses declare which ``EventType``\\s they care about via
71
+ ``get_subscriptions()``. The ``register()`` helper wires them up to
72
+ an ``EventBus``.
73
+ """
74
+
75
+ @abstractmethod
76
+ def get_subscriptions(self) -> dict[EventType, EventCallback]:
77
+ """Return event_type -> callback mapping.
78
+
79
+ Called once during ``register()``. The EventBus stores these
80
+ callbacks directly.
81
+ """
82
+ ...
83
+
84
+ def register(self, bus: EventBus) -> None:
85
+ """Subscribe all declared handlers to *bus*."""
86
+ for event_type, callback in self.get_subscriptions().items():
87
+ bus.subscribe(event_type, callback)
88
+
89
+ def shutdown(self) -> None: # noqa: B027
90
+ """Optional cleanup hook. Called by ``EventBus.stop()``."""
91
+ pass
92
+
93
+
94
+ # ---------------------------------------------------------------------------
95
+ # EventBus
96
+ # ---------------------------------------------------------------------------
97
+
98
+
99
+ class EventBus:
100
+ """Manages event ingestion, queueing, and dispatch to subscribers.
101
+
102
+ Events are appended to a deque on the hot path (``publish()``) and
103
+ drained by a background thread that dispatches to registered callbacks.
104
+ """
105
+
106
+ def __init__(self, config: EventBusConfig | None = None) -> None:
107
+ if config is None:
108
+ config = EventBusConfig()
109
+ self._config = config
110
+ self._subscribers: dict[EventType, list[EventCallback]] = defaultdict(list)
111
+ self._queue: collections.deque[Event] = collections.deque()
112
+ self._wake = threading.Event()
113
+ self._stop_flag = threading.Event()
114
+ self._lock = threading.Lock()
115
+ self._thread: threading.Thread | None = None
116
+ self._registered_subscribers: list[EventSubscriber] = []
117
+ self._discard_count: int = 0
118
+ self._last_discard_warning: float = 0.0
119
+
120
+ # -- Public API --------------------------------------------------------
121
+
122
+ def subscribe(self, event_type: EventType, callback: EventCallback) -> None:
123
+ """Register a callback for a specific event type (thread-safe)."""
124
+ with self._lock:
125
+ self._subscribers[event_type].append(callback)
126
+
127
+ def register_subscriber(self, subscriber: EventSubscriber) -> None:
128
+ """Register an ``EventSubscriber`` and wire up its callbacks."""
129
+ subscriber.register(self)
130
+ with self._lock:
131
+ self._registered_subscribers.append(subscriber)
132
+
133
+ def has_subscribers(self, event_type: EventType) -> bool:
134
+ """Return True if at least one callback is registered for *event_type*.
135
+
136
+ Use this to skip expensive event construction on the hot path when
137
+ no subscriber is listening::
138
+
139
+ if bus.has_subscribers(EventType.MP_LOOKUP):
140
+ bus.publish(Event(event_type=EventType.MP_LOOKUP, ...))
141
+ """
142
+ return bool(self._subscribers.get(event_type))
143
+
144
+ def publish_on_stream(self, stream: Any, event: Event) -> None:
145
+ """Schedule event recording as a CUDA host function on *stream*.
146
+
147
+ Uses a C++ callback via ``cudaLaunchHostFunc`` so the callback
148
+ never touches the GIL, avoiding the CUDA-driver/GIL deadlock.
149
+
150
+ No-op when the EventBus is disabled, avoiding the overhead of
151
+ scheduling a host function on the CUDA stream entirely.
152
+ """
153
+ if not self._config.enabled:
154
+ return
155
+ if _has_native_recorder:
156
+ str_metadata: dict[str, str] = {}
157
+ int_metadata: dict[str, int] = {}
158
+ for k, v in event.metadata.items():
159
+ if isinstance(v, int):
160
+ int_metadata[k] = v
161
+ else:
162
+ str_metadata[k] = str(v)
163
+ _lmc_ops.record_event_on_stream(
164
+ stream.ptr,
165
+ event.event_type.value,
166
+ event.session_id,
167
+ str_metadata,
168
+ int_metadata,
169
+ )
170
+ else:
171
+ stream.launch_host_func(self.publish, event)
172
+
173
+ def publish(self, event: Event) -> None:
174
+ """Submit an event (hot path — non-blocking).
175
+
176
+ The event's ``timestamp`` is set to ``time.time()`` at call time.
177
+ When the queue is full the event is silently discarded with a
178
+ rate-limited warning (at most once per second).
179
+ """
180
+ if not self._config.enabled:
181
+ return
182
+
183
+ if len(self._queue) >= self._config.max_queue_size:
184
+ self._discard_count += 1
185
+ now = time.monotonic()
186
+ if now - self._last_discard_warning >= 1.0:
187
+ logger.warning(
188
+ "EventBus queue full (max_queue_size=%d), "
189
+ "%d event(s) discarded so far",
190
+ self._config.max_queue_size,
191
+ self._discard_count,
192
+ )
193
+ self._last_discard_warning = now
194
+ return
195
+
196
+ event.timestamp = time.time()
197
+ self._queue.append(event)
198
+ self._wake.set()
199
+
200
+ def start(self) -> None:
201
+ """Start the background drain thread. No-op when disabled or
202
+ already running."""
203
+ if not self._config.enabled:
204
+ return
205
+ if self._thread is not None and self._thread.is_alive():
206
+ return
207
+
208
+ self._stop_flag.clear()
209
+ self._thread = threading.Thread(
210
+ target=self._run,
211
+ daemon=True,
212
+ name="EventBus",
213
+ )
214
+ logger.debug("Starting EventBus drain thread...")
215
+ self._thread.start()
216
+
217
+ def stop(self) -> None:
218
+ """Stop the drain thread, flush remaining events, and shut down
219
+ all registered subscribers. Safe to call when not started."""
220
+ self._stop_flag.set()
221
+ self._wake.set()
222
+ if self._thread is not None and self._thread.is_alive():
223
+ self._thread.join()
224
+
225
+ # Final drain
226
+ self._drain_all()
227
+
228
+ # Shutdown subscribers
229
+ with self._lock:
230
+ snapshot = list(self._registered_subscribers)
231
+ for sub in snapshot:
232
+ try:
233
+ sub.shutdown()
234
+ except Exception:
235
+ logger.exception(
236
+ "EventBus: error shutting down %s",
237
+ type(sub).__name__,
238
+ )
239
+
240
+ # -- Internal ----------------------------------------------------------
241
+
242
+ def _run(self) -> None:
243
+ """Drain loop: wait for wake signal or timeout, then drain."""
244
+ while not self._stop_flag.is_set():
245
+ self._wake.wait(timeout=0.1)
246
+ self._wake.clear()
247
+ self._drain_all()
248
+
249
+ def _drain_all(self) -> None:
250
+ """Pop all queued events and dispatch to subscribers."""
251
+ # Drain events buffered on the C++ side (from CUDA host callbacks)
252
+ if _has_native_recorder:
253
+ for name, sid, ts, str_meta, int_meta in _lmc_ops.drain_recorded_events():
254
+ metadata: dict[str, Any] = dict(str_meta)
255
+ metadata.update(int_meta)
256
+ self._queue.append(
257
+ Event(
258
+ event_type=EventType(name),
259
+ session_id=sid,
260
+ timestamp=ts,
261
+ metadata=metadata,
262
+ )
263
+ )
264
+
265
+ with self._lock:
266
+ snapshot = dict(self._subscribers)
267
+
268
+ while True:
269
+ try:
270
+ event = self._queue.popleft()
271
+ except IndexError:
272
+ break
273
+ for cb in snapshot.get(event.event_type, []):
274
+ try:
275
+ cb(event)
276
+ except Exception:
277
+ logger.exception(
278
+ "EventBus: error in callback for %s",
279
+ event.event_type.value,
280
+ )
281
+
282
+
283
+ # ---------------------------------------------------------------------------
284
+ # Singleton
285
+ # ---------------------------------------------------------------------------
286
+
287
+ _global_bus = EventBus(EventBusConfig(enabled=False))
288
+ _observability_enabled: bool = False
289
+
290
+
291
+ def is_observability_enabled() -> bool:
292
+ """Fast check for whether observability is active.
293
+
294
+ Use this to guard expensive event-construction or CUDA host-function
295
+ scheduling when observability is disabled.
296
+ """
297
+ return _observability_enabled
298
+
299
+
300
+ def get_event_bus() -> EventBus:
301
+ """Return the current global EventBus singleton."""
302
+ return _global_bus
303
+
304
+
305
+ def init_event_bus(config: EventBusConfig | None = None) -> EventBus:
306
+ """Replace the global singleton with a new EventBus built from *config*.
307
+
308
+ Returns the newly created bus.
309
+ """
310
+ global _global_bus, _observability_enabled
311
+ _global_bus = EventBus(config)
312
+ _observability_enabled = config.enabled if config else True
313
+ return _global_bus
@@ -0,0 +1,145 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """OpenTelemetry SDK initialization for the MP observability system.
4
+
5
+ Supports two modes, controlled by the ``otlp_endpoint`` field in
6
+ ``ObservabilityConfig``:
7
+
8
+ - **OTLP push** (production): metrics/traces are pushed to an OTel collector.
9
+ - **Prometheus pull** (dev/debug): metrics are served on a local ``/metrics``
10
+ endpoint via ``prometheus_client``, no collector needed.
11
+ """
12
+
13
+ # Future
14
+ from __future__ import annotations
15
+
16
+ # Standard
17
+ from collections.abc import Callable
18
+
19
+ # First Party
20
+ from lmcache.logging import init_logger
21
+
22
+ logger = init_logger(__name__)
23
+
24
+
25
+ def init_otel_metrics(
26
+ otlp_endpoint: str | None = None,
27
+ prometheus_port: int | None = None,
28
+ ) -> None:
29
+ """Set up the OpenTelemetry MeterProvider.
30
+
31
+ Args:
32
+ otlp_endpoint: OTLP gRPC endpoint (e.g. ``http://localhost:4317``).
33
+ When set, metrics are pushed to an OTel collector.
34
+ When ``None``, falls back to Prometheus pull mode.
35
+ prometheus_port: Port for the fallback Prometheus ``/metrics``
36
+ endpoint. Only used when *otlp_endpoint* is ``None``.
37
+ Defaults to 9090.
38
+ """
39
+ # Third Party
40
+ from opentelemetry import metrics
41
+ from opentelemetry.sdk.metrics import MeterProvider
42
+
43
+ if otlp_endpoint is not None:
44
+ # OTLP push mode
45
+ # Third Party
46
+ from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
47
+ OTLPMetricExporter,
48
+ )
49
+ from opentelemetry.sdk.metrics.export import (
50
+ PeriodicExportingMetricReader,
51
+ )
52
+
53
+ exporter = OTLPMetricExporter(endpoint=otlp_endpoint, insecure=True)
54
+ reader = PeriodicExportingMetricReader(exporter, export_interval_millis=10000)
55
+ provider = MeterProvider(metric_readers=[reader])
56
+ metrics.set_meter_provider(provider)
57
+ logger.info(
58
+ "OTel MeterProvider initialised with OTLP exporter (%s)",
59
+ otlp_endpoint,
60
+ )
61
+ else:
62
+ # Prometheus pull fallback — no collector needed
63
+ # Third Party
64
+ from opentelemetry.exporter.prometheus import PrometheusMetricReader
65
+ import prometheus_client
66
+
67
+ if prometheus_port is None:
68
+ prometheus_port = 9090
69
+
70
+ reader = PrometheusMetricReader()
71
+ provider = MeterProvider(metric_readers=[reader])
72
+ metrics.set_meter_provider(provider)
73
+ prometheus_client.start_http_server(prometheus_port)
74
+ logger.info(
75
+ "OTel MeterProvider initialised with Prometheus fallback "
76
+ "(http://0.0.0.0:%d/metrics)",
77
+ prometheus_port,
78
+ )
79
+
80
+
81
+ def init_otel_tracing(otlp_endpoint: str | None = None) -> None:
82
+ """Set up the OpenTelemetry TracerProvider with an OTLP exporter.
83
+
84
+ Tracing requires an OTLP endpoint — there is no local fallback.
85
+ When *otlp_endpoint* is ``None``, tracing init is skipped.
86
+
87
+ Args:
88
+ otlp_endpoint: OTLP gRPC endpoint. When ``None``, tracing
89
+ init is skipped (no-op).
90
+ """
91
+ if otlp_endpoint is None:
92
+ logger.debug("No OTLP endpoint configured, skipping tracing init")
93
+ return
94
+
95
+ # Third Party
96
+ from opentelemetry import trace
97
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
98
+ OTLPSpanExporter,
99
+ )
100
+ from opentelemetry.sdk.trace import TracerProvider
101
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
102
+
103
+ exporter = OTLPSpanExporter(endpoint=otlp_endpoint, insecure=True)
104
+ provider = TracerProvider()
105
+ provider.add_span_processor(BatchSpanProcessor(exporter))
106
+ trace.set_tracer_provider(provider)
107
+ logger.info(
108
+ "OTel TracerProvider initialised with OTLP exporter (%s)",
109
+ otlp_endpoint,
110
+ )
111
+
112
+
113
+ def register_gauge(
114
+ meter_name: str,
115
+ gauge_name: str,
116
+ description: str,
117
+ func: Callable[[], int | float],
118
+ ) -> None:
119
+ """Register an OTel observable gauge with a callback.
120
+
121
+ This is a convenience wrapper that hides the OTel boilerplate.
122
+ If OTel is not available, the call is silently ignored.
123
+
124
+ Args:
125
+ meter_name: OTel meter name (e.g. ``lmcache.mp_engine``).
126
+ gauge_name: Metric name (e.g.
127
+ ``lmcache_mp.active_prefetch_jobs``).
128
+ description: Human-readable description of the gauge.
129
+ func: Zero-arg callable returning the current value.
130
+ """
131
+ try:
132
+ # Third Party
133
+ from opentelemetry import metrics as otel_metrics
134
+
135
+ meter = otel_metrics.get_meter(meter_name)
136
+ meter.create_observable_gauge(
137
+ gauge_name,
138
+ callbacks=[lambda _: [otel_metrics.Observation(func())]],
139
+ description=description,
140
+ )
141
+ except ImportError:
142
+ logger.debug(
143
+ "opentelemetry package not found, skipping gauge %s",
144
+ gauge_name,
145
+ )
@@ -0,0 +1,28 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # First Party
4
+ from lmcache.v1.mp_observability.subscribers.logging import (
5
+ L1LoggingSubscriber,
6
+ MPServerLoggingSubscriber,
7
+ SMLoggingSubscriber,
8
+ )
9
+ from lmcache.v1.mp_observability.subscribers.metrics import (
10
+ L0LifecycleSubscriber,
11
+ L1LifecycleSubscriber,
12
+ L1MetricsSubscriber,
13
+ SMMetricsSubscriber,
14
+ )
15
+ from lmcache.v1.mp_observability.subscribers.tracing import (
16
+ MPServerTracingSubscriber,
17
+ )
18
+
19
+ __all__ = [
20
+ "L0LifecycleSubscriber",
21
+ "L1LifecycleSubscriber",
22
+ "L1LoggingSubscriber",
23
+ "L1MetricsSubscriber",
24
+ "MPServerLoggingSubscriber",
25
+ "MPServerTracingSubscriber",
26
+ "SMLoggingSubscriber",
27
+ "SMMetricsSubscriber",
28
+ ]
@@ -0,0 +1,19 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # First Party
3
+ from lmcache.v1.mp_observability.subscribers.logging.l1 import L1LoggingSubscriber
4
+ from lmcache.v1.mp_observability.subscribers.logging.l2 import L2LoggingSubscriber
5
+ from lmcache.v1.mp_observability.subscribers.logging.lookup_hash import (
6
+ LookupHashLoggingSubscriber,
7
+ )
8
+ from lmcache.v1.mp_observability.subscribers.logging.mp_server import (
9
+ MPServerLoggingSubscriber,
10
+ )
11
+ from lmcache.v1.mp_observability.subscribers.logging.sm import SMLoggingSubscriber
12
+
13
+ __all__ = [
14
+ "LookupHashLoggingSubscriber",
15
+ "L1LoggingSubscriber",
16
+ "L2LoggingSubscriber",
17
+ "MPServerLoggingSubscriber",
18
+ "SMLoggingSubscriber",
19
+ ]
@@ -0,0 +1,56 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """L1 logging subscriber — debug logs for L1Manager events.
4
+
5
+ Logs are emitted via Python's standard logging module. When OpenTelemetry
6
+ is installed, ``init_logger`` automatically attaches an OTel
7
+ ``LoggingHandler`` so records are forwarded to OTel when a
8
+ ``LoggerProvider`` is configured at startup.
9
+ """
10
+
11
+ # Future
12
+ from __future__ import annotations
13
+
14
+ # First Party
15
+ from lmcache.logging import init_logger
16
+ from lmcache.v1.mp_observability.event import Event, EventType
17
+ from lmcache.v1.mp_observability.event_bus import EventCallback, EventSubscriber
18
+
19
+ logger = init_logger(__name__)
20
+
21
+
22
+ class L1LoggingSubscriber(EventSubscriber):
23
+ """Logs L1Manager events at debug level."""
24
+
25
+ def get_subscriptions(self) -> dict[EventType, EventCallback]:
26
+ return {
27
+ EventType.L1_READ_RESERVED: self._on_read_reserved,
28
+ EventType.L1_READ_FINISHED: self._on_read_finished,
29
+ EventType.L1_WRITE_RESERVED: self._on_write_reserved,
30
+ EventType.L1_WRITE_FINISHED: self._on_write_finished,
31
+ EventType.L1_WRITE_FINISHED_AND_READ_RESERVED: (
32
+ self._on_write_finished_and_read_reserved
33
+ ),
34
+ EventType.L1_KEYS_EVICTED: self._on_evicted,
35
+ }
36
+
37
+ def _on_read_reserved(self, event: Event) -> None:
38
+ logger.debug("L1 read reserved: %d keys", len(event.metadata["keys"]))
39
+
40
+ def _on_read_finished(self, event: Event) -> None:
41
+ logger.debug("L1 read finished: %d keys", len(event.metadata["keys"]))
42
+
43
+ def _on_write_reserved(self, event: Event) -> None:
44
+ logger.debug("L1 write reserved: %d keys", len(event.metadata["keys"]))
45
+
46
+ def _on_write_finished(self, event: Event) -> None:
47
+ logger.debug("L1 write finished: %d keys", len(event.metadata["keys"]))
48
+
49
+ def _on_write_finished_and_read_reserved(self, event: Event) -> None:
50
+ logger.debug(
51
+ "L1 write finished and read reserved: %d keys",
52
+ len(event.metadata["keys"]),
53
+ )
54
+
55
+ def _on_evicted(self, event: Event) -> None:
56
+ logger.debug("L1 eviction: %d keys", len(event.metadata["keys"]))