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,646 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # Standard
4
+ from dataclasses import dataclass
5
+ from typing import Any, Callable, List, Optional, Sequence, Union
6
+ import threading
7
+ import time
8
+
9
+ # Third Party
10
+ import msgspec
11
+ import torch
12
+ import zmq
13
+
14
+ # First Party
15
+ from lmcache.integration.vllm.utils import get_size_bytes
16
+ from lmcache.logging import init_logger
17
+ from lmcache.utils import (
18
+ STR_DTYPE_TO_TORCH_DTYPE,
19
+ TORCH_DTYPE_TO_STR_DTYPE,
20
+ CacheEngineKey,
21
+ )
22
+ from lmcache.v1.config import LMCacheEngineConfig
23
+ from lmcache.v1.memory_management import (
24
+ MemoryFormat,
25
+ MemoryObj,
26
+ PagedCpuGpuMemoryAllocator,
27
+ )
28
+ from lmcache.v1.metadata import LMCacheMetadata
29
+ from lmcache.v1.rpc_utils import get_zmq_context, get_zmq_socket
30
+ from lmcache.v1.storage_backend.abstract_backend import AllocatorBackendInterface
31
+ from lmcache.v1.transfer_channel import CreateTransferChannel
32
+ from lmcache.v1.transfer_channel.transfer_utils import get_correct_device
33
+
34
+ logger = init_logger(__name__)
35
+
36
+
37
+ class PDMsgBase(msgspec.Struct, tag=True):
38
+ """Base class for all PD-related messages"""
39
+
40
+ pass
41
+
42
+
43
+ class AllocRequest(PDMsgBase):
44
+ """Allocation request message"""
45
+
46
+ keys: list[str] # len(keys) indicates num_chunks
47
+ fmt: int
48
+ shape: list[int] # The shape of the memory objects
49
+ dtype: str
50
+ last_chunk_toks: int
51
+
52
+
53
+ class AllocResponse(PDMsgBase):
54
+ """Allocation response message"""
55
+
56
+ # Indexes (local) of already sent memory objects
57
+ already_sent_indexes: list[int]
58
+
59
+ # Indexes (remote) of allocated memory objects (to be written)
60
+ remote_indexes: list[int]
61
+
62
+
63
+ class ProxyNotif(PDMsgBase):
64
+ req_id: str # The request UUID to notify the proxy
65
+
66
+
67
+ PDMsg = Union[AllocRequest, AllocResponse, ProxyNotif]
68
+
69
+
70
+ @dataclass
71
+ class PDConfig:
72
+ role: str
73
+
74
+ peer_host: str
75
+ peer_init_port: int
76
+ peer_alloc_port: int
77
+
78
+ buffer_size: int
79
+ buffer_device: str
80
+
81
+ proxy_host: Optional[str] = None
82
+ proxy_port: Optional[int] = None
83
+ skip_proxy_notification: bool = False
84
+
85
+ @staticmethod
86
+ def from_cache_engine_config(
87
+ config: LMCacheEngineConfig,
88
+ metadata: LMCacheMetadata,
89
+ tp_rank: int,
90
+ ) -> "PDConfig":
91
+ """Convert the LMCacheEngineConfig to PDConfig"""
92
+
93
+ role = config.pd_role
94
+
95
+ # TODO(Jiayi): Could be both if we want to do dynamic role switch.
96
+ assert role in ["sender", "receiver"], (
97
+ f"Invalid role: {config.pd_role}, must be either sender or receiver"
98
+ )
99
+
100
+ assert config.pd_buffer_size is not None
101
+ assert config.pd_buffer_device is not None
102
+
103
+ if role == "receiver":
104
+ assert config.pd_peer_host is not None
105
+ assert config.pd_peer_init_port is not None
106
+ assert config.pd_peer_alloc_port is not None
107
+ elif role == "sender":
108
+ if not config.pd_skip_proxy_notification:
109
+ assert config.pd_proxy_host is not None
110
+ assert config.pd_proxy_port is not None
111
+
112
+ corrected_device = get_correct_device(
113
+ config.pd_buffer_device, metadata.worker_id
114
+ )
115
+
116
+ if config.pd_peer_alloc_port is not None:
117
+ pd_peer_alloc_port = config.pd_peer_alloc_port[tp_rank]
118
+ else:
119
+ pd_peer_alloc_port = None
120
+
121
+ if config.pd_peer_init_port is not None:
122
+ pd_peer_init_port = config.pd_peer_init_port[tp_rank]
123
+ else:
124
+ pd_peer_init_port = None
125
+
126
+ return PDConfig(
127
+ role=role,
128
+ peer_host=config.pd_peer_host,
129
+ peer_init_port=pd_peer_init_port,
130
+ peer_alloc_port=pd_peer_alloc_port,
131
+ proxy_host=config.pd_proxy_host,
132
+ proxy_port=config.pd_proxy_port,
133
+ buffer_size=config.pd_buffer_size,
134
+ buffer_device=corrected_device,
135
+ skip_proxy_notification=config.pd_skip_proxy_notification,
136
+ )
137
+
138
+
139
+ class PDBackend(AllocatorBackendInterface):
140
+ """
141
+ Implementation of the StorageBackendInterface for PD Disaggregation.
142
+
143
+ At the sender side, it will never save anything but directly write the data
144
+ to the receiver side.
145
+ """
146
+
147
+ def __init__(
148
+ self,
149
+ config: LMCacheEngineConfig,
150
+ metadata: LMCacheMetadata,
151
+ ):
152
+ self.running = True
153
+
154
+ self.tp_rank = metadata.worker_id
155
+
156
+ self.pd_config = PDConfig.from_cache_engine_config(
157
+ config, metadata, self.tp_rank
158
+ )
159
+
160
+ self.corrected_device = get_correct_device(
161
+ config.pd_buffer_device,
162
+ metadata.worker_id,
163
+ )
164
+
165
+ # NOTE(Jiayi): sender/prefiller will not use this pool;
166
+ # only receiver/decoder will.
167
+ self.data: dict[CacheEngineKey, MemoryObj] = {}
168
+ self.data_lock = threading.Lock()
169
+
170
+ self.memory_allocator = self.initialize_allocator(config, metadata)
171
+ assert isinstance(self.memory_allocator, PagedCpuGpuMemoryAllocator)
172
+
173
+ # TODO(Jiayi): add async zmq context if we want better asynchrony.
174
+ self.zmq_context = get_zmq_context(use_asyncio=False)
175
+ self.running_threads: list[threading.Thread] = []
176
+ self.side_channels: list[zmq.Socket] = []
177
+
178
+ # Initialize transfer channel
179
+ peer_init_url = None
180
+ self.local_id = ""
181
+ # TODO(Jiayi): both sender and receiver have to have
182
+ # peer_init_url if they want to do instance flip.
183
+ if self.pd_config.peer_init_port is not None:
184
+ peer_init_url = (
185
+ f"{self.pd_config.peer_host}:{self.pd_config.peer_init_port}"
186
+ )
187
+ self.local_id = self.pd_config.peer_host + str(
188
+ self.pd_config.peer_init_port
189
+ )
190
+
191
+ allocator = (
192
+ self.memory_allocator.cpu_allocator
193
+ if self.corrected_device == "cpu"
194
+ else self.memory_allocator.gpu_allocator
195
+ )
196
+ self.transfer_channel = CreateTransferChannel(
197
+ async_mode=False,
198
+ channel_type=config.transfer_channel,
199
+ role=self.pd_config.role,
200
+ buffer_ptr=allocator.buffer_ptr,
201
+ buffer_size=allocator.buffer_size,
202
+ align_bytes=allocator.align_bytes,
203
+ tp_rank=self.tp_rank,
204
+ peer_init_url=peer_init_url,
205
+ backends=config.nixl_backends,
206
+ device=self.corrected_device,
207
+ )
208
+
209
+ if self.pd_config.role == "sender":
210
+ self._init_sender()
211
+ self.initialized_peers: set[str] = set()
212
+ self.mem_alloc_sockets: dict[str, zmq.Socket] = {}
213
+ elif self.pd_config.role == "receiver":
214
+ self._init_receiver()
215
+ else:
216
+ raise ValueError("Invalid PD role.")
217
+
218
+ self.full_chunk_size_bytes = config.chunk_size
219
+
220
+ def __str__(self):
221
+ return self.__class__.__name__
222
+
223
+ def initialize_allocator(
224
+ self, config: LMCacheEngineConfig, metadata: LMCacheMetadata
225
+ ) -> PagedCpuGpuMemoryAllocator:
226
+ if self.corrected_device != "cpu":
227
+ logger.info(f"Setting cuda device to {self.corrected_device} ")
228
+ torch.cuda.set_device(self.corrected_device)
229
+
230
+ paged_mem_allocator = PagedCpuGpuMemoryAllocator()
231
+
232
+ init_func = (
233
+ paged_mem_allocator.init_cpu_memory_allocator
234
+ if self.corrected_device == "cpu"
235
+ else paged_mem_allocator.init_gpu_memory_allocator
236
+ )
237
+
238
+ # Calculate the chunk size (align_bytes) and align buffer size
239
+ shapes = [torch.Size(metadata.kv_shape)]
240
+ dtypes = [metadata.kv_dtype]
241
+ chunk_size_bytes = get_size_bytes(shapes, dtypes)
242
+ origin_buffer_size = config.pd_buffer_size
243
+ aligned_buffer_size = origin_buffer_size // chunk_size_bytes * chunk_size_bytes
244
+
245
+ if aligned_buffer_size == 0 and origin_buffer_size > 0:
246
+ raise ValueError(
247
+ f"pd_buffer_size ({origin_buffer_size}) is smaller than a "
248
+ f"single chunk ({chunk_size_bytes}), resulting in an aligned "
249
+ f"buffer of size 0. Please increase pd_buffer_size to be at "
250
+ f"least {chunk_size_bytes}."
251
+ )
252
+
253
+ if aligned_buffer_size != origin_buffer_size:
254
+ logger.info(
255
+ f"Auto align pd_buffer_size, origin: {origin_buffer_size}, "
256
+ f"aligned: {aligned_buffer_size}, chunk size: {chunk_size_bytes}. "
257
+ f"The remaining {origin_buffer_size - aligned_buffer_size} bytes "
258
+ f"will not be allocated."
259
+ )
260
+
261
+ init_func(
262
+ aligned_buffer_size,
263
+ shapes,
264
+ dtypes,
265
+ MemoryFormat.KV_2LTD, # TODO: remove this hardcode
266
+ )
267
+
268
+ return paged_mem_allocator
269
+
270
+ def get_memory_allocator(self) -> PagedCpuGpuMemoryAllocator:
271
+ return self.memory_allocator
272
+
273
+ def get_allocator_backend(self):
274
+ return self
275
+
276
+ def allocate(
277
+ self,
278
+ shapes: Union[torch.Size, list[torch.Size]],
279
+ dtypes: Union[torch.dtype, list[torch.dtype]],
280
+ fmt: MemoryFormat = MemoryFormat.KV_2LTD,
281
+ eviction: bool = True,
282
+ busy_loop: bool = True,
283
+ ) -> Optional[MemoryObj]:
284
+ if fmt is None:
285
+ fmt = MemoryFormat.KV_2LTD
286
+ # NOTE: no eviction and busy_loop in PD
287
+ alloc_type = "cpu" if self.corrected_device == "cpu" else "gpu"
288
+ return self.memory_allocator.allocate(
289
+ shapes, dtypes, fmt=fmt, allocator_type=alloc_type
290
+ )
291
+
292
+ # TODO(Jiayi): Please implement batched allocate to reduce memory
293
+ # allocation overhead.
294
+ def batched_allocate(
295
+ self,
296
+ shapes: Union[torch.Size, list[torch.Size]],
297
+ dtypes: Union[torch.dtype, list[torch.dtype]],
298
+ batch_size: int,
299
+ fmt: MemoryFormat = MemoryFormat.KV_2LTD,
300
+ eviction: bool = True,
301
+ busy_loop: bool = True,
302
+ ):
303
+ if fmt is None:
304
+ fmt = MemoryFormat.KV_2LTD
305
+ alloc_type = "cpu" if self.corrected_device == "cpu" else "gpu"
306
+ return self.memory_allocator.batched_allocate(
307
+ shapes, dtypes, batch_size, fmt, allocator_type=alloc_type
308
+ )
309
+
310
+ # NOTE(Jiayi): If two requests have overlapped keys, will
311
+ # the later one cause any problems here?
312
+ def contains(self, key: CacheEngineKey, pin: bool = False) -> bool:
313
+ assert isinstance(key, CacheEngineKey)
314
+ with self.data_lock:
315
+ if mem_obj := self.data.get(key, None):
316
+ if pin:
317
+ mem_obj.ref_count_up()
318
+ return True
319
+ return False
320
+
321
+ def exists_in_put_tasks(self, key: CacheEngineKey) -> bool:
322
+ return False
323
+
324
+ ############################################################
325
+ # Prefiller functions
326
+ ############################################################
327
+ def _init_sender(self):
328
+ if self.pd_config.skip_proxy_notification:
329
+ logger.info(
330
+ "pd_skip_proxy_notification=True, "
331
+ "skipping ZMQ PUSH proxy notification setup. "
332
+ "This mode is for external orchestrators only "
333
+ "(e.g., vLLM Production Stack router). "
334
+ "Do not use with LMCache's built-in disagg proxy."
335
+ )
336
+ self.proxy_side_channel = None
337
+ else:
338
+ proxy_url = f"{self.pd_config.proxy_host}:{self.pd_config.proxy_port}"
339
+ self.proxy_side_channel = get_zmq_socket(
340
+ self.zmq_context,
341
+ proxy_url,
342
+ "tcp",
343
+ zmq.PUSH,
344
+ "connect",
345
+ )
346
+
347
+ def _ensure_peer_connection(
348
+ self,
349
+ receiver_id: str,
350
+ receiver_host: str,
351
+ receiver_init_port: int,
352
+ receiver_alloc_port: int,
353
+ ) -> None:
354
+ if receiver_id in self.initialized_peers:
355
+ return
356
+
357
+ receiver_init_url = f"{receiver_host}:{receiver_init_port}"
358
+ receiver_mem_alloc_url = f"{receiver_host}:{receiver_alloc_port}"
359
+
360
+ # Establish the connection with the receiver/decoder
361
+ self.transfer_channel.lazy_init_peer_connection(
362
+ local_id=self.local_id, peer_id=receiver_id, peer_init_url=receiver_init_url
363
+ )
364
+
365
+ # Set up the memory allocation socket
366
+ mem_alloc_socket = get_zmq_socket(
367
+ self.zmq_context,
368
+ receiver_mem_alloc_url,
369
+ "tcp",
370
+ zmq.REQ,
371
+ "connect",
372
+ )
373
+ self.mem_alloc_sockets[receiver_id] = mem_alloc_socket
374
+
375
+ self.initialized_peers.add(receiver_id)
376
+
377
+ def _remote_allocate(
378
+ self, receiver_id: str, alloc_request: AllocRequest
379
+ ) -> AllocResponse:
380
+ side_channel = self.mem_alloc_sockets[receiver_id]
381
+ side_channel.send(msgspec.msgpack.encode(alloc_request))
382
+ msg = side_channel.recv()
383
+ alloc_response = msgspec.msgpack.decode(msg, type=PDMsg)
384
+
385
+ return alloc_response
386
+
387
+ def _get_remote_alloc_request(
388
+ self, keys: Sequence[CacheEngineKey], mem_objs: List[MemoryObj]
389
+ ) -> AllocRequest:
390
+ """
391
+ Get the allocation request given the keys and memory objects.
392
+
393
+ Let's say there are N memory objects in total.
394
+ We have the following assumptions:
395
+ - The first N-1 memory objects are full chunks, each with
396
+ `full_chunk_size_bytes` tokens.
397
+ - The last memory object can be a partial chunk, which has
398
+ `last_chunk_toks` tokens.
399
+ """
400
+
401
+ fmt = mem_objs[0].meta.fmt
402
+ shape = mem_objs[0].meta.shape
403
+ dtype = TORCH_DTYPE_TO_STR_DTYPE[mem_objs[0].meta.dtype]
404
+ token_dim = fmt.token_dim()
405
+ last_chunk_toks = mem_objs[-1].meta.shape[token_dim]
406
+
407
+ str_keys = [key.to_string() for key in keys]
408
+
409
+ return AllocRequest(
410
+ keys=str_keys,
411
+ fmt=fmt.value,
412
+ shape=list(shape),
413
+ dtype=dtype,
414
+ last_chunk_toks=last_chunk_toks,
415
+ )
416
+
417
+ # TODO(Jiayi): make this async in the future
418
+ def batched_submit_put_task(
419
+ self,
420
+ keys: Sequence[CacheEngineKey],
421
+ memory_objs: List[MemoryObj],
422
+ transfer_spec: Any = None,
423
+ on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
424
+ ) -> None:
425
+ """
426
+ Submit batched put tasks to transfer KV caches to peer.
427
+
428
+ :param on_complete_callback: Optional callback invoked once per key
429
+ after the transfer completes. Callback exceptions are caught and logged.
430
+ """
431
+ for mem_obj in memory_objs:
432
+ mem_obj.ref_count_up()
433
+
434
+ receiver_init_port = transfer_spec.receiver_init_port[self.tp_rank]
435
+ receiver_alloc_port = transfer_spec.receiver_alloc_port[self.tp_rank]
436
+ receiver_id = transfer_spec.receiver_host + str(receiver_init_port)
437
+ receiver_host = transfer_spec.receiver_host
438
+
439
+ self._ensure_peer_connection(
440
+ receiver_id=receiver_id,
441
+ receiver_host=receiver_host,
442
+ receiver_init_port=receiver_init_port,
443
+ receiver_alloc_port=receiver_alloc_port,
444
+ )
445
+
446
+ # Allocate remote memory objects
447
+ alloc_request = self._get_remote_alloc_request(keys, memory_objs)
448
+ alloc_response = self._remote_allocate(receiver_id, alloc_request)
449
+ already_sent_indexes = alloc_response.already_sent_indexes
450
+ remote_indexes = alloc_response.remote_indexes
451
+
452
+ # Filter out already sent memory objects and free them
453
+ mem_objs_to_send = []
454
+ for idx, mem_obj in enumerate(memory_objs):
455
+ if idx in already_sent_indexes:
456
+ mem_obj.ref_count_down()
457
+ else:
458
+ mem_objs_to_send.append(mem_obj)
459
+
460
+ if mem_objs_to_send:
461
+ # TODO(Jiayi): make this decoupled with transfer channel
462
+ # Construct transfer spec
463
+ channel_transfer_spec = {
464
+ "receiver_id": receiver_id,
465
+ "remote_indexes": remote_indexes,
466
+ }
467
+
468
+ # TODO(Jiayi): Consider making this real async
469
+ # Perform the actual transfer
470
+ self.transfer_channel.batched_write(
471
+ objects=mem_objs_to_send,
472
+ transfer_spec=channel_transfer_spec,
473
+ )
474
+
475
+ # TODO(Jiayi): consider moving this to the transfer channel
476
+ # since we might want the transfer to be async.
477
+ for mem_obj in mem_objs_to_send:
478
+ mem_obj.ref_count_down()
479
+ else:
480
+ logger.debug(
481
+ "All memory objects have been already sent to the remote peer."
482
+ " Skipping transfer."
483
+ )
484
+
485
+ if transfer_spec.is_last_prefill:
486
+ # Notify the proxy that the transfer is done
487
+ if self.proxy_side_channel is not None:
488
+ notif_msg = ProxyNotif(req_id=transfer_spec.req_id)
489
+ notif_msg_bytes = msgspec.msgpack.encode(notif_msg)
490
+ self.proxy_side_channel.send(notif_msg_bytes)
491
+
492
+ # Call completion callback for all keys after transfer completes
493
+ if on_complete_callback is not None:
494
+ for key in keys:
495
+ try:
496
+ on_complete_callback(key)
497
+ except Exception as e:
498
+ logger.warning(f"on_complete_callback failed for key {key}: {e}")
499
+
500
+ ############################################################
501
+ # Prefiller functions end
502
+ ############################################################
503
+
504
+ ############################################################
505
+ # Decoder functions
506
+ ############################################################
507
+ def _init_receiver(self):
508
+ # Initialize initialization side channels
509
+ receiver_alloc_url = (
510
+ f"{self.pd_config.peer_host}:{self.pd_config.peer_alloc_port}"
511
+ )
512
+ self.alloc_side_channel = get_zmq_socket(
513
+ self.zmq_context, receiver_alloc_url, "tcp", zmq.REP, "bind"
514
+ )
515
+ self.side_channels.append(self.alloc_side_channel)
516
+
517
+ # Start the memory allocation thread
518
+ self.mem_alloc_thread = threading.Thread(
519
+ target=self._mem_alloc_loop, daemon=True
520
+ )
521
+ self.mem_alloc_thread.start()
522
+ self.running_threads.append(self.mem_alloc_thread)
523
+
524
+ def _allocate_and_put(self, alloc_request: AllocRequest) -> AllocResponse:
525
+ total_allocs = len(alloc_request.keys)
526
+ fmt = MemoryFormat(alloc_request.fmt)
527
+ dtype = STR_DTYPE_TO_TORCH_DTYPE[alloc_request.dtype]
528
+ shape = alloc_request.shape
529
+
530
+ alloc_indexes = []
531
+ already_send_indexes = []
532
+
533
+ for idx, key_str in enumerate(alloc_request.keys):
534
+ key = CacheEngineKey.from_string(key_str)
535
+ if self.contains(key, pin=False):
536
+ already_send_indexes.append(idx)
537
+ continue
538
+
539
+ if idx == total_allocs - 1:
540
+ num_alloc_tokens = alloc_request.last_chunk_toks
541
+ token_dim = fmt.token_dim()
542
+ shape[token_dim] = num_alloc_tokens
543
+ else:
544
+ num_alloc_tokens = self.full_chunk_size_bytes
545
+
546
+ mem_obj = self.allocate(torch.Size(shape), dtype, fmt)
547
+
548
+ # TODO(Jiayi): make busy loop allocation part of
549
+ # memory allocator instead of backend as both PD
550
+ # and CPU offloading might need this.
551
+ wait_time = 0.01
552
+ while mem_obj is None:
553
+ logger.warning(
554
+ "Failed to allocate memory object, retrying...",
555
+ )
556
+ time.sleep(wait_time)
557
+ mem_obj = self.allocate(torch.Size(shape), dtype, fmt)
558
+
559
+ alloc_indexes.append(mem_obj.meta.address)
560
+
561
+ self.put(key, mem_obj)
562
+
563
+ return AllocResponse(
564
+ already_sent_indexes=already_send_indexes, remote_indexes=alloc_indexes
565
+ )
566
+
567
+ def _mem_alloc_loop(self):
568
+ """
569
+ Running the memory allocation loop.
570
+ """
571
+ while self.running:
572
+ try:
573
+ # receive alloc request
574
+ alloc_req_bytes = self.alloc_side_channel.recv()
575
+ alloc_req = msgspec.msgpack.decode(alloc_req_bytes, type=PDMsg)
576
+ assert isinstance(alloc_req, AllocRequest), (
577
+ "The request from the remote peer is not a AllocRequest"
578
+ )
579
+
580
+ # NOTE: it's okay to put the memory objs into the storage backend
581
+ # first because decode vllm will not be able to see the decode
582
+ # request until proxy receives the ack.
583
+ alloc_resp = self._allocate_and_put(alloc_req)
584
+
585
+ # send back response
586
+ self.alloc_side_channel.send(msgspec.msgpack.encode(alloc_resp))
587
+
588
+ except Exception as e:
589
+ logger.error("Failed to process mem alloc loop: %s", str(e))
590
+ if self.running:
591
+ time.sleep(0.01)
592
+
593
+ def put(
594
+ self,
595
+ key: CacheEngineKey,
596
+ mem_obj: MemoryObj,
597
+ ):
598
+ with self.data_lock:
599
+ self.data[key] = mem_obj
600
+
601
+ def get_blocking(self, key: CacheEngineKey) -> Optional[MemoryObj]:
602
+ with self.data_lock:
603
+ # NOTE(Jiayi): we assume that the key must be in local data
604
+ # because we are using a push-based transfer
605
+ mem_obj = self.data.get(key, None)
606
+ assert mem_obj is not None, f"Key {key} not found in local data."
607
+ return mem_obj
608
+
609
+ def remove(
610
+ self,
611
+ key: CacheEngineKey,
612
+ force: bool = True,
613
+ ) -> bool:
614
+ """
615
+ Remove the key from the storage backend.
616
+
617
+ :param key: The key to remove.
618
+ """
619
+ # TODO(Jiayi): The logic here is confusing. Ref count down
620
+ # will be done after this function call in cache engine.
621
+ with self.data_lock:
622
+ if mem_obj := self.data.get(key, None):
623
+ if mem_obj.get_ref_count() == 1:
624
+ del self.data[key]
625
+ return True
626
+ return False
627
+
628
+ ############################################################
629
+ # Decoder functions end
630
+ ############################################################
631
+
632
+ def close(self) -> None:
633
+ """
634
+ Close the storage backend.
635
+ """
636
+ self.running = False
637
+ for thread in self.running_threads:
638
+ thread.join()
639
+ self.transfer_channel.close()
640
+ self.zmq_context.term()
641
+
642
+ def pin(self, key: CacheEngineKey) -> bool:
643
+ return True
644
+
645
+ def unpin(self, key: CacheEngineKey) -> bool:
646
+ return True