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,614 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from dataclasses import dataclass
4
+ from typing import List, Optional, no_type_check
5
+ import asyncio
6
+ import json
7
+ import os
8
+
9
+ # Third Party
10
+ import torch
11
+
12
+ # First Party
13
+ from lmcache.logging import init_logger
14
+ from lmcache.utils import CacheEngineKey
15
+ from lmcache.v1.config import LMCacheEngineConfig
16
+ from lmcache.v1.memory_management import MemoryObj
17
+ from lmcache.v1.protocol import RemoteMetadata
18
+ from lmcache.v1.storage_backend.connector.base_connector import RemoteConnector
19
+ from lmcache.v1.storage_backend.local_cpu_backend import LocalCPUBackend
20
+ from lmcache.v1.system_detection import NUMADetector
21
+
22
+ logger = init_logger(__name__)
23
+
24
+
25
+ @dataclass
26
+ class MooncakeStoreConfig:
27
+ local_hostname: str
28
+ metadata_server: str
29
+ global_segment_size: int
30
+ local_buffer_size: int
31
+ protocol: str
32
+ device_name: str
33
+ master_server_address: str
34
+ transfer_timeout: int
35
+ storage_root_dir: str
36
+ prefer_local_alloc: bool = False
37
+
38
+ @staticmethod
39
+ def from_file(file_path: str) -> "MooncakeStoreConfig":
40
+ """Load the config from a JSON file."""
41
+ with open(file_path) as fin:
42
+ config = json.load(fin)
43
+ # Read Mooncake-specific knob
44
+ prefer_local_alloc = config.get("mooncake_prefer_local_alloc", False)
45
+
46
+ return MooncakeStoreConfig(
47
+ local_hostname=config.get("local_hostname"),
48
+ metadata_server=config.get("metadata_server"),
49
+ global_segment_size=config.get("global_segment_size", 3355443200),
50
+ local_buffer_size=config.get("local_buffer_size", 1073741824),
51
+ protocol=config.get("protocol", "tcp"),
52
+ device_name=config.get("device_name", ""),
53
+ master_server_address=config.get("master_server_address"),
54
+ transfer_timeout=config.get("transfer_timeout", 1),
55
+ storage_root_dir=config.get("storage_root_dir", ""),
56
+ prefer_local_alloc=prefer_local_alloc,
57
+ )
58
+
59
+ @staticmethod
60
+ def load_from_env() -> "MooncakeStoreConfig":
61
+ """Load config from a file specified in the environment variable."""
62
+ config_file_path = os.getenv("MOONCAKE_CONFIG_PATH")
63
+ if config_file_path is None:
64
+ raise ValueError(
65
+ "The environment variable 'MOONCAKE_CONFIG_PATH' is not set."
66
+ )
67
+ return MooncakeStoreConfig.from_file(config_file_path)
68
+
69
+ @staticmethod
70
+ def load_from_lmcache_config(
71
+ config: "LMCacheEngineConfig",
72
+ ) -> "MooncakeStoreConfig":
73
+ """Load config from a file specified in the environment variable."""
74
+ extra_config = config.extra_config
75
+ if extra_config is None:
76
+ raise ValueError("The extra config is not set.")
77
+ # Read Mooncake-specific knob
78
+ prefer_local_alloc = extra_config.get("mooncake_prefer_local_alloc", False)
79
+
80
+ return MooncakeStoreConfig(
81
+ local_hostname=extra_config["local_hostname"],
82
+ metadata_server=extra_config["metadata_server"],
83
+ global_segment_size=extra_config.get("global_segment_size", 3355443200),
84
+ local_buffer_size=extra_config.get("local_buffer_size", 1073741824),
85
+ protocol=extra_config.get("protocol", "tcp"),
86
+ device_name=extra_config.get("device_name", ""),
87
+ master_server_address=extra_config["master_server_address"],
88
+ transfer_timeout=extra_config.get("transfer_timeout", 1),
89
+ storage_root_dir=extra_config.get("storage_root_dir", ""),
90
+ prefer_local_alloc=prefer_local_alloc,
91
+ )
92
+
93
+
94
+ class MooncakestoreConnector(RemoteConnector):
95
+ def __init__(
96
+ self,
97
+ loop: asyncio.AbstractEventLoop,
98
+ local_cpu_backend: LocalCPUBackend,
99
+ lmcache_config: Optional[LMCacheEngineConfig],
100
+ plugin_name: Optional[str] = None,
101
+ ):
102
+ # initialize base class, which includes some common attributes
103
+ super().__init__(local_cpu_backend.config, local_cpu_backend.metadata)
104
+
105
+ try:
106
+ # Third Party
107
+ from mooncake.store import (
108
+ MooncakeDistributedStore,
109
+ ReplicateConfig,
110
+ )
111
+ except ImportError as e:
112
+ raise ImportError(
113
+ "Please install mooncake by following the instructions at "
114
+ "https://github.com/kvcache-ai/Mooncake/blob/main/doc/en/build.md " # noqa: E501
115
+ "to run vLLM with MooncakeConnector."
116
+ ) from e
117
+
118
+ try:
119
+ self.store = MooncakeDistributedStore()
120
+ config_file_path = os.getenv("MOONCAKE_CONFIG_PATH")
121
+ if config_file_path is not None:
122
+ self.config = MooncakeStoreConfig.from_file(config_file_path)
123
+ elif lmcache_config is not None:
124
+ self.config = MooncakeStoreConfig.load_from_lmcache_config(
125
+ lmcache_config
126
+ )
127
+ else:
128
+ raise ValueError("MOONCAKE_CONFIG_PATH/lmcache_config must be provided")
129
+
130
+ logger.info("Mooncake Configuration loaded. config: %s", self.config)
131
+
132
+ # Check if storage_root_dir exists and set environment variable
133
+ if (
134
+ self.config.storage_root_dir is not None
135
+ and self.config.storage_root_dir != ""
136
+ ):
137
+ os.environ["MOONCAKE_STORAGE_ROOT_DIR"] = self.config.storage_root_dir
138
+ logger.info(
139
+ "Set MOONCAKE_STORAGE_ROOT_DIR to: %s", self.config.storage_root_dir
140
+ )
141
+
142
+ logger.info("Setting up Mooncake store with parameters:")
143
+ logger.info(f" local_hostname: {self.config.local_hostname}")
144
+ logger.info(f" metadata_server: {self.config.metadata_server}")
145
+ logger.info(f" global_segment_size: {self.config.global_segment_size}")
146
+ logger.info(f" local_buffer_size: {self.config.local_buffer_size}")
147
+ logger.info(f" protocol: {self.config.protocol}")
148
+ logger.info(f" device_name: {self.config.device_name}")
149
+ logger.info(f" master_server_address: {self.config.master_server_address}")
150
+
151
+ try:
152
+ numa_mapping = getattr(
153
+ local_cpu_backend.memory_allocator, "numa_mapping", None
154
+ )
155
+ if numa_mapping is None and lmcache_config is not None:
156
+ numa_mapping = NUMADetector.get_numa_mapping(lmcache_config)
157
+
158
+ if numa_mapping:
159
+ current_device_id = torch.cuda.current_device()
160
+ gpu_to_numa = getattr(numa_mapping, "gpu_to_numa_mapping", {})
161
+ numa_id = gpu_to_numa.get(current_device_id)
162
+ logger.info(
163
+ f"NUMA mapping detected (pre-Mooncake setup): {gpu_to_numa}"
164
+ )
165
+ try:
166
+ # Third Party
167
+ from mooncake.store import bind_to_numa_node
168
+
169
+ if numa_id is not None:
170
+ bind_to_numa_node(numa_id)
171
+ logger.info(
172
+ f"GPU {current_device_id}, "
173
+ f"NUMA node {numa_id} binding done"
174
+ )
175
+ else:
176
+ logger.info(
177
+ f"NUMA mapping not found for GPU {current_device_id}"
178
+ )
179
+ except ImportError:
180
+ logger.warning(
181
+ "unable to import bind_to_numa_node from mooncake.store"
182
+ )
183
+ else:
184
+ logger.info("NUMA mapping unavailable or disabled")
185
+ except Exception as e:
186
+ logger.warning(
187
+ f"Failed to determine NUMA mapping before Mooncake setup: {e}"
188
+ )
189
+
190
+ self.store.setup(
191
+ self.config.local_hostname,
192
+ self.config.metadata_server,
193
+ self.config.global_segment_size,
194
+ self.config.local_buffer_size,
195
+ self.config.protocol,
196
+ self.config.device_name,
197
+ self.config.master_server_address,
198
+ )
199
+ logger.info("Mooncake store setup completed successfully")
200
+
201
+ except ValueError as e:
202
+ logger.error("Configuration loading failed: %s", e)
203
+ raise
204
+ except Exception as exc:
205
+ logger.error("An error occurred while loading the configuration: %s", exc)
206
+ raise
207
+
208
+ self.loop = loop
209
+ self.local_cpu_backend = local_cpu_backend
210
+ self.registered_buffer_ptr = None
211
+
212
+ # Initialize ReplicateConfig
213
+ self.replica_config = ReplicateConfig()
214
+ self.replica_config.replica_num = 1
215
+
216
+ # Set preferred_segment based on configuration
217
+ if self.config.prefer_local_alloc:
218
+ self.replica_config.preferred_segment = self.store.get_hostname()
219
+
220
+ # Register CPU buffer for zero-copy operations
221
+ self._register_cpu_buffer()
222
+
223
+ logger.info("MooncakeConnector initialized successfully.")
224
+
225
+ def _register_cpu_buffer(self):
226
+ """Register CPU buffer for zero-copy operations."""
227
+ try:
228
+ allocator = self.local_cpu_backend.memory_allocator
229
+ if hasattr(allocator, "pin_allocator") and hasattr(
230
+ allocator.pin_allocator, "buffer"
231
+ ):
232
+ buffer = allocator.pin_allocator.buffer
233
+ self.registered_buffer_ptr = buffer.data_ptr()
234
+ result = self.store.register_buffer(buffer.data_ptr(), buffer.numel())
235
+ if result == 0:
236
+ logger.info(
237
+ f"Registered: {hex(buffer.data_ptr())}, {buffer.numel()} bytes"
238
+ )
239
+ else:
240
+ logger.warning(f"Buffer registration failed: error={result}")
241
+ self.registered_buffer_ptr = None
242
+ else:
243
+ self.registered_buffer_ptr = None
244
+ except Exception as e:
245
+ logger.error(f"Buffer registration error: {e}")
246
+ self.registered_buffer_ptr = None
247
+
248
+ def _unregister_cpu_buffer(self):
249
+ """Unregister CPU buffer."""
250
+ if self.registered_buffer_ptr is not None:
251
+ result = self.store.unregister_buffer(self.registered_buffer_ptr)
252
+ if result == 0:
253
+ logger.info(f"Unregistered buffer: {hex(self.registered_buffer_ptr)}")
254
+ else:
255
+ logger.warning(f"Buffer unregistration failed: error={result}")
256
+ self.registered_buffer_ptr = None
257
+
258
+ def support_batched_get(self) -> bool:
259
+ """
260
+ Check if the connector supports batched get
261
+
262
+ Returns:
263
+ True if batched get is supported, False otherwise
264
+ """
265
+ return True
266
+
267
+ async def exists(self, key: CacheEngineKey) -> bool:
268
+ return self.store.is_exist(key.to_string())
269
+
270
+ def exists_sync(self, key: CacheEngineKey) -> bool:
271
+ return self.store.is_exist(key.to_string())
272
+
273
+ async def batched_get(
274
+ self, keys: List[CacheEngineKey]
275
+ ) -> List[Optional[MemoryObj]]:
276
+ """
277
+ Batch get operation - the only supported get method.
278
+ Uses batch_get_into (with metadata) or batch_get_buffer (without metadata).
279
+ """
280
+ if not keys:
281
+ return []
282
+
283
+ # Check if we have metadata for zero-copy operations
284
+ if self.save_chunk_meta:
285
+ # Use legacy mode with metadata stored in remote
286
+ return await self._batch_get_buffer(keys)
287
+ else:
288
+ # Use optimized mode with local metadata
289
+ return await self._batch_get_into(keys)
290
+
291
+ def support_batched_async_contains(self) -> bool:
292
+ return True
293
+
294
+ async def batched_async_contains(
295
+ self,
296
+ lookup_id: str,
297
+ keys: List[CacheEngineKey],
298
+ pin: bool = False,
299
+ ) -> int:
300
+ num_hit_counts = 0
301
+ for key in keys:
302
+ if not self.store.is_exist(key.to_string()):
303
+ break
304
+ num_hit_counts += 1
305
+ return num_hit_counts
306
+
307
+ async def _batch_get_into(
308
+ self, keys: List[CacheEngineKey]
309
+ ) -> List[Optional[MemoryObj]]:
310
+ """
311
+ Zero-copy batch get using batch_get_into when metadata is available locally.
312
+ This is used when save_chunk_meta=False (metadata not stored remotely).
313
+ """
314
+ if not self.meta_shapes or not self.meta_dtypes or not self.meta_fmt:
315
+ logger.error(
316
+ f"Metadata required for batch_get_into but not available: "
317
+ f"meta_shapes={self.meta_shapes}, "
318
+ f"meta_dtypes={self.meta_dtypes}, "
319
+ f"meta_fmt={self.meta_fmt}"
320
+ )
321
+ return [None] * len(keys)
322
+
323
+ logger.debug(f"Using batch_get_into for {len(keys)} keys (zero-copy mode)")
324
+
325
+ # Reserve a buffer for every requested chunk
326
+ memory_objs: list[Optional[MemoryObj]] = []
327
+ valid_idx: list[int] = []
328
+
329
+ key_strs: list[str] = []
330
+ buffer_ptrs: list[int] = []
331
+ buffer_sizes: list[int] = []
332
+
333
+ for i, _ in enumerate(keys):
334
+ obj = self.local_cpu_backend.allocate(
335
+ self.meta_shapes, self.meta_dtypes, self.meta_fmt
336
+ )
337
+ memory_objs.append(obj)
338
+ if obj is not None and obj.raw_tensor is not None:
339
+ valid_idx.append(i)
340
+
341
+ # Prepare the argument lists for the C++ call
342
+ key_strs.append(keys[i].to_string())
343
+ buffer_ptrs.append(obj.data_ptr)
344
+ buffer_sizes.append(obj.get_size())
345
+
346
+ if not valid_idx:
347
+ logger.warning("Batch-get aborted: unable to allocate any buffers.")
348
+ return [None] * len(keys)
349
+
350
+ try:
351
+ # Single RPC call for multiple chunks
352
+ logger.debug(f"Calling batch_get_into with {len(key_strs)} keys")
353
+ bytes_read_list = await asyncio.to_thread(
354
+ self.store.batch_get_into, key_strs, buffer_ptrs, buffer_sizes
355
+ )
356
+ logger.debug(f"batch_get_into returned: {bytes_read_list}")
357
+
358
+ # Assemble the final result list
359
+ results: list[Optional[MemoryObj]] = [None] * len(keys)
360
+
361
+ for i, n_read in zip(valid_idx, bytes_read_list, strict=False):
362
+ if n_read <= 0:
363
+ logger.warning(
364
+ f"batch_get_into failed for key {keys[i]} (code={n_read})"
365
+ )
366
+ memory_objs[i].ref_count_down() # type: ignore
367
+ continue
368
+
369
+ try:
370
+ results[i] = self.reshape_partial_chunk(
371
+ memory_objs[i], # type: ignore
372
+ n_read,
373
+ )
374
+ except Exception as exc:
375
+ logger.error(f"Reshape failed for key {keys[i]}: {exc}")
376
+ memory_objs[i].ref_count_down() # type: ignore
377
+
378
+ return results
379
+
380
+ except Exception as exc:
381
+ logger.error(f"batch_get_into threw exception: {str(exc)}")
382
+ # Release any buffers we successfully allocated
383
+ for i in valid_idx:
384
+ memory_objs[i].ref_count_down() # type: ignore
385
+ return [None] * len(keys)
386
+
387
+ async def _batch_get_buffer(
388
+ self, keys: List[CacheEngineKey]
389
+ ) -> List[Optional[MemoryObj]]:
390
+ """
391
+ Batch get using batch_get_buffer when metadata is stored remotely.
392
+ This is used when save_chunk_meta=True (metadata stored with data).
393
+ """
394
+ key_strs = [key.to_string() for key in keys]
395
+
396
+ try:
397
+ buffers = await asyncio.to_thread(self.store.batch_get_buffer, key_strs)
398
+ except Exception as e:
399
+ logger.error(f"batch_get_buffer failed: {str(e)}")
400
+ return [None] * len(keys)
401
+
402
+ results: list[Optional[MemoryObj]] = []
403
+ for i, buffer in enumerate(buffers):
404
+ if buffer is None:
405
+ logger.warning(f"Buffer {i} is None for key {key_strs[i]}")
406
+ results.append(None)
407
+ continue
408
+ try:
409
+ memory_obj = self._process_buffer_with_metadata(buffer)
410
+ results.append(memory_obj)
411
+ except Exception as e:
412
+ logger.error(
413
+ f"Failed to process buffer {i} for key {key_strs[i]}: {str(e)}"
414
+ )
415
+ results.append(None)
416
+ return results
417
+
418
+ async def get(self, key: CacheEngineKey) -> Optional[MemoryObj]:
419
+ """
420
+ Single get method - NOT SUPPORTED.
421
+ Use batched_get instead for all operations.
422
+ """
423
+ logger.error("Single get operation is not supported. Use batched_get instead.")
424
+ raise NotImplementedError(
425
+ "Single get is not supported. Use batched_get([key]) instead."
426
+ )
427
+
428
+ def _process_buffer_with_metadata(self, buffer: bytes) -> Optional[MemoryObj]:
429
+ """
430
+ Process buffer that contains metadata + data.
431
+ Used when save_chunk_meta=True (metadata stored remotely).
432
+ """
433
+ retrieved_view = memoryview(buffer)
434
+ metadata_bytes = retrieved_view[: self.remote_metadata_bytes]
435
+ if metadata_bytes is None or len(metadata_bytes) != self.remote_metadata_bytes:
436
+ return None
437
+
438
+ metadata = RemoteMetadata.deserialize(metadata_bytes)
439
+
440
+ memory_obj = self.local_cpu_backend.allocate(
441
+ metadata.shapes,
442
+ metadata.dtypes,
443
+ metadata.fmt,
444
+ )
445
+ assert len(retrieved_view) == metadata.length + self.remote_metadata_bytes
446
+
447
+ if memory_obj is None:
448
+ logger.warning("Failed to allocate memory during remote receive")
449
+ return None
450
+
451
+ if memory_obj.raw_tensor is not None:
452
+ temp_tensor = torch.frombuffer(
453
+ buffer,
454
+ dtype=torch.uint8,
455
+ offset=self.remote_metadata_bytes,
456
+ count=metadata.length,
457
+ )
458
+
459
+ memory_obj.raw_tensor.copy_(temp_tensor)
460
+ return memory_obj
461
+ else:
462
+ return None
463
+
464
+ async def put(self, key: CacheEngineKey, memory_obj: MemoryObj):
465
+ """
466
+ Put operation with metadata-consistent handling.
467
+ Uses put_from (without metadata) or
468
+ put_parts (with metadata) to match get behavior.
469
+ """
470
+ key_str = key.to_string()
471
+
472
+ # Check metadata handling mode to match get behavior
473
+ if self.save_chunk_meta:
474
+ # Use put_parts with metadata stored remotely
475
+ await self._put_with_metadata(key_str, memory_obj)
476
+ else:
477
+ # Use put_from without metadata (zero-copy)
478
+ await self._put_without_metadata(key_str, memory_obj)
479
+
480
+ def support_batched_put(self) -> bool:
481
+ return True
482
+
483
+ async def batched_put(
484
+ self,
485
+ keys: List[CacheEngineKey],
486
+ memory_objs: List[MemoryObj],
487
+ ):
488
+ """
489
+ Batched put with clear split by metadata mode.
490
+ - save_chunk_meta False: use Mooncake's batch_put_from (zero-copy).
491
+ - save_chunk_meta True: no batch API; fall back to sequential put_parts.
492
+ """
493
+ if not keys:
494
+ return
495
+
496
+ if self.save_chunk_meta:
497
+ await self._batched_put_with_metadata(keys, memory_objs)
498
+ else:
499
+ await self._batched_put_zero_copy(keys, memory_objs)
500
+
501
+ async def _batched_put_zero_copy(
502
+ self,
503
+ keys: List[CacheEngineKey],
504
+ memory_objs: List[MemoryObj],
505
+ ) -> None:
506
+ key_strs = [k.to_string() for k in keys]
507
+ buffer_ptrs: list[int] = []
508
+ buffer_sizes: list[int] = []
509
+ for obj in memory_objs:
510
+ assert obj.raw_tensor is not None
511
+ buffer_ptrs.append(obj.data_ptr)
512
+ buffer_sizes.append(obj.get_size())
513
+
514
+ try:
515
+ await asyncio.wait_for(
516
+ asyncio.to_thread(
517
+ self.store.batch_put_from,
518
+ key_strs,
519
+ buffer_ptrs,
520
+ buffer_sizes,
521
+ self.replica_config,
522
+ ),
523
+ timeout=self.config.transfer_timeout,
524
+ )
525
+ except asyncio.TimeoutError:
526
+ logger.warning(
527
+ "Timeout during batch_put_from; some decoders may redo prefill."
528
+ )
529
+
530
+ async def _batched_put_with_metadata(
531
+ self,
532
+ keys: List[CacheEngineKey],
533
+ memory_objs: List[MemoryObj],
534
+ ) -> None:
535
+ for key, obj in zip(keys, memory_objs, strict=False):
536
+ await self._put_with_metadata(key.to_string(), obj)
537
+
538
+ async def _put_without_metadata(self, key_str: str, memory_obj: MemoryObj):
539
+ """
540
+ Zero-copy put using put_from when metadata is not stored remotely.
541
+ This is used when save_chunk_meta=False (matches _batch_get_into).
542
+ """
543
+ try:
544
+ assert memory_obj.raw_tensor is not None
545
+ buffer_ptr = memory_obj.data_ptr
546
+ buffer_size = memory_obj.get_size()
547
+
548
+ await asyncio.wait_for(
549
+ asyncio.to_thread(
550
+ self.store.put_from,
551
+ key_str,
552
+ buffer_ptr,
553
+ buffer_size,
554
+ self.replica_config,
555
+ ),
556
+ timeout=self.config.transfer_timeout,
557
+ )
558
+ except asyncio.TimeoutError:
559
+ logger.warning(
560
+ f"Timeout when putting key {key_str} using put_from. "
561
+ "Decode instance may redo prefill."
562
+ )
563
+ except Exception as e:
564
+ logger.error(
565
+ f"Failed to put key {key_str} using put_from: "
566
+ f"{type(e).__name__}: {str(e)}"
567
+ )
568
+ raise
569
+
570
+ async def _put_with_metadata(self, key_str: str, memory_obj: MemoryObj):
571
+ """
572
+ Put using put_parts when metadata is stored remotely.
573
+ This is used when save_chunk_meta=True (matches _batch_get_buffer).
574
+ """
575
+ try:
576
+ # Serialize data and metadata
577
+ kv_bytes = memory_obj.byte_array
578
+ kv_shapes = memory_obj.get_shapes()
579
+ kv_dtypes = memory_obj.get_dtypes()
580
+ memory_format = memory_obj.get_memory_format()
581
+
582
+ metadata_bytes = RemoteMetadata(
583
+ len(kv_bytes), kv_shapes, kv_dtypes, memory_format
584
+ ).serialize()
585
+ assert len(metadata_bytes) == self.remote_metadata_bytes
586
+
587
+ await asyncio.wait_for(
588
+ asyncio.to_thread(
589
+ self.store.put_parts, key_str, metadata_bytes, kv_bytes
590
+ ),
591
+ timeout=self.config.transfer_timeout,
592
+ )
593
+ except asyncio.TimeoutError:
594
+ logger.warning(
595
+ f"Timeout when putting key {key_str} using put_parts. "
596
+ "Decode instance may redo prefill."
597
+ )
598
+ except Exception as e:
599
+ logger.error(
600
+ f"Failed to put key {key_str} using put_parts: "
601
+ f"{type(e).__name__}: {str(e)}"
602
+ )
603
+ raise
604
+
605
+ @no_type_check
606
+ async def list(self) -> List[str]:
607
+ pass
608
+
609
+ async def close(self):
610
+ # Unregister buffer before closing the store
611
+ self._unregister_cpu_buffer()
612
+
613
+ self.store.close()
614
+ logger.info("Closed the mooncake store connection")