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,357 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """HTTP request for VLLM Serving Engine."""
3
+
4
+ # Standard
5
+ from typing import Any, Optional
6
+ import json
7
+ import sys
8
+ import time
9
+ import urllib.error
10
+ import urllib.request
11
+
12
+ _MAX_ERR = 65536
13
+ MetricValue = tuple[str, Any]
14
+ MetricMap = dict[str, MetricValue]
15
+ _METRIC_NAMES = {
16
+ "prompt_tokens": "Input tokens",
17
+ "output_tokens": "Output tokens",
18
+ "ttft_ms": "TTFT (ms)",
19
+ "tpot_ms_per_token": "TPOT (ms/token)",
20
+ "total_latency_ms": "Total latency (ms)",
21
+ "throughput_tokens_per_s": "Throughput (tokens/s)",
22
+ "model": "Model",
23
+ }
24
+
25
+
26
+ def _clip(text: str, limit: int = _MAX_ERR) -> str:
27
+ return (
28
+ text
29
+ if len(text) <= limit
30
+ else text[: max(0, limit - 24)] + "\n...(message truncated)..."
31
+ )
32
+
33
+
34
+ def _info(msg: str) -> None:
35
+ print(f"lmcache query: {msg}", file=sys.stderr)
36
+
37
+
38
+ def _openai_error(obj: dict[str, Any]) -> Optional[str]:
39
+ err = obj.get("error")
40
+ if err is None:
41
+ return None
42
+ if isinstance(err, str):
43
+ return err.strip() or None
44
+ if not isinstance(err, dict):
45
+ return _clip(str(err))
46
+ for key in ("message", "detail"):
47
+ val = err.get(key)
48
+ if not isinstance(val, str) or not val.strip():
49
+ continue
50
+ typ = err.get("type") or err.get("code")
51
+ if key == "message" and isinstance(typ, str) and typ.strip():
52
+ return f"{typ.strip()}: {val.strip()}"
53
+ return val.strip()
54
+ try:
55
+ return _clip(json.dumps(err, ensure_ascii=False))
56
+ except Exception:
57
+ return _clip(str(err))
58
+
59
+
60
+ def _raise_openai_error(obj: dict[str, Any]) -> None:
61
+ msg = _openai_error(obj)
62
+ if msg:
63
+ raise RuntimeError(_clip(msg))
64
+
65
+
66
+ def _raise_json_blob_error(blob: str) -> None:
67
+ s = blob.strip()
68
+ if not s.startswith("{"):
69
+ return
70
+ try:
71
+ obj = json.loads(s)
72
+ except json.JSONDecodeError:
73
+ return
74
+ if isinstance(obj, dict):
75
+ _raise_openai_error(obj)
76
+
77
+
78
+ def _api_url(base: str, path: str) -> str:
79
+ base = base.strip()
80
+ if "://" not in base:
81
+ base = f"http://{base}"
82
+ base = base.rstrip("/")
83
+ return f"{base if base.endswith('/v1') else base + '/v1'}/{path}"
84
+
85
+
86
+ def _read_json(url: str, timeout: float) -> dict[str, Any]:
87
+ try:
88
+ with urllib.request.urlopen(
89
+ urllib.request.Request(url, method="GET"), timeout=max(timeout + 2.0, 5.0)
90
+ ) as resp:
91
+ raw = resp.read().decode("utf-8", errors="replace")
92
+ except urllib.error.HTTPError as e:
93
+ body = e.read().decode("utf-8", errors="replace")[:512]
94
+ raise RuntimeError(
95
+ f"GET {url} failed (HTTP {e.code}): {body or 'no body'}"
96
+ ) from e
97
+ except urllib.error.URLError as e:
98
+ raise RuntimeError(f"GET {url} failed: {getattr(e, 'reason', e)}") from e
99
+ try:
100
+ obj = json.loads(raw)
101
+ except json.JSONDecodeError as e:
102
+ raise RuntimeError(f"Invalid JSON from GET {url}: {e}") from e
103
+ if not isinstance(obj, dict):
104
+ raise RuntimeError(f"GET {url}: expected a JSON object")
105
+ return obj
106
+
107
+
108
+ def _sse_piece(obj: dict[str, Any], chat: bool) -> str:
109
+ choices = obj.get("choices") or []
110
+ if not choices:
111
+ return ""
112
+ c0 = choices[0]
113
+ return (
114
+ str((c0.get("delta") or {}).get("content") or "")
115
+ if chat
116
+ else str(c0.get("text") or "")
117
+ )
118
+
119
+
120
+ def _trim_misc_buffer(misc: list[str], limit: int = _MAX_ERR) -> None:
121
+ while misc and sum(map(len, misc)) > limit:
122
+ misc.pop(0)
123
+
124
+
125
+ def _stream(
126
+ url: str,
127
+ body: dict[str, Any],
128
+ timeout: float,
129
+ *,
130
+ chat: bool,
131
+ max_tokens: int,
132
+ ) -> dict[str, Any]:
133
+ """POST with ``stream: true``; parse SSE; return TTFT/TPOT and token metrics."""
134
+ payload = {
135
+ **body,
136
+ "stream": True,
137
+ "stream_options": {"include_usage": True},
138
+ }
139
+ req = urllib.request.Request(
140
+ url,
141
+ data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
142
+ method="POST",
143
+ headers={"Content-Type": "application/json"},
144
+ )
145
+ t0, first_token_t, pieces, usage, misc = time.time(), None, [], None, []
146
+ try:
147
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
148
+ while True:
149
+ raw = resp.readline()
150
+ if not raw:
151
+ break
152
+ line = raw.decode("utf-8", errors="replace").strip()
153
+ if not line:
154
+ continue
155
+ if not line.startswith("data:"):
156
+ misc.append(line)
157
+ _trim_misc_buffer(misc)
158
+ continue
159
+ chunk = line[5:].strip()
160
+ if chunk == "[DONE]":
161
+ break
162
+ try:
163
+ obj = json.loads(chunk)
164
+ except json.JSONDecodeError:
165
+ misc.append(chunk)
166
+ _trim_misc_buffer(misc)
167
+ continue
168
+ if not isinstance(obj, dict):
169
+ continue
170
+ _raise_openai_error(obj)
171
+ piece = _sse_piece(obj, chat)
172
+ if piece:
173
+ first_token_t = first_token_t or time.time()
174
+ pieces.append(piece)
175
+
176
+ u_chunk = obj.get("usage")
177
+ if u_chunk is not None:
178
+ usage = u_chunk
179
+ t1 = time.time()
180
+ except urllib.error.HTTPError as e:
181
+ err_body = e.read().decode("utf-8", errors="replace")
182
+ _raise_json_blob_error(err_body)
183
+ raise RuntimeError(
184
+ _clip(f"POST {url} failed (HTTP {e.code}):\n{_clip(err_body)}")
185
+ ) from e
186
+ except urllib.error.URLError as e:
187
+ raise RuntimeError(f"POST {url} failed: {getattr(e, 'reason', e)}") from e
188
+
189
+ misc_text = "\n".join(misc).strip()
190
+ _raise_json_blob_error(misc_text)
191
+ joined = "".join(pieces)
192
+ if not joined and usage is None:
193
+ raise RuntimeError(
194
+ _clip(f"No completion output from engine. Captured response:\n{misc_text}")
195
+ if misc_text
196
+ else "Empty response from engine (no SSE chunks parsed)."
197
+ )
198
+
199
+ u = usage or {}
200
+ prompt_tokens = int(u.get("prompt_tokens") or 0)
201
+ num_completion = int(u.get("completion_tokens") or 0)
202
+ # Match V2RequestSender: server count if present, else max_tokens cap.
203
+ num_generated = num_completion if num_completion > 0 else max_tokens
204
+ if first_token_t is None:
205
+ # Use total round-trip as a conservative TTFT approximation.
206
+ ttft_s = t1 - t0
207
+ decode_time = 0.0
208
+ else:
209
+ ttft_s = first_token_t - t0
210
+ decode_time = t1 - first_token_t
211
+ dt = t1 - t0
212
+ decoding_speed = (num_generated / decode_time) if decode_time > 0 else 0.0
213
+ tpot_s = (
214
+ (decode_time / num_generated) if num_generated > 0 and decode_time > 0 else 0.0
215
+ )
216
+ return {
217
+ "prompt_tokens": prompt_tokens,
218
+ "output_tokens": num_generated,
219
+ "ttft_ms": ttft_s * 1000.0,
220
+ "tpot_ms_per_token": tpot_s * 1000.0,
221
+ "total_latency_ms": dt * 1000.0,
222
+ "throughput_tokens_per_s": decoding_speed,
223
+ }
224
+
225
+
226
+ def _missing_chat_template(exc: BaseException) -> bool:
227
+ msg = str(exc).lower()
228
+ return any(
229
+ s in msg
230
+ for s in (
231
+ "chat template",
232
+ "chat_template",
233
+ "chattemplate",
234
+ "template resolution",
235
+ "must provide a chat template",
236
+ "default chat template is no longer allowed",
237
+ )
238
+ )
239
+
240
+
241
+ def _weak_completions_error(msg: str) -> bool:
242
+ msg = msg.lower()
243
+ return any(
244
+ s in msg
245
+ for s in (
246
+ "empty response from engine",
247
+ "no completion output from engine",
248
+ "no sse chunks parsed",
249
+ )
250
+ )
251
+
252
+
253
+ class Request:
254
+ """Build and send one query request against an OpenAI-compatible endpoint."""
255
+
256
+ def __init__(
257
+ self,
258
+ base: str,
259
+ model: Optional[str],
260
+ max_tokens: int,
261
+ timeout: float,
262
+ *,
263
+ completions_only: bool = False,
264
+ chat_first: bool = False,
265
+ ) -> None:
266
+ self._base = base
267
+ self._model = model
268
+ self._max_tokens = max_tokens
269
+ self._timeout = timeout
270
+ self._completions_only = completions_only
271
+ self._chat_first = chat_first
272
+
273
+ def build_request(self, prompt: str) -> dict[str, Any]:
274
+ """Build request payload and metadata for the provided prompt."""
275
+ model = self._model or self._first_model_id()
276
+ return {
277
+ "base": self._base,
278
+ "model": model,
279
+ "prompt": prompt,
280
+ "max_tokens": self._max_tokens,
281
+ "timeout": self._timeout,
282
+ "completions_only": self._completions_only,
283
+ "chat_first": self._chat_first,
284
+ }
285
+
286
+ def send_request(self, prompt: str) -> dict[str, Any] | MetricMap:
287
+ """Send request and return stats."""
288
+ request_data = self.build_request(prompt)
289
+ stats = {
290
+ "model": request_data["model"],
291
+ **self._query_with_fallback(request_data),
292
+ }
293
+ return {key: (_METRIC_NAMES.get(key, key), stats[key]) for key in stats}
294
+
295
+ def _first_model_id(self) -> str:
296
+ """Return the first model ID from ``GET /v1/models``."""
297
+ obj = _read_json(_api_url(self._base, "models"), self._timeout)
298
+ data = obj.get("data")
299
+ if not isinstance(data, list) or not data:
300
+ raise RuntimeError(
301
+ "GET /v1/models returned no models; pass --model explicitly."
302
+ )
303
+ first = data[0]
304
+ if not isinstance(first, dict) or "id" not in first:
305
+ raise RuntimeError("GET /v1/models: first entry missing 'id'.")
306
+ return str(first["id"])
307
+
308
+ def _query_with_fallback(self, request_data: dict[str, Any]) -> dict[str, Any]:
309
+ """Send one query and fallback between completions/chat endpoints."""
310
+ if request_data["completions_only"]:
311
+ return self._query(request_data, chat=False)
312
+ try:
313
+ return self._query(request_data, chat=request_data["chat_first"])
314
+ except RuntimeError as first_err:
315
+ if request_data["chat_first"]:
316
+ if not _missing_chat_template(first_err):
317
+ raise
318
+ _info(
319
+ "chat API failed (no chat template); retrying with /v1/completions"
320
+ )
321
+ return self._query(request_data, chat=False)
322
+ _info("/v1/completions failed; retrying with /v1/chat/completions")
323
+ try:
324
+ return self._query(request_data, chat=True)
325
+ except RuntimeError as second_err:
326
+ if _weak_completions_error(str(first_err)) and _missing_chat_template(
327
+ second_err
328
+ ):
329
+ _info(
330
+ "base / completion-only models: try `--completions` or "
331
+ "an instruct model with a chat template."
332
+ )
333
+ raise second_err
334
+ raise RuntimeError(f"{first_err}; then {second_err}") from second_err
335
+
336
+ def _query(self, request_data: dict[str, Any], *, chat: bool) -> dict[str, Any]:
337
+ path = "chat/completions" if chat else "completions"
338
+ model = request_data["model"]
339
+ prompt = request_data["prompt"]
340
+ max_tokens = request_data["max_tokens"]
341
+ timeout = request_data["timeout"]
342
+ body = (
343
+ {
344
+ "model": model,
345
+ "messages": [{"role": "user", "content": prompt}],
346
+ "max_tokens": max_tokens,
347
+ }
348
+ if chat
349
+ else {"model": model, "prompt": prompt, "max_tokens": max_tokens}
350
+ )
351
+ return _stream(
352
+ _api_url(request_data["base"], path),
353
+ body,
354
+ timeout,
355
+ chat=chat,
356
+ max_tokens=max_tokens,
357
+ )
@@ -0,0 +1,99 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """``lmcache server`` — launch the LMCache server (ZMQ + HTTP)."""
3
+
4
+ # Standard
5
+ import argparse
6
+
7
+ # First Party
8
+ from lmcache.cli.commands.base import BaseCommand
9
+
10
+
11
+ class ServerCommand(BaseCommand):
12
+ """CLI command that launches the LMCache server (ZMQ + HTTP)."""
13
+
14
+ def name(self) -> str:
15
+ """Return the subcommand name.
16
+
17
+ Returns:
18
+ The string ``"server"``.
19
+ """
20
+ return "server"
21
+
22
+ def help(self) -> str:
23
+ """Return short help text.
24
+
25
+ Returns:
26
+ Help string shown by ``lmcache -h``.
27
+ """
28
+ return "Launch the LMCache server (ZMQ + HTTP)."
29
+
30
+ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
31
+ """Add server-specific arguments to the parser.
32
+
33
+ Composes argument groups from the multiprocess, storage manager,
34
+ HTTP frontend, Prometheus, and telemetry config modules.
35
+ Silently skips argument registration when server dependencies
36
+ (e.g. CUDA extensions) are not installed; ``execute`` will then
37
+ print an actionable error.
38
+
39
+ Args:
40
+ parser: The ``ArgumentParser`` for this subcommand.
41
+ """
42
+ try:
43
+ # First Party
44
+ from lmcache.v1.distributed.config import add_storage_manager_args
45
+ from lmcache.v1.mp_observability.config import add_observability_args
46
+ from lmcache.v1.multiprocess.config import (
47
+ add_http_frontend_args,
48
+ add_mp_server_args,
49
+ )
50
+ except ImportError as e:
51
+ print(
52
+ f"Failed to import server dependencies: {e}. "
53
+ "Install the full lmcache package to use 'lmcache server'."
54
+ )
55
+ return
56
+
57
+ add_mp_server_args(parser)
58
+ add_storage_manager_args(parser)
59
+ add_http_frontend_args(parser)
60
+ add_observability_args(parser)
61
+
62
+ def execute(self, args: argparse.Namespace) -> None:
63
+ """Parse CLI arguments into config objects and launch the HTTP server.
64
+
65
+ Args:
66
+ args: Parsed CLI arguments.
67
+
68
+ Raises:
69
+ SystemExit: When server dependencies are not installed.
70
+ """
71
+ # Standard
72
+ import sys
73
+
74
+ try:
75
+ # First Party
76
+ from lmcache.v1.distributed.config import parse_args_to_config
77
+ from lmcache.v1.mp_observability.config import (
78
+ parse_args_to_observability_config,
79
+ )
80
+ from lmcache.v1.multiprocess.config import (
81
+ parse_args_to_http_frontend_config,
82
+ parse_args_to_mp_server_config,
83
+ )
84
+ from lmcache.v1.multiprocess.http_server import run_http_server
85
+ except ImportError:
86
+ print(
87
+ "The 'lmcache server' command requires the full lmcache "
88
+ "installation with CUDA extensions.\n"
89
+ "Install with: pip install lmcache",
90
+ file=sys.stderr,
91
+ )
92
+ sys.exit(1)
93
+
94
+ run_http_server(
95
+ http_config=parse_args_to_http_frontend_config(args),
96
+ mp_config=parse_args_to_mp_server_config(args),
97
+ storage_manager_config=parse_args_to_config(args),
98
+ obs_config=parse_args_to_observability_config(args),
99
+ )
@@ -0,0 +1,63 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """``lmcache tool`` command — offline analysis utilities.
3
+
4
+ To add a new tool:
5
+
6
+ 1. Create ``lmcache/cli/commands/tool/<your_tool>.py`` with a ``register``
7
+ function that adds a sub-subcommand to the supplied ``subparsers``.
8
+ 2. Import it here and call ``your_tool.register(inner)`` inside
9
+ :meth:`ToolCommand.register`.
10
+ """
11
+
12
+ # Standard
13
+ import argparse
14
+ import sys
15
+
16
+ # First Party
17
+ from lmcache.cli.commands.base import BaseCommand
18
+ from lmcache.cli.commands.tool import cache_simulator
19
+
20
+
21
+ class ToolCommand(BaseCommand):
22
+ """CLI command for offline analysis tools bundled with LMCache."""
23
+
24
+ def name(self) -> str:
25
+ """Return the subcommand name."""
26
+ return "tool"
27
+
28
+ def help(self) -> str:
29
+ """Return short help text shown by ``lmcache -h``."""
30
+ return "Run offline analysis tools."
31
+
32
+ def add_arguments(self, _parser: argparse.ArgumentParser) -> None:
33
+ """No top-level arguments; all args are registered in register()."""
34
+
35
+ def register(self, subparsers: argparse._SubParsersAction) -> None:
36
+ """Register ``lmcache tool`` and all tool sub-subcommands.
37
+
38
+ Args:
39
+ subparsers: The subparsers action from the root parser.
40
+ """
41
+ parser = subparsers.add_parser(
42
+ self.name(),
43
+ help=self.help(),
44
+ description="Run offline analysis tools bundled with LMCache.",
45
+ )
46
+ inner = parser.add_subparsers(
47
+ dest="tool_name",
48
+ required=True,
49
+ metavar="{cache-simulator}",
50
+ )
51
+ cache_simulator.register(inner)
52
+
53
+ def execute(self, args: argparse.Namespace) -> None:
54
+ """Dispatch is handled per-tool via parser.set_defaults(func=...).
55
+
56
+ This method is never called directly; each tool's register() binds
57
+ its own execute function as the dispatch target.
58
+
59
+ Args:
60
+ args: Parsed CLI arguments.
61
+ """
62
+ print(f"Unknown tool: {getattr(args, 'tool_name', '?')}", file=sys.stderr)
63
+ sys.exit(1)
@@ -0,0 +1,113 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """``lmcache tool cache-simulator`` sub-subcommand wiring.
3
+
4
+ Registers the ``simulate``, ``sweep``, and ``gen-dataset`` actions under
5
+ ``lmcache tool cache-simulator``. Flag definitions and execution logic
6
+ live entirely in the simulator modules:
7
+
8
+ * :func:`~lmcache.tools.cache_simulator.simulator.add_simulate_arguments`
9
+ * :func:`~lmcache.tools.cache_simulator.simulator.run_simulate`
10
+ * :func:`~lmcache.tools.cache_simulator.plot_hit_rate.add_sweep_arguments`
11
+ * :func:`~lmcache.tools.cache_simulator.plot_hit_rate.run_sweep`
12
+ * :func:`~lmcache.tools.cache_simulator.gen_bench_dataset.add_gen_dataset_arguments`
13
+ * :func:`~lmcache.tools.cache_simulator.gen_bench_dataset.run_gen_dataset`
14
+
15
+ To add a new action, add ``add_*_arguments`` / ``run_*`` functions to the
16
+ appropriate simulator module and wire them up in :func:`register`.
17
+ """
18
+
19
+ # Standard
20
+ import argparse
21
+
22
+
23
+ def register(subparsers: argparse._SubParsersAction) -> None:
24
+ """Register ``cache-simulator`` and its ``simulate``/``sweep`` actions.
25
+
26
+ Imports are lazy so that matplotlib is not loaded at CLI startup.
27
+
28
+ Args:
29
+ subparsers: The subparsers action from the ``lmcache tool`` parser.
30
+ """
31
+ # Lazy imports — keeps CLI startup fast and makes cache-simulator optional.
32
+ # Missing sortedcontainers/matplotlib (plot extra) skips the subcommand.
33
+ try:
34
+ # First Party
35
+ from lmcache.tools.cache_simulator.gen_bench_dataset import (
36
+ add_gen_dataset_arguments,
37
+ )
38
+ from lmcache.tools.cache_simulator.plot_hit_rate import add_sweep_arguments
39
+ from lmcache.tools.cache_simulator.simulator import add_simulate_arguments
40
+ except ImportError:
41
+ return
42
+
43
+ cs_parser = subparsers.add_parser(
44
+ "cache-simulator",
45
+ help="Simulate KV-cache token hit rate from lookup-hash JSONL logs.",
46
+ description=(
47
+ "Replay LMCache lookup-hash JSONL logs through an LRU cache "
48
+ "to measure token hit rate."
49
+ ),
50
+ )
51
+ cs_sub = cs_parser.add_subparsers(
52
+ dest="cs_action",
53
+ required=True,
54
+ metavar="{simulate,sweep,gen-dataset}",
55
+ )
56
+
57
+ sim_parser = cs_sub.add_parser(
58
+ "simulate",
59
+ help=(
60
+ "Replay logs at a fixed cache capacity; print a text report "
61
+ "and save a 7-panel statistics PNG."
62
+ ),
63
+ )
64
+ add_simulate_arguments(sim_parser)
65
+ sim_parser.set_defaults(func=execute)
66
+
67
+ sweep_parser = cs_sub.add_parser(
68
+ "sweep",
69
+ help=(
70
+ "Sweep across a range of cache capacities and save a "
71
+ "hit-rate vs capacity PNG."
72
+ ),
73
+ )
74
+ add_sweep_arguments(sweep_parser)
75
+ sweep_parser.set_defaults(func=execute)
76
+
77
+ gen_parser = cs_sub.add_parser(
78
+ "gen-dataset",
79
+ help=(
80
+ "Generate a vllm bench serve custom dataset (JSONL) from "
81
+ "lookup-hash JSONL logs, preserving prefix-sharing structure."
82
+ ),
83
+ )
84
+ add_gen_dataset_arguments(gen_parser)
85
+ gen_parser.set_defaults(func=execute)
86
+
87
+
88
+ def execute(args: argparse.Namespace) -> None:
89
+ """Dispatch to the correct cache-simulator action.
90
+
91
+ Args:
92
+ args: Parsed CLI arguments (includes ``cs_action``).
93
+ """
94
+ # Standard
95
+ import sys
96
+
97
+ # First Party
98
+ # Lazy imports — keeps CLI startup fast (avoids loading matplotlib)
99
+ from lmcache.tools.cache_simulator.plot_hit_rate import run_sweep
100
+ from lmcache.tools.cache_simulator.simulator import run_simulate
101
+
102
+ if args.cs_action == "simulate":
103
+ run_simulate(args)
104
+ elif args.cs_action == "sweep":
105
+ run_sweep(args)
106
+ elif args.cs_action == "gen-dataset":
107
+ # First Party
108
+ from lmcache.tools.cache_simulator.gen_bench_dataset import run_gen_dataset
109
+
110
+ run_gen_dataset(args)
111
+ else:
112
+ print(f"Unknown cache-simulator action: {args.cs_action}", file=sys.stderr)
113
+ sys.exit(1)