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,895 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Annotated, Any, Callable, List, Optional, Tuple
4
+ import asyncio
5
+ import hashlib
6
+ import json
7
+ import traceback
8
+
9
+ # Third Party
10
+ from fastapi import APIRouter, Query
11
+ from starlette.requests import Request
12
+ from starlette.responses import PlainTextResponse
13
+ import torch
14
+
15
+ # First Party
16
+ from lmcache.logging import init_logger
17
+ from lmcache.utils import (
18
+ compress_slot_mapping,
19
+ parse_mixed_slot_mapping,
20
+ )
21
+ from lmcache.v1.cache_engine import LMCacheEngine
22
+
23
+ logger = init_logger(__name__)
24
+
25
+ router = APIRouter()
26
+
27
+
28
+ def _parse_tokens_from_params(
29
+ tokens_mock: Optional[str],
30
+ ) -> Tuple[Optional[List[int]], Optional[dict]]:
31
+ """Parse tokens from input parameters.
32
+
33
+ Args:
34
+ tokens_mock: Two comma-separated numbers specifying start and end of token range
35
+ - Example: "0,100" generates tokens [0, 1, 2, ..., 99]
36
+ - Example: "50,75" generates tokens [50, 51, 52, ..., 74]
37
+
38
+ Returns:
39
+ Tuple of (tokens list, error dict).
40
+ If error dict is not None, tokens will be None.
41
+ """
42
+ # TODO(baoloongmao): Add support for tokens_input parameter to read tokens from file
43
+ if tokens_mock:
44
+ try:
45
+ parts = tokens_mock.split(",")
46
+ if len(parts) != 2:
47
+ raise ValueError("tokens_mock must contain exactly 2 numbers")
48
+ start, end = int(parts[0].strip()), int(parts[1].strip())
49
+ if start >= end:
50
+ raise ValueError("start must be less than end")
51
+ tokens = list(range(start, end))
52
+ return tokens, None
53
+ except ValueError as e:
54
+ return None, {
55
+ "error": "Invalid tokens_mock format",
56
+ "message": f"tokens_mock must be 'start,end': {str(e)}",
57
+ }
58
+ else:
59
+ return None, {
60
+ "error": "Missing parameters",
61
+ "message": "Must specify either tokens_input or tokens_mock",
62
+ }
63
+
64
+
65
+ def _create_error_response(error_info: dict, status_code: int) -> PlainTextResponse:
66
+ """Create a standardized error response.
67
+
68
+ Args:
69
+ error_info: Dictionary containing error information
70
+ status_code: HTTP status code
71
+
72
+ Returns:
73
+ PlainTextResponse with error information
74
+ """
75
+ return PlainTextResponse(
76
+ content=json.dumps(error_info, indent=2),
77
+ media_type="application/json",
78
+ status_code=status_code,
79
+ )
80
+
81
+
82
+ def _check_lmcache_engine(
83
+ request: Request,
84
+ ) -> Tuple[Optional["LMCacheEngine"], Optional[PlainTextResponse]]:
85
+ """Check if LMCache engine is available.
86
+
87
+ Args:
88
+ request: FastAPI request object
89
+
90
+ Returns:
91
+ Tuple of (lmcache_engine, error_response).
92
+ If error_response is not None, engine will be None.
93
+ """
94
+ lmcache_adapter = request.app.state.lmcache_adapter
95
+ lmcache_engine = getattr(lmcache_adapter, "lmcache_engine", None)
96
+ if not lmcache_engine:
97
+ error_info = {
98
+ "error": "LMCache API is unavailable",
99
+ "message": "LMCache engine not configured.",
100
+ }
101
+ return None, _create_error_response(error_info, 503)
102
+ return lmcache_engine, None
103
+
104
+
105
+ def _get_kvcaches_and_device(engine):
106
+ """Get kvcaches and device from engine's gpu_connector.
107
+
108
+ Args:
109
+ engine: LMCache engine instance
110
+
111
+ Returns:
112
+ Tuple of (kvcaches, device).
113
+ kvcaches may be None if not available.
114
+ device defaults to "cpu" if kvcaches not available.
115
+ """
116
+ kvcaches = None
117
+ device = "cpu" # Default device
118
+
119
+ if engine.gpu_connector:
120
+ kvcaches = engine.gpu_connector.kvcaches
121
+ if kvcaches is not None and len(kvcaches) > 0:
122
+ device = kvcaches[0].device
123
+ logger.debug(f"Using kvcaches device: {device}")
124
+ else:
125
+ logger.warning(
126
+ "gpu_connector.kvcaches is None or empty. "
127
+ "Make sure post_init was called with kvcaches."
128
+ )
129
+
130
+ return kvcaches, device
131
+
132
+
133
+ def _compute_tensor_checksum(tensor: torch.Tensor) -> str:
134
+ """Compute MD5 checksum of a tensor."""
135
+ # Move to CPU and convert to bytes for hashing
136
+ # Handle BFloat16 which is not supported by numpy
137
+ if tensor.dtype == torch.bfloat16:
138
+ # Convert bfloat16 to float32 for numpy compatibility
139
+ tensor = tensor.to(torch.float32)
140
+
141
+ tensor_bytes = tensor.detach().cpu().contiguous().numpy().tobytes()
142
+ return hashlib.md5(tensor_bytes).hexdigest()
143
+
144
+
145
+ def _slice_by_slot_dim(
146
+ kv_at_slots: torch.Tensor, start_idx: int, end_idx: int
147
+ ) -> torch.Tensor:
148
+ """Slice tensor by slot dimension based on tensor ndim."""
149
+ if kv_at_slots.ndim == 4:
150
+ # MHA: [2, num_slots, num_heads, head_size]
151
+ return kv_at_slots[:, start_idx:end_idx, :, :]
152
+ elif kv_at_slots.ndim == 3:
153
+ # 4D format: [num_slots, num_heads, head_size]
154
+ return kv_at_slots[start_idx:end_idx, :, :]
155
+ else:
156
+ # MLA: [num_slots, head_size]
157
+ return kv_at_slots[start_idx:end_idx, :]
158
+
159
+
160
+ def _extract_kv_at_slots(
161
+ kv_tensor: torch.Tensor, slot_tensor: torch.Tensor
162
+ ) -> torch.Tensor:
163
+ """Extract KV data at specified slot positions from kv_tensor.
164
+
165
+ Handles different kv_tensor formats:
166
+ - MHA (5D): [2, num_blocks, block_size, num_heads, head_size]
167
+ - MLA (3D): [num_blocks, block_size, head_size]
168
+
169
+ The slot_mapping is calculated as:
170
+ slot_idx = block_id * block_size + block_offset
171
+
172
+ This means we can reshape the tensor to flatten (num_blocks, block_size)
173
+ into a single slot dimension and index directly.
174
+
175
+ Args:
176
+ kv_tensor: The KV cache tensor for a single layer.
177
+ slot_tensor: Tensor of slot indices to extract.
178
+
179
+ Returns:
180
+ Tensor with KV data at the specified slots.
181
+ - MHA: shape [2, num_slots, num_heads, head_size]
182
+ - MLA: shape [num_slots, head_size]
183
+ """
184
+ ndim = kv_tensor.ndim
185
+
186
+ if ndim == 5:
187
+ # MHA format: [2, num_blocks, block_size, num_heads, head_size]
188
+ # Reshape to [2, num_blocks * block_size, num_heads, head_size]
189
+ # then index by slot_tensor on dimension 1
190
+ kv_2d = 2
191
+ num_heads = kv_tensor.shape[3]
192
+ head_size = kv_tensor.shape[4]
193
+ kv_reshaped = kv_tensor.reshape(kv_2d, -1, num_heads, head_size)
194
+ return kv_reshaped[:, slot_tensor, :, :]
195
+ elif ndim == 3:
196
+ # MLA format: [num_blocks, block_size, head_size]
197
+ # Reshape to [num_blocks * block_size, head_size]
198
+ head_size = kv_tensor.shape[2]
199
+ kv_reshaped = kv_tensor.reshape(-1, head_size)
200
+ return kv_reshaped[slot_tensor, :]
201
+ elif ndim == 4:
202
+ # Alternative format: [num_blocks, block_size, num_heads, head_size]
203
+ # (used in some test cases)
204
+ num_heads = kv_tensor.shape[2]
205
+ head_size = kv_tensor.shape[3]
206
+ kv_reshaped = kv_tensor.reshape(-1, num_heads, head_size)
207
+ return kv_reshaped[slot_tensor, :, :]
208
+ else:
209
+ # Fallback: try the original approach
210
+ logger.warning(
211
+ "Unknown kv_tensor ndim=%d, shape=%s. Using fallback indexing.",
212
+ ndim,
213
+ kv_tensor.shape,
214
+ )
215
+ return kv_tensor.view(-1, *kv_tensor.shape[2:])[slot_tensor]
216
+
217
+
218
+ def compute_kvcache_checksums(
219
+ lmcache_adapter,
220
+ slot_indices: list[int],
221
+ chunk_size: Optional[int] = None,
222
+ layerwise: bool = False,
223
+ ) -> Optional[dict[str, Any]]:
224
+ """Compute MD5 checksums for kvcaches at specified slot positions.
225
+
226
+ This method is used by the kvcache check API to verify that stored
227
+ and retrieved kvcaches are identical.
228
+
229
+ The slot_mapping is calculated in vllm_v1_adapter.py as:
230
+ slot_idx = block_id * block_size + block_offset
231
+
232
+ For vLLM kv_cache formats:
233
+ - MHA (5D): [2, num_blocks, block_size, num_heads, head_size]
234
+ - MLA (3D): [num_blocks, block_size, head_size]
235
+
236
+ Args:
237
+ lmcache_adapter: The LMCache adapter containing kv_caches.
238
+ slot_indices: List of slot indices to compute checksums for.
239
+ chunk_size: Optional chunk size for computing per-chunk checksums.
240
+ If provided, will compute checksums for each chunk.
241
+ layerwise: If True, output per-layer checksums for each chunk.
242
+ If False (default), output one checksum per chunk (all layers combined).
243
+
244
+ Returns:
245
+ Dictionary containing:
246
+ - 'chunk_checksums': (if layerwise=True) dict mapping layer names to
247
+ list of per-chunk checksums
248
+ - 'chunk_checksums': (if layerwise=False) list of checksums, one per chunk
249
+ (each checksum covers all layers for that chunk)
250
+ Returns None if kv_caches is not available.
251
+ """
252
+ if not lmcache_adapter.kv_caches:
253
+ logger.warning("kv_caches is empty, cannot compute checksums")
254
+ return None
255
+
256
+ if chunk_size is None or chunk_size <= 0:
257
+ return {"chunk_checksums": [], "chunk_size": chunk_size, "num_chunks": 0}
258
+
259
+ num_slots = len(slot_indices)
260
+ num_chunks = (num_slots + chunk_size - 1) // chunk_size
261
+
262
+ # Pre-extract all layer data at slot positions
263
+ layer_data_at_slots: dict[str, torch.Tensor] = {}
264
+ for layer_name, kv_tensor in lmcache_adapter.kv_caches.items():
265
+ try:
266
+ slot_tensor = torch.tensor(
267
+ slot_indices, dtype=torch.long, device=kv_tensor.device
268
+ )
269
+ layer_data_at_slots[layer_name] = _extract_kv_at_slots(
270
+ kv_tensor, slot_tensor
271
+ )
272
+ except Exception as e:
273
+ logger.error("Failed to extract data for layer %s: %s", layer_name, str(e))
274
+ layer_data_at_slots[layer_name] = None # type: ignore
275
+
276
+ if layerwise:
277
+ # Output per-layer checksums for each chunk: {layer_name: [checksum1, ...]}
278
+ chunk_checksums: dict[str, list[str]] = {}
279
+ for layer_name, kv_at_slots in layer_data_at_slots.items():
280
+ if kv_at_slots is None:
281
+ chunk_checksums[layer_name] = ["error"] * num_chunks
282
+ continue
283
+ chunk_checksum_list: list[str] = []
284
+ for chunk_idx in range(num_chunks):
285
+ start_idx = chunk_idx * chunk_size
286
+ end_idx = min(start_idx + chunk_size, num_slots)
287
+ chunk_data = _slice_by_slot_dim(kv_at_slots, start_idx, end_idx)
288
+ chunk_checksum_list.append(_compute_tensor_checksum(chunk_data))
289
+ chunk_checksums[layer_name] = chunk_checksum_list
290
+ return {
291
+ "chunk_checksums": chunk_checksums,
292
+ "chunk_size": chunk_size,
293
+ "num_chunks": num_chunks,
294
+ }
295
+ else:
296
+ # Output one checksum per chunk (all layers combined): [checksum1, ...]
297
+ chunk_checksums_list: list[str] = []
298
+ for chunk_idx in range(num_chunks):
299
+ start_idx = chunk_idx * chunk_size
300
+ end_idx = min(start_idx + chunk_size, num_slots)
301
+ md5_hash = hashlib.md5()
302
+ for layer_name in sorted(layer_data_at_slots.keys()):
303
+ kv_at_slots = layer_data_at_slots[layer_name]
304
+ if kv_at_slots is None:
305
+ continue
306
+ chunk_data = _slice_by_slot_dim(kv_at_slots, start_idx, end_idx)
307
+ md5_hash.update(_compute_tensor_checksum(chunk_data).encode())
308
+ chunk_checksums_list.append(md5_hash.hexdigest())
309
+ return {
310
+ "chunk_checksums": chunk_checksums_list,
311
+ "chunk_size": chunk_size,
312
+ "num_chunks": num_chunks,
313
+ }
314
+
315
+
316
+ @router.delete("/cache/clear")
317
+ async def clear(
318
+ request: Request,
319
+ locations: Annotated[Optional[List[str]], Query()] = None,
320
+ request_configs: Optional[dict] = None,
321
+ ):
322
+ """Clear cached data from the LMCache engine.
323
+
324
+ This endpoint provides a way to clear cached KV (Key-Value) data from the
325
+ LMCache engine. It can clear all cached data or selectively clear data
326
+ from specific storage locations.
327
+
328
+ Args:
329
+ request (Request): The FastAPI request object containing application state.
330
+ locations (Optional[List[str]], optional): List of storage backend locations
331
+ to clear cache from. If None, clears from all available locations.
332
+ Common values include ["LocalCPUBackend", "LocalDiskBackend"].
333
+ Defaults to None.
334
+ request_configs (Optional[dict], optional): Additional configuration
335
+ parameters for the clear operation. Currently unused but reserved
336
+ for future extensions. Defaults to None.
337
+
338
+ Returns:
339
+ PlainTextResponse: A plain text response
340
+
341
+ Example:
342
+ Clear all cached data:
343
+ ```bash
344
+ curl -X DELETE "http://localhost:8000/cache/clear"
345
+ # Response: {"status": "success", "num_removed": 10,
346
+ # "locations": null, "request_configs": null}
347
+ ```
348
+
349
+ Clear cache from specific locations:
350
+ ```bash
351
+ curl -X DELETE "http://localhost:8000/cache/clear?locations=LocalCPUBackend&locations=LocalDiskBackend"
352
+ # Response: {"status": "success", "num_removed": 5,
353
+ # "locations": ["LocalCPUBackend", "LocalDiskBackend"],
354
+ # "request_configs": null}
355
+ ```
356
+ """
357
+ try:
358
+ lmcache_engine, error_response = _check_lmcache_engine(request)
359
+ if error_response:
360
+ return error_response
361
+
362
+ assert lmcache_engine is not None
363
+ num_removed = lmcache_engine.clear( # type: ignore[attr-defined]
364
+ locations=locations, request_configs=request_configs
365
+ )
366
+ success_info = {
367
+ "status": "success",
368
+ "num_removed": num_removed,
369
+ }
370
+ return PlainTextResponse(
371
+ content=json.dumps(success_info, indent=2),
372
+ media_type="application/json",
373
+ )
374
+ except Exception as e:
375
+ error_info = {"error": "Failed to clear cache", "message": str(e)}
376
+ return _create_error_response(error_info, 500)
377
+
378
+
379
+ def _process_tokens_request(
380
+ request: Request,
381
+ tokens_mock: Optional[str],
382
+ ) -> Tuple[Optional[object], Optional[List[int]], Optional[PlainTextResponse]]:
383
+ """Process tokens request and validate parameters.
384
+
385
+ Args:
386
+ request: FastAPI request object
387
+ tokens_mock: Mock token range specification
388
+
389
+ Returns:
390
+ Tuple of (lmcache_engine, tokens, error_response).
391
+ If error_response is not None, the other values will be None.
392
+ """
393
+ lmcache_engine, error_response = _check_lmcache_engine(request)
394
+ if error_response:
395
+ return None, None, error_response
396
+
397
+ tokens, error_info = _parse_tokens_from_params(tokens_mock)
398
+ if error_info:
399
+ status_code = 400 if error_info["error"] != "File not found" else 404
400
+ return None, None, _create_error_response(error_info, status_code)
401
+
402
+ return lmcache_engine, tokens, None
403
+
404
+
405
+ def _execute_cache_operation(
406
+ operation_name: str,
407
+ operation_func: Callable,
408
+ lmcache_engine: object,
409
+ tokens: List[int],
410
+ ) -> PlainTextResponse:
411
+ """Execute a cache operation and return standardized response.
412
+
413
+ Args:
414
+ operation_name: Name of the operation for error messages
415
+ operation_func: Function to execute the operation
416
+ lmcache_engine: LMCache engine instance
417
+ tokens: List of token IDs
418
+
419
+ Returns:
420
+ PlainTextResponse with operation result
421
+ """
422
+ try:
423
+ result = operation_func(lmcache_engine, tokens)
424
+ success_info = {
425
+ "status": "success",
426
+ "num_tokens": len(tokens),
427
+ }
428
+ if result is not None:
429
+ success_info.update(result)
430
+ return PlainTextResponse(
431
+ content=json.dumps(success_info, indent=2),
432
+ media_type="application/json",
433
+ )
434
+ except Exception as e:
435
+ # Log the full traceback for debugging
436
+ tb_str = traceback.format_exc()
437
+ logger.error(f"Failed to {operation_name}: {str(e)}\\n{tb_str}")
438
+
439
+ # Include more detailed error info in response
440
+ error_message = str(e) if str(e) else f"Exception type: {type(e).__name__}"
441
+ error_info = {
442
+ "error": f"Failed to {operation_name}",
443
+ "message": error_message,
444
+ "exception_type": type(e).__name__,
445
+ }
446
+ return _create_error_response(error_info, 500)
447
+
448
+
449
+ @router.post("/cache/store")
450
+ async def store(
451
+ request: Request,
452
+ tokens_mock: Optional[str] = None,
453
+ ):
454
+ """Store KV cache data into the LMCache engine.
455
+
456
+ This endpoint provides a way to store KV cache data by generating mock tokens.
457
+
458
+ Args:
459
+ request (Request): The FastAPI request object containing application state.
460
+ tokens_mock (Optional[str], optional): Two comma-separated numbers specifying
461
+ the start and end of a token range. Example: "0,100" generates tokens
462
+ from 0 to 99. Defaults to None.
463
+
464
+ Returns:
465
+ PlainTextResponse: A plain text response with operation status
466
+
467
+ Example:
468
+ Store with mock tokens:
469
+ ```bash
470
+ curl -X POST "http://localhost:8000/cache/store?tokens_mock=0,100"
471
+ # Response: {"status": "success", "num_tokens": 100}
472
+ ```
473
+ """
474
+ lmcache_engine, tokens, error_response = _process_tokens_request(
475
+ request, tokens_mock
476
+ )
477
+ if error_response:
478
+ return error_response
479
+
480
+ assert tokens is not None
481
+ assert lmcache_engine is not None
482
+
483
+ def _store_operation(engine, token_list):
484
+ # Get kvcaches and device using the shared function
485
+ kvcaches, device = _get_kvcaches_and_device(engine)
486
+
487
+ # Create slot mapping for the tokens
488
+ slot_mapping = torch.arange(len(token_list), dtype=torch.long, device=device)
489
+
490
+ logger.debug(
491
+ f"Storing {len(token_list)} tokens with slot_mapping on device {device}"
492
+ )
493
+
494
+ engine.store(
495
+ req_id="cache_api_store",
496
+ tokens=token_list,
497
+ slot_mapping=slot_mapping,
498
+ kvcaches=kvcaches,
499
+ )
500
+ return None
501
+
502
+ return _execute_cache_operation(
503
+ "store cache", _store_operation, lmcache_engine, tokens
504
+ )
505
+
506
+
507
+ @router.post("/cache/retrieve")
508
+ async def retrieve(
509
+ request: Request,
510
+ tokens_mock: Optional[str] = None,
511
+ ):
512
+ """Retrieve KV cache data from the LMCache engine.
513
+
514
+ This endpoint provides a way to retrieve KV cache data by generating mock tokens.
515
+
516
+ Args:
517
+ request (Request): The FastAPI request object containing application state.
518
+ tokens_mock (Optional[str], optional): Two comma-separated numbers specifying
519
+ the start and end of a token range. Example: "0,100" generates tokens
520
+ from 0 to 99. Defaults to None.
521
+
522
+ Returns:
523
+ PlainTextResponse: A plain text response with operation status
524
+
525
+ Example:
526
+ Retrieve with mock tokens:
527
+ ```bash
528
+ curl -X POST "http://localhost:8000/cache/retrieve?tokens_mock=0,100"
529
+ # Response: {"status": "success", "num_tokens": 100, "num_retrieved": 80}
530
+ ```
531
+ """
532
+ lmcache_engine, tokens, error_response = _process_tokens_request(
533
+ request, tokens_mock
534
+ )
535
+ if error_response:
536
+ return error_response
537
+
538
+ assert tokens is not None
539
+ assert lmcache_engine is not None
540
+
541
+ def _retrieve_operation(engine, token_list):
542
+ # Get kvcaches and device using the shared function
543
+ kvcaches, device = _get_kvcaches_and_device(engine)
544
+
545
+ # Create slot_mapping for retrieve operation
546
+ slot_mapping = torch.arange(len(token_list), dtype=torch.long, device=device)
547
+
548
+ logger.debug(
549
+ "Retrieving %d tokens with slot_mapping on device %s",
550
+ len(token_list),
551
+ device,
552
+ )
553
+
554
+ ret_mask = engine.retrieve(
555
+ req_id="cache_api_retrieve",
556
+ tokens=token_list,
557
+ slot_mapping=slot_mapping,
558
+ kvcaches=kvcaches,
559
+ )
560
+ num_retrieved = int(ret_mask.sum().item())
561
+ return {"num_retrieved": num_retrieved}
562
+
563
+ return _execute_cache_operation(
564
+ "retrieve cache", _retrieve_operation, lmcache_engine, tokens
565
+ )
566
+
567
+
568
+ @router.get("/cache/kvcache/check")
569
+ async def kvcache_check(
570
+ request: Request,
571
+ slot_mapping: Optional[str] = None,
572
+ chunk_size: Optional[int] = None,
573
+ layerwise: bool = False,
574
+ ):
575
+ """Compute checksum for kvcaches at specified slot_mapping positions.
576
+
577
+ This endpoint is used to verify that stored and retrieved kvcaches are identical.
578
+
579
+ Args:
580
+ request (Request): The FastAPI request object containing application state.
581
+ slot_mapping (Optional[str], optional): Slot indices in comma-separated format,
582
+ supports single numbers and range expressions.
583
+ Examples: "0,1,2,3", "1,2,3,[9,12],17,19". Defaults to None.
584
+ chunk_size (Optional[int], optional): Chunk size for computing checksums.
585
+ Each chunk contains `chunk_size` slots. Required parameter.
586
+ layerwise (bool, optional): If True, output per-layer checksums for each chunk.
587
+ If False (default), output one checksum per chunk (all layers combined).
588
+
589
+ Returns:
590
+ PlainTextResponse: A JSON response containing checksums.
591
+
592
+ Example:
593
+ ```bash
594
+ # layerwise=false (default): one checksum per chunk (all layers combined)
595
+ curl -X GET "http://localhost:8000/cache/kvcache/check?slot_mapping=0,1,2,3&chunk_size=2"
596
+ # Response: {
597
+ # "status": "success",
598
+ # "slot_mapping_ranges": [[0, 3]],
599
+ # "chunk_size": 2,
600
+ # "num_chunks": 2,
601
+ # "chunk_checksums": ["checksum_chunk0", "checksum_chunk1"],
602
+ # "layerwise": false
603
+ # }
604
+
605
+ # layerwise=true: per-layer checksums for each chunk
606
+ curl -X GET "http://localhost:8000/cache/kvcache/check?slot_mapping=0,1,2,3&chunk_size=2&layerwise=true"
607
+ # Response: {
608
+ # "status": "success",
609
+ # "slot_mapping_ranges": [[0, 3]],
610
+ # "chunk_size": 2,
611
+ # "num_chunks": 2,
612
+ # "chunk_checksums": {
613
+ # "layer_0": ["checksum_chunk0", "checksum_chunk1"],
614
+ # "layer_1": ["checksum_chunk0", "checksum_chunk1"],
615
+ # },
616
+ # "layerwise": true
617
+ # }
618
+ ```
619
+ """
620
+ try:
621
+ lmcache_adapter = request.app.state.lmcache_adapter
622
+ if not lmcache_adapter:
623
+ return _create_error_response(
624
+ {
625
+ "error": "LMCache adapter unavailable",
626
+ "message": "LMCache adapter not configured.",
627
+ },
628
+ 503,
629
+ )
630
+
631
+ if not slot_mapping:
632
+ return _create_error_response(
633
+ {
634
+ "error": "Missing parameters",
635
+ "message": "slot_mapping parameter is required",
636
+ },
637
+ 400,
638
+ )
639
+
640
+ # Parse slot_mapping from mixed format string
641
+ # (supports single numbers and ranges)
642
+ slot_indices, error_info = parse_mixed_slot_mapping(slot_mapping)
643
+ if error_info:
644
+ return _create_error_response(error_info, 400)
645
+
646
+ # slot_indices is guaranteed to be non-None when error_info is None
647
+ assert slot_indices is not None
648
+
649
+ # Validate slot indices are within valid range
650
+ if lmcache_adapter.kv_caches:
651
+ # Get the first kv_tensor to check dimensions
652
+ first_kv_tensor = next(iter(lmcache_adapter.kv_caches.values()))
653
+ # Calculate total slots: num_blocks * block_size
654
+ # For different formats:
655
+ # - MHA (5D): [2, num_blocks, block_size, num_heads, head_size]
656
+ # - MLA (3D): [num_blocks, block_size, head_size]
657
+ # - 4D: [num_blocks, block_size, num_heads, head_size]
658
+ ndim = first_kv_tensor.ndim
659
+ if ndim == 5:
660
+ # MHA: [2, num_blocks, block_size, num_heads, head_size]
661
+ total_slots = first_kv_tensor.shape[1] * first_kv_tensor.shape[2]
662
+ elif ndim == 3:
663
+ # MLA: [num_blocks, block_size, head_size]
664
+ total_slots = first_kv_tensor.shape[0] * first_kv_tensor.shape[1]
665
+ elif ndim == 4:
666
+ # 4D: [num_blocks, block_size, num_heads, head_size]
667
+ total_slots = first_kv_tensor.shape[0] * first_kv_tensor.shape[1]
668
+ else:
669
+ # Fallback
670
+ reshaped = first_kv_tensor.view(-1, *first_kv_tensor.shape[2:])
671
+ total_slots = reshaped.shape[0]
672
+
673
+ # Check each slot index
674
+ invalid_indices = []
675
+ for slot_idx in slot_indices:
676
+ if slot_idx < 0 or slot_idx >= total_slots:
677
+ invalid_indices.append(slot_idx)
678
+
679
+ if invalid_indices:
680
+ return _create_error_response(
681
+ {
682
+ "error": "Invalid slot indices",
683
+ "message": (
684
+ "Slot indices out of bounds: %s. Valid range: 0 to %d"
685
+ )
686
+ % (invalid_indices, total_slots - 1),
687
+ },
688
+ 400,
689
+ )
690
+ else:
691
+ return _create_error_response(
692
+ {
693
+ "error": "kv_caches not available",
694
+ "message": "kv_caches is empty or not initialized",
695
+ },
696
+ 404,
697
+ )
698
+
699
+ # Validate chunk_size if provided
700
+ if chunk_size is not None and chunk_size <= 0:
701
+ return _create_error_response(
702
+ {
703
+ "error": "Invalid chunk_size",
704
+ "message": "chunk_size must be a positive integer",
705
+ },
706
+ 400,
707
+ )
708
+
709
+ # Get checksums from the adapter asynchronously to not block the loop
710
+ loop = asyncio.get_running_loop()
711
+ checksums_result = await loop.run_in_executor(
712
+ None, # Uses default ThreadPoolExecutor
713
+ lambda: compute_kvcache_checksums(
714
+ lmcache_adapter, slot_indices, chunk_size, layerwise
715
+ ),
716
+ )
717
+
718
+ if checksums_result is None:
719
+ return _create_error_response(
720
+ {
721
+ "error": "Failed to compute checksums",
722
+ "message": "kv_caches not available or empty",
723
+ },
724
+ 500,
725
+ )
726
+
727
+ # Compute slot mapping ranges using compress_slot_mapping
728
+ slot_mapping_ranges = compress_slot_mapping(slot_indices)
729
+
730
+ response_data: dict[str, Any] = {
731
+ "status": "success",
732
+ "slot_mapping_ranges": slot_mapping_ranges,
733
+ }
734
+
735
+ # Include chunk checksums
736
+ response_data["chunk_size"] = checksums_result.get("chunk_size")
737
+ response_data["num_chunks"] = checksums_result.get("num_chunks")
738
+ response_data["chunk_checksums"] = checksums_result.get("chunk_checksums")
739
+ response_data["layerwise"] = layerwise
740
+
741
+ return PlainTextResponse(
742
+ content=json.dumps(response_data, indent=2),
743
+ media_type="application/json",
744
+ )
745
+
746
+ except Exception as e:
747
+ logger.error("Failed to compute kvcache checksums: %s", str(e))
748
+ return _create_error_response(
749
+ {"error": "Failed to compute checksums", "message": str(e)},
750
+ 500,
751
+ )
752
+
753
+
754
+ @router.post("/cache/kvcache/record_slot")
755
+ async def kvcache_record_slot(
756
+ request: Request,
757
+ enabled: Optional[str] = None,
758
+ ):
759
+ """Enable or disable KVCache Check slot_mapping logging.
760
+
761
+ This endpoint controls whether the KVCache Check logs (slot_mapping info)
762
+ are printed when store/retrieve operations are performed.
763
+
764
+ Args:
765
+ request (Request): The FastAPI request object containing application state.
766
+ enabled (Optional[str], optional): "true" to enable logging, "false" to
767
+ disable. Defaults to None.
768
+
769
+ Returns:
770
+ PlainTextResponse: A JSON response containing the current logging status.
771
+
772
+ Example:
773
+ ```bash
774
+ # Enable logging
775
+ curl -X POST "http://localhost:8000/cache/kvcache/record_slot?enabled=true"
776
+
777
+ # Disable logging
778
+ curl -X POST "http://localhost:8000/cache/kvcache/record_slot?enabled=false"
779
+
780
+ # Check current status
781
+ curl -X POST "http://localhost:8000/cache/kvcache/record_slot"
782
+ ```
783
+ """
784
+ try:
785
+ lmcache_adapter = request.app.state.lmcache_adapter
786
+ if not lmcache_adapter:
787
+ return _create_error_response(
788
+ {
789
+ "error": "LMCache adapter unavailable",
790
+ "message": "LMCache adapter not configured.",
791
+ },
792
+ 503,
793
+ )
794
+
795
+ # Get current status from lmcache_engine
796
+ lmcache_engine = lmcache_adapter.lmcache_engine
797
+ current_status = getattr(lmcache_engine, "kvcache_check_log_enabled", False)
798
+
799
+ # Update status if enabled parameter is provided
800
+ if enabled is not None:
801
+ enabled_lower = enabled.lower()
802
+ if enabled_lower == "true":
803
+ lmcache_engine.kvcache_check_log_enabled = True
804
+ current_status = True
805
+ logger.info("KVCache Check logging enabled")
806
+ elif enabled_lower == "false":
807
+ lmcache_engine.kvcache_check_log_enabled = False
808
+ current_status = False
809
+ logger.info("KVCache Check logging disabled")
810
+ else:
811
+ return _create_error_response(
812
+ {
813
+ "error": "Invalid parameter",
814
+ "message": "enabled must be 'true' or 'false'",
815
+ },
816
+ 400,
817
+ )
818
+
819
+ response_data = {
820
+ "status": "success",
821
+ "kvcache_check_log_enabled": current_status,
822
+ }
823
+
824
+ return PlainTextResponse(
825
+ content=json.dumps(response_data, indent=2),
826
+ media_type="application/json",
827
+ )
828
+
829
+ except Exception as e:
830
+ logger.error("Failed to set kvcache record slot status: %s", str(e))
831
+ return _create_error_response(
832
+ {"error": "Failed to set record slot status", "message": str(e)},
833
+ 500,
834
+ )
835
+
836
+
837
+ @router.get("/cache/kvcache/info")
838
+ async def kvcache_info(request: Request):
839
+ """Get information about the current kvcaches.
840
+
841
+ Returns information about the kvcaches structure including layer names,
842
+ shapes, and device information.
843
+
844
+ Args:
845
+ request (Request): The FastAPI request object containing application state.
846
+
847
+ Returns:
848
+ PlainTextResponse: A JSON response containing kvcache information.
849
+ """
850
+ try:
851
+ lmcache_adapter = request.app.state.lmcache_adapter
852
+ if not lmcache_adapter:
853
+ return _create_error_response(
854
+ {
855
+ "error": "LMCache adapter unavailable",
856
+ "message": "LMCache adapter not configured.",
857
+ },
858
+ 503,
859
+ )
860
+
861
+ kv_caches = getattr(lmcache_adapter, "kvcaches", None)
862
+ if not kv_caches:
863
+ return _create_error_response(
864
+ {
865
+ "error": "kv_caches not available",
866
+ "message": "kv_caches is empty or not initialized",
867
+ },
868
+ 404,
869
+ )
870
+
871
+ layers_info: dict = {}
872
+ for layer_name, kv_tensor in kv_caches.items():
873
+ layers_info[layer_name] = {
874
+ "shape": list(kv_tensor.shape),
875
+ "dtype": str(kv_tensor.dtype),
876
+ "device": str(kv_tensor.device),
877
+ }
878
+
879
+ info = {
880
+ "status": "success",
881
+ "num_layers": len(kv_caches),
882
+ "layers": layers_info,
883
+ }
884
+
885
+ return PlainTextResponse(
886
+ content=json.dumps(info, indent=2),
887
+ media_type="application/json",
888
+ )
889
+
890
+ except Exception as e:
891
+ logger.error("Failed to get kvcache info: %s", str(e))
892
+ return _create_error_response(
893
+ {"error": "Failed to get kvcache info", "message": str(e)},
894
+ 500,
895
+ )