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,505 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ """``lmcache trace`` — inspect and replay storage-level trace files.
4
+
5
+ Subcommands:
6
+
7
+ * ``info FILE`` — print a summary (header metadata + per-qualname
8
+ record counts).
9
+ * ``replay FILE ...`` — reissue every recorded call against a fresh
10
+ StorageManager, honoring the recorded inter-call timings. Takes
11
+ the standard storage-manager CLI flags (see
12
+ :func:`lmcache.v1.distributed.config.add_storage_manager_args`),
13
+ plus per-record output (``--verbose`` / ``--jsonl-out``),
14
+ aggregated CSV/JSON summary export (``--output-dir`` / ``--no-csv``
15
+ / ``--json``), and a terminal metrics table (suppressible with
16
+ ``-q``).
17
+
18
+ Trace *capture* is not a ``trace`` subcommand — recording is bound to
19
+ the live process via ``lmcache server --trace-level storage
20
+ [--trace-output ...]``. Surfacing a CLI stub here would only
21
+ duplicate that flag while leaving the user wondering why it cannot
22
+ start a recorder against an already-running server.
23
+ """
24
+
25
+ # Future
26
+ from __future__ import annotations
27
+
28
+ # Standard
29
+ from collections import Counter
30
+ from typing import Callable
31
+ import argparse
32
+ import json
33
+ import os
34
+ import sys
35
+
36
+ # First Party
37
+ from lmcache.cli.commands.base import BaseCommand
38
+ from lmcache.cli.metrics import Metrics, StreamHandler, get_formatter
39
+ from lmcache.logging import init_logger
40
+
41
+ logger = init_logger(__name__)
42
+
43
+ # ``lmcache trace`` drives a real StorageManager and decodes a binary
44
+ # trace file — both pulled from the full LMCache runtime
45
+ # (``lmcache.v1.*``, torch kernels, native ops). Users who installed
46
+ # the thin ``lmcache-cli`` shell lack those modules, so importing them
47
+ # unconditionally at the top of this file would kill the *entire*
48
+ # ``lmcache`` CLI with an opaque ImportError the first time
49
+ # ``lmcache/cli/commands/__init__.py`` loads the command registry.
50
+ #
51
+ # Wrap the heavy imports and remember the error so each subcommand
52
+ # handler can bail out with an actionable install hint. ``record`` is
53
+ # a stub that needs no runtime, so it keeps working on a CLI-only
54
+ # install.
55
+ _IMPORT_ERROR: ImportError | None = None
56
+ try:
57
+ # First Party
58
+ from lmcache.cli.commands.trace.driver import (
59
+ ReplayResult,
60
+ StorageReplayDriver,
61
+ )
62
+ from lmcache.cli.commands.trace.stats import ReplayStatsCollector
63
+ from lmcache.v1.distributed.config import (
64
+ StorageManagerConfig,
65
+ add_storage_manager_args,
66
+ parse_args_to_config,
67
+ )
68
+ from lmcache.v1.mp_observability.config import (
69
+ add_observability_args,
70
+ parse_args_to_observability_config,
71
+ )
72
+ from lmcache.v1.mp_observability.trace.reader import TraceReader
73
+ except ImportError as _exc:
74
+ _IMPORT_ERROR = _exc
75
+
76
+
77
+ def _require_full_install() -> None:
78
+ """Exit with an install hint if the full LMCache runtime is missing.
79
+
80
+ ``lmcache trace info`` and ``lmcache trace replay`` both need
81
+ ``lmcache.v1.*`` (StorageManager, trace codecs, TraceReader).
82
+ When those imports failed at module load — almost always because
83
+ the user installed ``lmcache-cli`` instead of the full package —
84
+ this helper prints the shortest actionable message to stderr and
85
+ exits with status ``2`` so scripts can detect the install gap
86
+ programmatically.
87
+
88
+ Writes directly to :data:`sys.stderr` rather than going through
89
+ :mod:`logging` so the message reaches the user even when the
90
+ lmcache logger has been suppressed or its handlers redirected.
91
+
92
+ No-op when imports succeeded, so it is safe to call
93
+ unconditionally at the top of every trace handler.
94
+ """
95
+ if _IMPORT_ERROR is None:
96
+ return
97
+ print(
98
+ "ERROR: `lmcache trace` needs the full LMCache package "
99
+ "(StorageManager, trace codecs, etc.), but only the `lmcache-cli` "
100
+ "shell appears to be installed.\n"
101
+ " Install the full package with `pip install lmcache` and try "
102
+ "again.\n"
103
+ f" Original import error: {_IMPORT_ERROR}",
104
+ file=sys.stderr,
105
+ )
106
+ sys.exit(2)
107
+
108
+
109
+ class TraceCommand(BaseCommand):
110
+ """Subcommand group for trace inspection and replay."""
111
+
112
+ def name(self) -> str:
113
+ return "trace"
114
+
115
+ def help(self) -> str:
116
+ return "Inspect and replay LMCache storage-level trace files."
117
+
118
+ def add_arguments(self, _parser: argparse.ArgumentParser) -> None:
119
+ # Empty; all args live under the per-subcommand parsers added
120
+ # in :meth:`register`.
121
+ pass
122
+
123
+ def register(self, subparsers: argparse._SubParsersAction) -> None:
124
+ """Register ``trace`` with the root parser.
125
+
126
+ Overrides :meth:`BaseCommand.register` because ``trace`` has
127
+ its own nested subparsers (``info`` and ``replay``). The
128
+ base-class ``--format``/``--output``/``--quiet`` flags do not
129
+ apply uniformly across the subcommands — ``replay`` has its
130
+ own ``--jsonl-out`` output channel — so they are added only
131
+ to ``info``.
132
+ """
133
+ parser = subparsers.add_parser(
134
+ self.name(),
135
+ help=self.help(),
136
+ description=self.help(),
137
+ )
138
+ inner = parser.add_subparsers(
139
+ dest="trace_target",
140
+ required=True,
141
+ metavar="{info,replay}",
142
+ )
143
+ self._register_info(inner)
144
+ self._register_replay(inner)
145
+
146
+ def execute(self, args: argparse.Namespace) -> None:
147
+ """Dispatch the parsed subcommand."""
148
+ handlers: dict[str, Callable[[argparse.Namespace], None]] = {
149
+ "info": self._run_info,
150
+ "replay": self._run_replay,
151
+ }
152
+ handler = handlers.get(args.trace_target)
153
+ if handler is None:
154
+ # ``required=True`` on the subparser makes this unreachable
155
+ # in practice; branch is kept for defensive logging.
156
+ print(f"Unknown trace target: {args.trace_target}", file=sys.stderr)
157
+ sys.exit(1)
158
+ handler(args)
159
+
160
+ # ------------------------------------------------------------------
161
+ # ``info``
162
+ # ------------------------------------------------------------------
163
+
164
+ def _register_info(self, subparsers: argparse._SubParsersAction) -> None:
165
+ parser = subparsers.add_parser(
166
+ "info",
167
+ help="Print a summary of a trace file.",
168
+ )
169
+ parser.add_argument(
170
+ "trace_path",
171
+ metavar="FILE",
172
+ help="Path to a .lct trace file.",
173
+ )
174
+ parser.set_defaults(func=self.execute)
175
+
176
+ def _run_info(self, args: argparse.Namespace) -> None:
177
+ """Read a trace file and print a one-screen summary."""
178
+ _require_full_install()
179
+ with TraceReader(args.trace_path) as r:
180
+ header = r.header
181
+ counts: Counter[str] = Counter()
182
+ max_mono = 0.0
183
+ for record in r.records():
184
+ counts[record.qualname] += 1
185
+ if record.t_mono > max_mono:
186
+ max_mono = record.t_mono
187
+
188
+ print(f"Trace file: {args.trace_path}")
189
+ print(f" level: {header.level}")
190
+ print(f" format_version: {header.format_version}")
191
+ print(f" trace_schema_version: {header.trace_schema_version}")
192
+ print(f" duration: {max_mono:.3f}s")
193
+ print(f" sm_config_digest: {header.sm_config_digest or '(none)'}")
194
+ print(f" total_records: {sum(counts.values())}")
195
+ if counts:
196
+ print(" ops:")
197
+ for qn in sorted(counts):
198
+ print(f" {qn}: {counts[qn]}")
199
+ else:
200
+ print(" ops: (none)")
201
+
202
+ # ------------------------------------------------------------------
203
+ # ``replay``
204
+ # ------------------------------------------------------------------
205
+
206
+ def _register_replay(self, subparsers: argparse._SubParsersAction) -> None:
207
+ parser = subparsers.add_parser(
208
+ "replay",
209
+ help="Replay a trace file against a fresh StorageManager.",
210
+ description=(
211
+ "Replay a trace file against a fresh StorageManager. "
212
+ "Accepts the standard storage-manager config flags "
213
+ "(--l1-size-gb, --eviction-policy, --l2-…); see "
214
+ "'lmcache server --help' for the full list."
215
+ ),
216
+ )
217
+ parser.add_argument(
218
+ "trace_path",
219
+ metavar="FILE",
220
+ help="Path to a .lct trace file.",
221
+ )
222
+ parser.add_argument(
223
+ "--verbose",
224
+ action="store_true",
225
+ default=False,
226
+ help="Print one line per replayed record.",
227
+ )
228
+ parser.add_argument(
229
+ "--jsonl-out",
230
+ default=None,
231
+ metavar="PATH",
232
+ help=(
233
+ "Write one JSON object per replayed record to PATH "
234
+ "(qualname, latency_ms, failed). Useful for post-hoc "
235
+ "analysis."
236
+ ),
237
+ )
238
+ parser.add_argument(
239
+ "--output-dir",
240
+ default=".",
241
+ help=(
242
+ "Directory for aggregated CSV/JSON summary output "
243
+ "(default: current directory)."
244
+ ),
245
+ )
246
+ parser.add_argument(
247
+ "--no-csv",
248
+ action="store_true",
249
+ help="Skip the aggregated CSV summary export.",
250
+ )
251
+ parser.add_argument(
252
+ "--json",
253
+ action="store_true",
254
+ help="Also export an aggregated JSON summary.",
255
+ )
256
+ parser.add_argument(
257
+ "-q",
258
+ "--quiet",
259
+ action="store_true",
260
+ help="Suppress the terminal metrics table (files are still written).",
261
+ )
262
+ # ``add_storage_manager_args`` and ``add_observability_args``
263
+ # live in the full LMCache runtime (``lmcache.v1.*``) and are
264
+ # unavailable in the CLI-only install. When those imports
265
+ # failed at module load, register ``replay`` with only the
266
+ # CLI-local flags above so ``--help`` still works; the actual
267
+ # execute path bails via :func:`_require_full_install` before
268
+ # it would try to parse the missing-flag namespace.
269
+ #
270
+ # When the full runtime *is* present we share the whole
271
+ # observability surface with ``lmcache server``.
272
+ # ``--trace-level`` / ``--trace-output`` configure *recording*
273
+ # and have no effect during replay; :meth:`_run_replay`
274
+ # overrides them to ``None`` before constructing the
275
+ # observability config rather than duplicating the argparse
276
+ # registration to strip them.
277
+ if _IMPORT_ERROR is None:
278
+ add_storage_manager_args(parser)
279
+ add_observability_args(parser)
280
+ parser.set_defaults(func=self.execute)
281
+
282
+ def _run_replay(self, args: argparse.Namespace) -> None:
283
+ """Construct a StorageManager from *args* and drive replay.
284
+
285
+ Produces three kinds of output:
286
+
287
+ * Per-record stream: every dispatch is logged at INFO with its
288
+ progress (``[N/total]``), qualname, and latency.
289
+ ``--verbose`` additionally mirrors each record to stdout,
290
+ and ``--jsonl-out PATH`` writes one JSON object per record
291
+ to ``PATH`` for post-hoc analysis.
292
+ * Aggregated per-qualname summary: CSV (unless ``--no-csv``)
293
+ and JSON (with ``--json``) written under ``--output-dir``.
294
+ * Terminal metrics table (unless ``--quiet``) using the shared
295
+ :class:`~lmcache.cli.metrics.Metrics` renderer.
296
+ """
297
+ _require_full_install()
298
+ sm_config: StorageManagerConfig = parse_args_to_config(args)
299
+
300
+ # ``--trace-level`` / ``--trace-output`` belong to the recording
301
+ # surface. They are still registered on the parser (see
302
+ # :meth:`_register_replay`) so the flag set stays in lock-step
303
+ # with ``lmcache server``, but they have no meaning here — any
304
+ # value a caller passes is silently clobbered to ``None`` so
305
+ # the replay-side ``ObservabilityConfig`` never tries to start
306
+ # a recorder.
307
+ args.trace_level = None
308
+ args.trace_output = None
309
+ obs_config = parse_args_to_observability_config(args)
310
+
311
+ # Create output directories *before* replay starts. A replay
312
+ # can run for minutes; surfacing a bad ``--output-dir`` or
313
+ # unwritable ``--jsonl-out`` parent now avoids silently losing
314
+ # the summary/stream after the work has already happened.
315
+ os.makedirs(args.output_dir, exist_ok=True)
316
+ if args.jsonl_out:
317
+ jsonl_parent = os.path.dirname(os.path.abspath(args.jsonl_out))
318
+ if jsonl_parent:
319
+ os.makedirs(jsonl_parent, exist_ok=True)
320
+
321
+ # ANSI: bold + yellow for the banner text, reset at the end.
322
+ # The lmcache log formatter only colors the WARNING prefix;
323
+ # these codes highlight the message body too. Writing them
324
+ # into a file via shell redirection leaves the escape bytes
325
+ # visible but still readable.
326
+ bold = "\033[1;33m"
327
+ reset = "\033[0m"
328
+ bar = "=" * 78
329
+ logger.warning(
330
+ "\n%s%s\n"
331
+ " !! REPLAY ENVIRONMENT MISMATCH MAY CAUSE RETRIEVE MISSES !!\n"
332
+ "%s%s\n"
333
+ " * Replay uses the *replay-side* StorageManager config, which\n"
334
+ " may differ from the config recorded in the trace.\n"
335
+ " * Replay runs on a host whose performance may differ from\n"
336
+ " the recording host.\n"
337
+ " * StorageManager reads/writes are async — an L2 load that\n"
338
+ " had finished at record time may not have finished yet at\n"
339
+ " replay time, so the matching retrieve can miss.\n"
340
+ "\n"
341
+ " Treat retrieve-miss counts as a signal about the replay\n"
342
+ " environment, not as a defect in the trace.\n"
343
+ "%s%s",
344
+ bold,
345
+ bar,
346
+ bar,
347
+ reset,
348
+ bar,
349
+ reset,
350
+ )
351
+
352
+ # Pre-scan to count total records so progress logs can show
353
+ # [N/total]. The reader streams frames, so counting is cheap
354
+ # relative to replay (which actually dispatches StorageManager
355
+ # calls).
356
+ with TraceReader(args.trace_path) as r:
357
+ total_records = sum(1 for _ in r.records())
358
+ logger.info(
359
+ "trace replay: file=%s records=%d",
360
+ args.trace_path,
361
+ total_records,
362
+ )
363
+
364
+ jsonl_fh = open(args.jsonl_out, "w") if args.jsonl_out else None
365
+ verbose = args.verbose
366
+ counter = {"n": 0}
367
+
368
+ def _on_record(qualname: str, latency_s: float, failed: bool) -> None:
369
+ counter["n"] += 1
370
+ status = "FAIL" if failed else "OK"
371
+ logger.info(
372
+ "[%d/%d] %s %s (%.3fms)",
373
+ counter["n"],
374
+ total_records,
375
+ status,
376
+ qualname,
377
+ latency_s * 1000.0,
378
+ )
379
+ if verbose:
380
+ print(
381
+ f" [{counter['n']}/{total_records}] "
382
+ f"{status:<4} {latency_s * 1000:8.3f}ms {qualname}"
383
+ )
384
+ if jsonl_fh is not None:
385
+ jsonl_fh.write(
386
+ json.dumps(
387
+ {
388
+ "qualname": qualname,
389
+ "latency_ms": latency_s * 1000.0,
390
+ "failed": failed,
391
+ }
392
+ )
393
+ + "\n"
394
+ )
395
+
396
+ try:
397
+ with StorageReplayDriver(
398
+ sm_config, args.trace_path, obs_config=obs_config
399
+ ) as driver:
400
+ result = driver.run(on_record=_on_record)
401
+ finally:
402
+ if jsonl_fh is not None:
403
+ jsonl_fh.close()
404
+
405
+ if not args.no_csv:
406
+ csv_path = os.path.join(args.output_dir, "trace_replay_ops.csv")
407
+ result.stats.export_csv(csv_path)
408
+ logger.info("CSV written to %s", csv_path)
409
+ if args.json:
410
+ json_path = os.path.join(args.output_dir, "trace_replay_summary.json")
411
+ result.stats.export_json(json_path)
412
+ logger.info("JSON written to %s", json_path)
413
+
414
+ if not args.quiet:
415
+ self._emit_replay_metrics(result.stats, result)
416
+
417
+ if result.records_failed > 0:
418
+ sys.exit(1)
419
+
420
+ @staticmethod
421
+ def _emit_replay_metrics(
422
+ stats: ReplayStatsCollector,
423
+ result: ReplayResult,
424
+ ) -> None:
425
+ """Print the replay summary using the shared :class:`Metrics` renderer.
426
+
427
+ Args:
428
+ stats: The stats collector populated during replay.
429
+ result: The full :class:`ReplayResult` — used for the
430
+ replayed/skipped/failed totals and digest comparison.
431
+ """
432
+ metrics = Metrics(title="Trace Replay Result")
433
+ metrics.add_handler(StreamHandler(get_formatter("terminal", width=64)))
434
+
435
+ overall = metrics.add_section("overall", "Overall")
436
+ overall.add("level", "Trace level", result.header_level)
437
+ overall.add("replayed", "Records replayed", result.records_replayed)
438
+ overall.add("skipped", "Records skipped", result.records_skipped)
439
+ overall.add("failed", "Records failed", result.records_failed)
440
+ overall.add(
441
+ "duration",
442
+ "Replay duration (s)",
443
+ round(stats.total_duration_s(), 3),
444
+ )
445
+ header_digest = result.header_digest
446
+ replay_digest = result.replay_config_digest
447
+ if header_digest and replay_digest and header_digest != replay_digest:
448
+ overall.add(
449
+ "digest",
450
+ "Config digest",
451
+ f"MISMATCH (rec={header_digest[:8]}, run={replay_digest[:8]})",
452
+ )
453
+ elif header_digest:
454
+ overall.add("digest", "Config digest", f"match ({header_digest[:8]})")
455
+
456
+ summary = stats.summary()
457
+ if summary:
458
+ ops_section = metrics.add_section("ops", "Per-Op Latency (ms)")
459
+ for qn in sorted(summary):
460
+ s = summary[qn]
461
+ short = _short_op_name(qn)
462
+ ops_section.add(f"{short}_count", f"{short} count", s.count)
463
+ ops_section.add(
464
+ f"{short}_mean",
465
+ f"{short} mean",
466
+ round(s.mean_ms, 3),
467
+ )
468
+ ops_section.add(
469
+ f"{short}_p50",
470
+ f"{short} p50",
471
+ round(s.p50_ms, 3),
472
+ )
473
+ ops_section.add(
474
+ f"{short}_p99",
475
+ f"{short} p99",
476
+ round(s.p99_ms, 3),
477
+ )
478
+
479
+ metrics.emit()
480
+
481
+
482
+ def _short_op_name(qualname: str) -> str:
483
+ """Return a compact, human-readable label for a traced qualname.
484
+
485
+ Plain methods collapse to the method name: the table has limited
486
+ column width and the fully-qualified path is verbose.
487
+
488
+ Context-manager handlers (``__enter__`` / ``__exit__``) instead
489
+ collapse to ``<owning_method>.enter`` / ``<owning_method>.exit``,
490
+ so the reader can tell *which* context manager the pair belongs
491
+ to — the bare ``__enter__`` / ``__exit__`` label is useless when
492
+ multiple context-manager-returning methods are traced.
493
+
494
+ Args:
495
+ qualname: Dotted qualname recorded by the tracer, e.g.
496
+ ``lmcache.v1.distributed.storage_manager.StorageManager.read_prefetched_results.__enter__``.
497
+
498
+ Returns:
499
+ A short label suitable as a metrics row prefix.
500
+ """
501
+ parts = qualname.split(".")
502
+ last = parts[-1]
503
+ if last in ("__enter__", "__exit__") and len(parts) >= 2:
504
+ return f"{parts[-2]}.{last.strip('_')}"
505
+ return last