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,916 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Copyright 2024-2025 LMCache Authors.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # Standard
16
+ from typing import List, Optional, Union, cast
17
+ import os
18
+
19
+ # Third Party
20
+ import torch
21
+
22
+ # First Party
23
+ from lmcache.logging import init_logger
24
+ from lmcache.utils import EngineType
25
+ from lmcache.v1.gpu_connector.gpu_connectors import (
26
+ GPUConnectorInterface,
27
+ VLLMPagedMemGPUConnectorV2,
28
+ )
29
+ from lmcache.v1.gpu_connector.utils import (
30
+ LayoutHints,
31
+ _get_head_size_view,
32
+ _split_token2d_kv,
33
+ discover_gpu_kv_format,
34
+ get_block_size,
35
+ get_dtype,
36
+ get_head_size,
37
+ get_hidden_dim_size,
38
+ get_num_blocks,
39
+ get_num_heads,
40
+ get_num_layers,
41
+ get_page_buffer_size,
42
+ is_mla,
43
+ )
44
+ from lmcache.v1.memory_management import (
45
+ MemoryAllocatorInterface,
46
+ MemoryFormat,
47
+ MemoryObj,
48
+ )
49
+ from lmcache.v1.metadata import LMCacheMetadata
50
+
51
+ logger = init_logger(__name__)
52
+
53
+ ALLOWED_FORMAT_TRANSITIONS = {
54
+ (None, MemoryFormat.KV_MLA_FMT),
55
+ (MemoryFormat.KV_MLA_FMT, MemoryFormat.KV_MLA_FMT),
56
+ (MemoryFormat.KV_T2D, MemoryFormat.KV_MLA_FMT),
57
+ }
58
+
59
+
60
+ class VLLMPagedMemXPUConnectorV2(VLLMPagedMemGPUConnectorV2):
61
+ """
62
+ The GPU KV cache should be a nested tuple of K and V tensors.
63
+ More specifically, we have:
64
+ - GPUTensor = Tuple[KVLayer, ...]
65
+ - KVLayer = Tuple[Tensor, Tensor]
66
+ - Tensor: [num_blocks, block_size, num_heads, head_size]
67
+
68
+ It will produce / consume memory object with KV_2LTD format
69
+ """
70
+
71
+ def __init__(
72
+ self,
73
+ use_gpu: bool = False,
74
+ **kwargs,
75
+ ):
76
+ self._attributes_initialized = False
77
+ self.kvcaches: Optional[List[torch.Tensor]] = None
78
+ self.use_gpu = use_gpu
79
+
80
+ @classmethod
81
+ def from_metadata(
82
+ cls,
83
+ metadata: LMCacheMetadata,
84
+ use_gpu: bool = False,
85
+ device: Optional[torch.device] = None,
86
+ layout_hints: Optional[LayoutHints] = None,
87
+ ) -> "VLLMPagedMemXPUConnectorV2":
88
+ """Create a connector from LMCacheMetadata.
89
+
90
+ Args:
91
+ metadata: The LMCache engine metadata containing model configuration.
92
+ use_gpu: Whether to use GPU intermediate buffer.
93
+ device: The device to use for the connector.
94
+ layout_hints: Optional hints about KV cache layout from the
95
+ serving engine.
96
+
97
+ Returns:
98
+ A new instance of VLLMPagedMemXPUConnectorV2.
99
+ """
100
+ return cls(
101
+ use_gpu=use_gpu,
102
+ )
103
+
104
+ def to_gpu(self, memory_obj: MemoryObj, start: int, end: int, **kwargs):
105
+ """Expect a kwarg 'kvcaches' which is a nested tuple of K and V tensors.
106
+ The kvcaches should correspond to the "WHOLE token sequence".
107
+
108
+ Note:
109
+ 1. This function expects the 'slot_mapping' is a "full slot mapping"
110
+ where it's length is the same as the whole token sequence.
111
+ 2. In the case that there is prefix caching, slot_mapping will starts
112
+ with -1s until the end of the matched prefix. The start and end
113
+ should NEVER overlap with the prefix caching (which means the
114
+ underlying CUDA kernel will never see -1 in slot_mapping)
115
+
116
+
117
+ :raises ValueError: If 'kvcaches' is not provided in kwargs.
118
+ :raises AssertionError: If the memory object does not have a tensor.
119
+ :raises ValueError: If 'slot_mapping' is not provided in kwargs.
120
+ """
121
+ assert memory_obj.tensor is not None
122
+
123
+ self.initialize_kvcaches_ptr(**kwargs)
124
+
125
+ assert self.kvcaches is not None, (
126
+ "kvcaches should be provided in kwargs or initialized beforehand."
127
+ )
128
+
129
+ if "slot_mapping" not in kwargs:
130
+ raise ValueError("'slot_mapping' should be provided in kwargs.")
131
+
132
+ slot_mapping: torch.Tensor = kwargs["slot_mapping"]
133
+ slices = slot_mapping[start:end]
134
+ self._initialize_attributes(self.kvcaches)
135
+ self._validate_memory_format(memory_obj)
136
+
137
+ if self.use_mla:
138
+ tmp = memory_obj.tensor[0].to(slot_mapping.device)
139
+ total_blocks = self.num_blocks * self.block_size
140
+ for i, kvcache in enumerate(self.kvcaches):
141
+ kvcache.view(total_blocks, self.head_size).index_copy_(
142
+ 0, slices, tmp[i]
143
+ )
144
+ else:
145
+ tmp_k = memory_obj.tensor[0].to(slot_mapping.device)
146
+ tmp_v = memory_obj.tensor[1].to(slot_mapping.device)
147
+ total_blocks = self.num_blocks * self.block_size
148
+ d = self.num_heads * self.head_size
149
+ for i, (kcache, vcache) in enumerate(self.kvcaches):
150
+ kcache.view(total_blocks, d).index_copy_(0, slices, tmp_k[i])
151
+ vcache.view(total_blocks, d).index_copy_(0, slices, tmp_v[i])
152
+
153
+ def from_gpu(self, memory_obj: MemoryObj, start: int, end: int, **kwargs):
154
+ """Expect a kwarg 'kvcaches' which is a nested tuple of K and V tensors.
155
+ The kvcaches should correspond to the "WHOLE token sequence".
156
+
157
+ Will set the memory_obj.metadata.fmt to MemoryFormat.KV_MLA_FMT
158
+ if use_mla is True.
159
+
160
+ Note:
161
+ 1. This function expects the 'slot_mapping' is a "full slot mapping"
162
+ where it's length is the same as the whole token sequence.
163
+ 2. In the case that there is prefix caching, slot_mapping will starts
164
+ with -1s until the end of the matched prefix. The start and end
165
+ should NEVER overlap with the prefix caching (which means the
166
+ underlying CUDA kernel will never see -1 in slot_mapping)
167
+
168
+ :raises ValueError: If 'kvcaches' is not provided in kwargs,
169
+ :raises AssertionError: If the memory object does not have a tensor.
170
+ :raises ValueError: If 'slot_mapping' is not provided in kwargs.
171
+ """
172
+ assert memory_obj.tensor is not None
173
+
174
+ self.initialize_kvcaches_ptr(**kwargs)
175
+ assert self.kvcaches is not None, (
176
+ "kvcaches should be provided in kwargs or initialized beforehand."
177
+ )
178
+
179
+ if "slot_mapping" not in kwargs:
180
+ raise ValueError("'slot_mapping' should be provided in kwargs.")
181
+
182
+ slot_mapping: torch.Tensor = kwargs["slot_mapping"]
183
+ slices = slot_mapping[start:end]
184
+ self._initialize_attributes(self.kvcaches)
185
+ self._validate_memory_format(memory_obj)
186
+
187
+ if self.use_mla:
188
+ total_blocks = self.num_blocks * self.block_size
189
+ tmp = torch.stack(
190
+ [
191
+ kvcache.view(total_blocks, self.head_size).index_select(0, slices)
192
+ for kvcache in self.kvcaches
193
+ ]
194
+ )
195
+ else:
196
+ total_blocks = self.num_blocks * self.block_size
197
+ d = self.num_heads * self.head_size
198
+ tmp_k = torch.stack(
199
+ [
200
+ kvcache[0].view(total_blocks, d).index_select(0, slices)
201
+ for kvcache in self.kvcaches
202
+ ]
203
+ )
204
+ tmp_v = torch.stack(
205
+ [
206
+ kvcache[1].view(total_blocks, d).index_select(0, slices)
207
+ for kvcache in self.kvcaches
208
+ ]
209
+ )
210
+ tmp = torch.stack([tmp_k, tmp_v])
211
+ memory_obj.tensor.copy_(tmp, non_blocking=True)
212
+
213
+ if not memory_obj.tensor.is_xpu:
214
+ # Force a synchronize if the target buffer is NOT XPU device
215
+ # NOTE: for better performance, we may not want to sync for every
216
+ # memory object
217
+ torch.xpu.synchronize()
218
+
219
+ if self.use_mla:
220
+ memory_obj.metadata.fmt = MemoryFormat.KV_MLA_FMT
221
+
222
+ # TODO(Jiayi): need to optimize to enable real batching
223
+ def batched_to_gpu(self, memory_objs, starts, ends, **kwargs):
224
+ for memory_obj, start, end in zip(memory_objs, starts, ends, strict=False):
225
+ self.to_gpu(memory_obj, start, end, **kwargs)
226
+
227
+ def get_shape(self, num_tokens: int) -> torch.Size:
228
+ """Get the shape of the data given the number of tokens.
229
+
230
+ Args:
231
+ num_tokens: The number of tokens in the data.
232
+
233
+ Returns:
234
+ The shape of the KV cache data.
235
+
236
+ Raises:
237
+ RuntimeError: If attributes have not been initialized yet
238
+ (i.e., no kv_caches have been seen).
239
+ """
240
+ if not self._attributes_initialized:
241
+ raise RuntimeError(
242
+ "Cannot determine shape before attributes are initialized. "
243
+ "Call to_gpu or from_gpu first so that _initialize_attributes "
244
+ "can discover the KV cache layout."
245
+ )
246
+ kv_size = 1 if self.use_mla else 2
247
+ return torch.Size([kv_size, self.num_layers, num_tokens, self.hidden_dim_size])
248
+
249
+ def _validate_memory_format(self, memory_obj: MemoryObj) -> None:
250
+ """Validate that the memory object has the expected format.
251
+
252
+ Args:
253
+ memory_obj: The memory object to validate.
254
+
255
+ Raises:
256
+ ValueError: If the memory format does not match the expected
257
+ format based on whether MLA is in use.
258
+ """
259
+ if self.use_mla:
260
+ if memory_obj.metadata.fmt != MemoryFormat.KV_MLA_FMT:
261
+ raise ValueError(
262
+ "The memory object should be in KV_MLA_FMT format in"
263
+ " order to be processed by VLLMPagedMemXPUConnectorV2"
264
+ )
265
+ else:
266
+ if memory_obj.metadata.fmt != MemoryFormat.KV_2LTD:
267
+ raise ValueError(
268
+ "The memory object should be in KV_2LTD format in"
269
+ " order to be processed by VLLMPagedMemXPUConnectorV2"
270
+ )
271
+
272
+ def _initialize_attributes(self, kv_caches: List[torch.Tensor]):
273
+ """Initialize attributes from the kv_caches using utils functions.
274
+
275
+ Uses format discovery and utility functions from utils.py to
276
+ extract all KV cache parameters lazily on first use.
277
+
278
+ Args:
279
+ kv_caches: The KV cache tensors from which to discover
280
+ the cache layout and parameters.
281
+ """
282
+ if self._attributes_initialized:
283
+ return
284
+
285
+ self.device = kv_caches[0].device
286
+ assert self.device.type == "xpu", "The device should be XPU."
287
+
288
+ self.gpu_kv_format = discover_gpu_kv_format(kv_caches, EngineType.VLLM)
289
+ self.num_layers = get_num_layers(kv_caches, self.gpu_kv_format)
290
+ self.num_blocks = get_num_blocks(kv_caches, self.gpu_kv_format)
291
+ self.block_size = get_block_size(kv_caches, self.gpu_kv_format)
292
+ self.page_buffer_size = get_page_buffer_size(kv_caches, self.gpu_kv_format)
293
+ self.hidden_dim_size = get_hidden_dim_size(kv_caches, self.gpu_kv_format)
294
+ self.head_size = get_head_size(kv_caches, self.gpu_kv_format)
295
+ self.use_mla = is_mla(self.gpu_kv_format)
296
+ self.dtype = get_dtype(kv_caches, self.gpu_kv_format)
297
+ self.num_heads = (
298
+ 1 if self.use_mla else get_num_heads(kv_caches, self.gpu_kv_format)
299
+ )
300
+
301
+ self._attributes_initialized = True
302
+ logger.info(
303
+ "XPU: attributes initialized - format: %s, "
304
+ "num_layers: %d, num_blocks: %d, block_size: %d, "
305
+ "page_buffer_size: %d, hidden_dim_size: %d, head_size: %d, "
306
+ "use_mla: %s, dtype: %s, num_heads: %d",
307
+ self.gpu_kv_format,
308
+ self.num_layers,
309
+ self.num_blocks,
310
+ self.block_size,
311
+ self.page_buffer_size,
312
+ self.hidden_dim_size,
313
+ self.head_size,
314
+ self.use_mla,
315
+ self.dtype,
316
+ self.num_heads,
317
+ )
318
+
319
+
320
+ class VLLMPagedMemLayerwiseXPUConnector(GPUConnectorInterface):
321
+ """
322
+ Layerwise paged KV connector for XPU.
323
+
324
+ Implements the *same generator contract* as VLLMPagedMemLayerwiseGPUConnector:
325
+ - batched_to_gpu(...) yields num_layers + 2 times
326
+ - batched_from_gpu(...) yields num_layers + 1 times
327
+
328
+ Transfer is implemented with pure torch ops (index_copy_/index_select).
329
+ """
330
+
331
+ def __init__(
332
+ self,
333
+ hidden_dim_size: int,
334
+ num_layers: int,
335
+ use_xpu: bool = False,
336
+ **kwargs,
337
+ ):
338
+ self.hidden_dim_size = hidden_dim_size
339
+ self.num_layers = num_layers
340
+ self.use_xpu = use_xpu
341
+
342
+ assert "chunk_size" in kwargs, "chunk_size should be provided."
343
+ assert "dtype" in kwargs, "dtype should be provided."
344
+ assert "device" in kwargs, "device should be provided."
345
+
346
+ self.dtype = kwargs["dtype"]
347
+ self.device = kwargs["device"]
348
+ self.use_mla = "use_mla" in kwargs and kwargs["use_mla"]
349
+
350
+ self.kvcaches: Optional[List[torch.Tensor]] = None
351
+
352
+ # XPU streams
353
+ self.load_stream = torch.xpu.Stream()
354
+ self.store_stream = torch.xpu.Stream()
355
+
356
+ # Optional device staging buffer allocator (same pattern as CUDA class)
357
+ self.gpu_buffer_allocator: Optional[MemoryAllocatorInterface] = None
358
+
359
+ @classmethod
360
+ def from_metadata(
361
+ cls,
362
+ metadata: LMCacheMetadata,
363
+ use_xpu: bool = False,
364
+ device: Optional[torch.device] = None,
365
+ ) -> "VLLMPagedMemLayerwiseXPUConnector":
366
+ num_layers = metadata.kv_shape[0]
367
+ num_kv_head = metadata.kv_shape[3]
368
+ head_size = metadata.kv_shape[4]
369
+ hidden_dim_size = num_kv_head * head_size
370
+ return cls(
371
+ hidden_dim_size=hidden_dim_size,
372
+ num_layers=num_layers,
373
+ use_xpu=use_xpu,
374
+ chunk_size=metadata.kv_shape[2],
375
+ dtype=metadata.kv_dtype,
376
+ device=device,
377
+ use_mla=metadata.use_mla,
378
+ )
379
+
380
+ def _validate_format_transition(self, mem, target_fmt):
381
+ current_fmt = mem.metadata.fmt
382
+
383
+ if (current_fmt, target_fmt) not in ALLOWED_FORMAT_TRANSITIONS:
384
+ raise ValueError(
385
+ f"Invalid KV format transition: {current_fmt} -> {target_fmt}"
386
+ )
387
+
388
+ def _lazy_initialize_buffer(self, kv_caches: List[torch.Tensor]) -> None:
389
+ # Buffer allocator only needed when use_xpu=True (device staging)
390
+ if self.use_xpu and self.gpu_buffer_allocator is None:
391
+ # First Party
392
+ from lmcache.v1.memory_management import XPUMemoryAllocator
393
+
394
+ # Derive size from first layer KV tensor
395
+ layer0 = kv_caches[0]
396
+ derived_bytes = layer0.numel() * layer0.element_size()
397
+
398
+ # Allow override via env variable
399
+ staging_bytes = int(
400
+ os.getenv("LMCACHE_GPU_STAGING_BUFFER_BYTES", derived_bytes)
401
+ )
402
+
403
+ logger.info(
404
+ "Initializing staging buffer (derived=%d bytes, final=%d bytes)",
405
+ derived_bytes,
406
+ staging_bytes,
407
+ )
408
+
409
+ self.gpu_buffer_allocator = XPUMemoryAllocator(
410
+ size=staging_bytes,
411
+ device=self.device,
412
+ )
413
+
414
+ def to_gpu(self, memory_obj: MemoryObj, start: int, end: int, **kwargs):
415
+ raise NotImplementedError("Layerwise uses batched_to_gpu(generator).")
416
+
417
+ def from_gpu(self, memory_obj: MemoryObj, start: int, end: int, **kwargs):
418
+ raise NotImplementedError("Layerwise uses batched_from_gpu(generator).")
419
+
420
+ def _batched_to_gpu_gen(self, starts: List[int], ends: List[int], **kwargs):
421
+ """
422
+ Generator: CPU token2d -> (optional XPU staging) -> XPU paged KV (per layer).
423
+ """
424
+ self.initialize_kvcaches_ptr(**kwargs)
425
+ assert self.kvcaches is not None
426
+
427
+ if "slot_mapping" not in kwargs:
428
+ raise ValueError("'slot_mapping' should be provided in kwargs.")
429
+ if "sync" not in kwargs:
430
+ raise ValueError("'sync' should be provided in kwargs.")
431
+
432
+ slot_mapping: torch.Tensor = kwargs["slot_mapping"]
433
+ sync: bool = kwargs["sync"]
434
+
435
+ self._lazy_initialize_buffer(self.kvcaches)
436
+
437
+ def _ensure_xpu(t: torch.Tensor) -> torch.Tensor:
438
+ # Handle both torch.device('xpu:0') and string devices consistently.
439
+ if t is None:
440
+ return t
441
+ if t.device != self.device:
442
+ # non_blocking is fine; will be blocking
443
+ # if underlying memory isn't pinned
444
+ return t.to(self.device, non_blocking=True)
445
+ return t
446
+
447
+ # Build a single contiguous mapping in the SAME order we will pack chunks.
448
+ slot_mapping_chunks = [
449
+ slot_mapping[s:e] for s, e in zip(starts, ends, strict=False)
450
+ ]
451
+ slot_mapping_full = torch.cat(slot_mapping_chunks, dim=0)
452
+
453
+ # Move mapping ONCE to device (fixes multiple small H2D copies).
454
+ slot_mapping_full = _ensure_xpu(slot_mapping_full)
455
+
456
+ num_tokens = int(slot_mapping_full.numel())
457
+ if num_tokens <= 0:
458
+ for _ in range(self.num_layers):
459
+ _ = yield
460
+ yield
461
+ if sync:
462
+ torch.xpu.current_stream().wait_stream(self.load_stream)
463
+ yield
464
+ return
465
+
466
+ tmp_gpu_buffer_obj: Optional[MemoryObj] = None
467
+ if self.use_xpu:
468
+ # First Party
469
+ from lmcache.v1.memory_management import MemoryFormat
470
+
471
+ buffer_shape = self.get_shape(num_tokens)
472
+ assert self.gpu_buffer_allocator is not None
473
+ requested_bytes = (
474
+ int(buffer_shape.numel())
475
+ * torch.empty((), dtype=self.dtype).element_size()
476
+ )
477
+ allocator_tensor = getattr(self.gpu_buffer_allocator, "tensor", None)
478
+ capacity_bytes: Optional[int] = None
479
+ if isinstance(allocator_tensor, torch.Tensor):
480
+ capacity_bytes = int(
481
+ allocator_tensor.numel() * allocator_tensor.element_size()
482
+ )
483
+ allocator_backend = getattr(self.gpu_buffer_allocator, "allocator", None)
484
+ allocated_bytes = getattr(allocator_backend, "total_allocated_size", None)
485
+ tmp_gpu_buffer_obj = self.gpu_buffer_allocator.allocate(
486
+ buffer_shape, self.dtype, MemoryFormat.KV_T2D
487
+ )
488
+ if tmp_gpu_buffer_obj is None or tmp_gpu_buffer_obj.tensor is None:
489
+ raise RuntimeError(
490
+ "Failed to allocate XPU staging buffer for batched_to_gpu: "
491
+ f"requested_bytes={requested_bytes}, "
492
+ f"capacity_bytes={capacity_bytes}, "
493
+ f"allocated_bytes={allocated_bytes}, "
494
+ f"allocator_type={type(self.gpu_buffer_allocator).__name__}, "
495
+ f"allocator_tensor_device="
496
+ f"{getattr(allocator_tensor, 'device', None)}"
497
+ )
498
+
499
+ current_stream = torch.xpu.current_stream()
500
+
501
+ try:
502
+ for layer_id in range(self.num_layers):
503
+ memory_objs_layer = yield # List[MemoryObj] for this layer
504
+
505
+ if sync:
506
+ current_stream.wait_stream(self.load_stream)
507
+
508
+ with torch.xpu.stream(self.load_stream):
509
+ dst_layer = self.kvcaches[layer_id]
510
+ if self.use_mla:
511
+ dst_flat = cast(
512
+ torch.Tensor,
513
+ _get_head_size_view(dst_layer, use_mla=True),
514
+ )
515
+ else:
516
+ dst_k_flat, dst_v_flat = _get_head_size_view( # type: ignore[misc]
517
+ dst_layer, use_mla=False
518
+ )
519
+
520
+ cursor = 0
521
+
522
+ if self.use_xpu:
523
+ assert tmp_gpu_buffer_obj is not None
524
+ staged = tmp_gpu_buffer_obj.tensor
525
+ assert staged is not None
526
+
527
+ for s, e, mem in zip(
528
+ starts, ends, memory_objs_layer, strict=False
529
+ ):
530
+ assert mem.tensor is not None
531
+ n = int(e - s)
532
+ if n <= 0:
533
+ continue
534
+
535
+ src = _ensure_xpu(mem.tensor)
536
+
537
+ staged[cursor : cursor + n].copy_(src, non_blocking=True)
538
+ cursor += n
539
+
540
+ sl = slot_mapping_full # already intended to be on device
541
+ sl = _ensure_xpu(sl)
542
+
543
+ if self.use_mla:
544
+ staged_xpu = _ensure_xpu(staged)
545
+ if staged_xpu.dim() == 2:
546
+ dst_flat.index_copy_(0, sl, staged_xpu)
547
+ elif staged_xpu.dim() == 3 and staged_xpu.shape[0] == 1:
548
+ dst_flat.index_copy_(0, sl, staged_xpu[0])
549
+ else:
550
+ raise ValueError(
551
+ f"Unexpected MLA staged tensor: {staged_xpu.shape}"
552
+ )
553
+ else:
554
+ k_tok, v_tok = _split_token2d_kv(staged)
555
+
556
+ # Make sure k_tok/v_tok are on XPU before index_copy_.
557
+ k_tok = _ensure_xpu(k_tok)
558
+ v_tok = _ensure_xpu(v_tok)
559
+
560
+ # Keep your reshape logic as-is (only triggers when needed)
561
+ if (
562
+ k_tok.dim() == 2
563
+ and dst_k_flat.dim() == 3
564
+ and k_tok.shape[1]
565
+ == dst_k_flat.shape[1] * dst_k_flat.shape[2]
566
+ ):
567
+ k_tok = k_tok.reshape(
568
+ k_tok.shape[0],
569
+ dst_k_flat.shape[1],
570
+ dst_k_flat.shape[2],
571
+ )
572
+ if (
573
+ v_tok.dim() == 2
574
+ and dst_v_flat.dim() == 3
575
+ and v_tok.shape[1]
576
+ == dst_v_flat.shape[1] * dst_v_flat.shape[2]
577
+ ):
578
+ v_tok = v_tok.reshape(
579
+ v_tok.shape[0],
580
+ dst_v_flat.shape[1],
581
+ dst_v_flat.shape[2],
582
+ )
583
+
584
+ dst_k_flat.index_copy_(0, sl, k_tok)
585
+ dst_v_flat.index_copy_(0, sl, v_tok)
586
+
587
+ else:
588
+ for s, e, mem in zip(
589
+ starts, ends, memory_objs_layer, strict=False
590
+ ):
591
+ assert mem.tensor is not None
592
+ n = int(e - s)
593
+ if n <= 0:
594
+ continue
595
+
596
+ src = _ensure_xpu(mem.tensor)
597
+ sl = slot_mapping_full[cursor : cursor + n]
598
+ sl = _ensure_xpu(sl)
599
+ cursor += n
600
+
601
+ if self.use_mla:
602
+ if src.dim() == 2:
603
+ dst_flat.index_copy_(0, sl, src)
604
+ elif src.dim() == 3 and src.shape[0] == 1:
605
+ dst_flat.index_copy_(0, sl, src[0])
606
+ else:
607
+ raise ValueError(
608
+ f"Unexpected MLA token tensor: {src.shape}"
609
+ )
610
+ else:
611
+ k_tok, v_tok = _split_token2d_kv(src)
612
+ k_tok = _ensure_xpu(k_tok)
613
+ v_tok = _ensure_xpu(v_tok)
614
+
615
+ if (
616
+ k_tok.dim() == 2
617
+ and dst_k_flat.dim() == 3
618
+ and k_tok.shape[1]
619
+ == dst_k_flat.shape[1] * dst_k_flat.shape[2]
620
+ ):
621
+ k_tok = k_tok.reshape(
622
+ k_tok.shape[0],
623
+ dst_k_flat.shape[1],
624
+ dst_k_flat.shape[2],
625
+ )
626
+ if (
627
+ v_tok.dim() == 2
628
+ and dst_v_flat.dim() == 3
629
+ and v_tok.shape[1]
630
+ == dst_v_flat.shape[1] * dst_v_flat.shape[2]
631
+ ):
632
+ v_tok = v_tok.reshape(
633
+ v_tok.shape[0],
634
+ dst_v_flat.shape[1],
635
+ dst_v_flat.shape[2],
636
+ )
637
+
638
+ dst_k_flat.index_copy_(0, sl, k_tok)
639
+ dst_v_flat.index_copy_(0, sl, v_tok)
640
+
641
+ yield
642
+
643
+ if sync:
644
+ current_stream.wait_stream(self.load_stream)
645
+ finally:
646
+ if tmp_gpu_buffer_obj is not None:
647
+ tmp_gpu_buffer_obj.ref_count_down()
648
+
649
+ yield
650
+
651
+ def batched_from_gpu( # type: ignore[override]
652
+ self,
653
+ memory_objs: List[List[MemoryObj]],
654
+ starts: List[int],
655
+ ends: List[int],
656
+ **kwargs,
657
+ ):
658
+ """
659
+ Generator: XPU paged KV -> (optional XPU staging) -> CPU token2d (per layer).
660
+ """
661
+ self.initialize_kvcaches_ptr(**kwargs)
662
+ assert self.kvcaches is not None
663
+
664
+ if "slot_mapping" not in kwargs:
665
+ raise ValueError("'slot_mapping' should be provided in kwargs.")
666
+ if "sync" not in kwargs:
667
+ raise ValueError("'sync' should be provided in kwargs.")
668
+
669
+ slot_mapping: torch.Tensor = kwargs["slot_mapping"]
670
+ sync: bool = kwargs["sync"]
671
+
672
+ self._lazy_initialize_buffer(self.kvcaches)
673
+
674
+ current_stream = torch.xpu.current_stream()
675
+
676
+ # ---- helpers (keep local to minimize file-wide changes) ----
677
+ def _flatten_last2_if_needed(
678
+ src: torch.Tensor, dst: torch.Tensor
679
+ ) -> torch.Tensor:
680
+ """
681
+ Make src match dst for the common KV layouts:
682
+ - src: [..., H, HS] -> dst: [..., H*HS]
683
+ - or already matches
684
+ """
685
+ if src.shape == dst.shape:
686
+ return src
687
+
688
+ # dst has one less trailing dim: [..., D] where D=H*HS
689
+ if src.dim() == dst.dim() + 1:
690
+ # e.g., src [..., 8, 128] -> dst [..., 1024]
691
+ if dst.shape == (*src.shape[:-2], src.shape[-2] * src.shape[-1]):
692
+ return src.reshape(*src.shape[:-2], -1)
693
+
694
+ # same ndim but dst last dim is flattened (dst ends with D)
695
+ if src.dim() == dst.dim():
696
+ if (
697
+ dst.shape[:-1] == src.shape[:-2]
698
+ and dst.shape[-1] == src.shape[-2] * src.shape[-1]
699
+ ):
700
+ return src.reshape(*src.shape[:-2], -1)
701
+
702
+ return src # caller will error if still incompatible
703
+
704
+ def _copy_kv_into_mem(
705
+ mem_tensor: torch.Tensor, k_src: torch.Tensor, v_src: torch.Tensor
706
+ ) -> None:
707
+ """
708
+ Copy K/V into mem.tensor supporting:
709
+ - [2, ..., D] (K in 0, V in 1)
710
+ - [..., 2, D] (K in [:,0,:], V in [:,1,:])
711
+ - [2, ..., H, HS] or [..., 2, H, HS] similarly
712
+ """
713
+ if mem_tensor.dim() < 3:
714
+ raise ValueError(
715
+ f"Unexpected output token2d layout: {mem_tensor.shape}"
716
+ )
717
+
718
+ # Case A: mem is [2, ...]
719
+ if mem_tensor.shape[0] == 2:
720
+ k_dst = mem_tensor[0]
721
+ v_dst = mem_tensor[1]
722
+ k_src2 = _flatten_last2_if_needed(k_src, k_dst)
723
+ v_src2 = _flatten_last2_if_needed(v_src, v_dst)
724
+ if k_src2.shape != k_dst.shape or v_src2.shape != v_dst.shape:
725
+ raise ValueError(
726
+ f"KV shape mismatch after reshape: "
727
+ f"k src {k_src.shape}->{k_src2.shape} vs dst {k_dst.shape}; "
728
+ f"v src {v_src.shape}->{v_src2.shape} vs dst {v_dst.shape}"
729
+ )
730
+ k_dst.copy_(k_src2.to(k_dst.device), non_blocking=True)
731
+ v_dst.copy_(v_src2.to(v_dst.device), non_blocking=True)
732
+ return
733
+
734
+ # Case B: mem is [..., 2, ...]
735
+ if mem_tensor.shape[1] == 2:
736
+ k_dst = mem_tensor[:, 0, ...]
737
+ v_dst = mem_tensor[:, 1, ...]
738
+ k_src2 = _flatten_last2_if_needed(k_src, k_dst)
739
+ v_src2 = _flatten_last2_if_needed(v_src, v_dst)
740
+ if k_src2.shape != k_dst.shape or v_src2.shape != v_dst.shape:
741
+ raise ValueError(
742
+ f"KV shape mismatch after reshape: "
743
+ f"k src {k_src.shape}->{k_src2.shape} vs dst {k_dst.shape}; "
744
+ f"v src {v_src.shape}->{v_src2.shape} vs dst {v_dst.shape}"
745
+ )
746
+ k_dst.copy_(k_src2.to(k_dst.device), non_blocking=True)
747
+ v_dst.copy_(v_src2.to(v_dst.device), non_blocking=True)
748
+ return
749
+
750
+ raise ValueError(f"Unexpected output token2d layout: {mem_tensor.shape}")
751
+
752
+ slot_mapping_on_device = slot_mapping.to(self.device)
753
+
754
+ # Precompute “full” mapping for batched gather
755
+ # NOTE: this assumes starts/ends partition slot_mapping contiguously.
756
+ # If not contiguous, concatenation is still correct.
757
+ slot_mapping_full = torch.cat(
758
+ [slot_mapping_on_device[s:e] for s, e in zip(starts, ends, strict=False)],
759
+ dim=0,
760
+ )
761
+ total_tokens = int(slot_mapping_full.numel())
762
+
763
+ # Optional staging buffer (will be USED when self.use_xpu=True)
764
+ tmp_gpu_buffer_obj: Optional[MemoryObj] = None
765
+ if self.use_xpu:
766
+ # First Party
767
+ from lmcache.v1.memory_management import MemoryFormat
768
+
769
+ # buffer shape uses existing helper; must match how allocator expects KV_T2D
770
+ buffer_shape = self.get_shape(total_tokens)
771
+ assert self.gpu_buffer_allocator is not None
772
+ requested_bytes = (
773
+ int(buffer_shape.numel())
774
+ * torch.empty((), dtype=self.dtype).element_size()
775
+ )
776
+ allocator_tensor = getattr(self.gpu_buffer_allocator, "tensor", None)
777
+ capacity_bytes: Optional[int] = None
778
+ if isinstance(allocator_tensor, torch.Tensor):
779
+ capacity_bytes = int(
780
+ allocator_tensor.numel() * allocator_tensor.element_size()
781
+ )
782
+ allocator_backend = getattr(self.gpu_buffer_allocator, "allocator", None)
783
+ allocated_bytes = getattr(allocator_backend, "total_allocated_size", None)
784
+ tmp_gpu_buffer_obj = self.gpu_buffer_allocator.allocate(
785
+ buffer_shape, self.dtype, MemoryFormat.KV_T2D
786
+ )
787
+ if tmp_gpu_buffer_obj is None or tmp_gpu_buffer_obj.tensor is None:
788
+ raise RuntimeError(
789
+ "Failed to allocate XPU staging buffer for batched_from_gpu: "
790
+ f"requested_bytes={requested_bytes}, "
791
+ f"capacity_bytes={capacity_bytes}, "
792
+ f"allocated_bytes={allocated_bytes}, "
793
+ f"allocator_type={type(self.gpu_buffer_allocator).__name__}, "
794
+ f"allocator_tensor_device="
795
+ f"{getattr(allocator_tensor, 'device', None)}"
796
+ )
797
+ tmp = tmp_gpu_buffer_obj.tensor # staging tensor on device
798
+
799
+ try:
800
+ for layer_id in range(self.num_layers):
801
+ mem_layer = memory_objs[layer_id]
802
+
803
+ with torch.xpu.stream(self.store_stream):
804
+ self.store_stream.wait_stream(current_stream)
805
+
806
+ src_layer = self.kvcaches[layer_id]
807
+
808
+ if self.use_mla:
809
+ src_flat = cast(
810
+ torch.Tensor,
811
+ _get_head_size_view(src_layer, use_mla=True),
812
+ )
813
+
814
+ if self.use_xpu:
815
+ gathered_full = src_flat.index_select(0, slot_mapping_full)
816
+ # Write into tmp if possible, else fallback to per-chunk
817
+ tmp_src = (
818
+ _flatten_last2_if_needed(gathered_full, tmp)
819
+ if "tmp" in locals()
820
+ else gathered_full
821
+ )
822
+ if "tmp" in locals() and tmp_src.shape == tmp.shape:
823
+ tmp.copy_(tmp_src, non_blocking=True)
824
+ off = 0
825
+ for s, e, mem in zip(
826
+ starts, ends, mem_layer, strict=False
827
+ ):
828
+ assert mem.tensor is not None
829
+ n = e - s
830
+ chunk = tmp[off : off + n]
831
+ off += n
832
+ mem.tensor.copy_(
833
+ chunk.to(mem.tensor.device), non_blocking=True
834
+ )
835
+ else:
836
+ for s, e, mem in zip(
837
+ starts, ends, mem_layer, strict=False
838
+ ):
839
+ assert mem.tensor is not None
840
+ sl = slot_mapping_on_device[s:e]
841
+ gathered = src_flat.index_select(0, sl)
842
+ mem.tensor.copy_(
843
+ gathered.to(mem.tensor.device),
844
+ non_blocking=True,
845
+ )
846
+ else:
847
+ for s, e, mem in zip(starts, ends, mem_layer, strict=False):
848
+ assert mem.tensor is not None
849
+ sl = slot_mapping_on_device[s:e]
850
+ gathered = src_flat.index_select(0, sl)
851
+ mem.tensor.copy_(
852
+ gathered.to(mem.tensor.device), non_blocking=True
853
+ )
854
+
855
+ # Keep memory format metadata consistent for downstream checks.
856
+ target_fmt = MemoryFormat.KV_MLA_FMT
857
+ for mem in mem_layer:
858
+ self._validate_format_transition(mem, target_fmt)
859
+ mem.metadata.fmt = target_fmt
860
+
861
+ else:
862
+ src_k_flat, src_v_flat = _get_head_size_view(
863
+ src_layer, use_mla=False
864
+ )
865
+
866
+ if self.use_xpu:
867
+ k_full = src_k_flat.index_select(0, slot_mapping_full)
868
+ v_full = src_v_flat.index_select(0, slot_mapping_full)
869
+
870
+ # Slice from staging. If tmp exists and can hold
871
+ # the layout, use it; otherwise slice k/v directly.
872
+ off = 0
873
+ for s, e, mem in zip(starts, ends, mem_layer, strict=False):
874
+ assert mem.tensor is not None
875
+ n = e - s
876
+
877
+ k_chunk = k_full[off : off + n]
878
+ v_chunk = v_full[off : off + n]
879
+ off += n
880
+
881
+ _copy_kv_into_mem(mem.tensor, k_chunk, v_chunk)
882
+
883
+ else:
884
+ # per-chunk gather (original behavior);
885
+ # avoids per-iteration H2D slot_mapping transfers
886
+ for s, e, mem in zip(starts, ends, mem_layer, strict=False):
887
+ assert mem.tensor is not None
888
+ sl = slot_mapping_on_device[s:e]
889
+ k = src_k_flat.index_select(0, sl)
890
+ v = src_v_flat.index_select(0, sl)
891
+ _copy_kv_into_mem(mem.tensor, k, v)
892
+
893
+ if sync:
894
+ self.store_stream.synchronize()
895
+ yield
896
+ finally:
897
+ if tmp_gpu_buffer_obj is not None:
898
+ tmp_gpu_buffer_obj.ref_count_down()
899
+
900
+ yield
901
+
902
+ def batched_to_gpu(
903
+ self,
904
+ memory_objs: Union[
905
+ List[List[MemoryObj]], List[MemoryObj], List[int], None
906
+ ] = None,
907
+ starts: Optional[List[int]] = None,
908
+ ends: Optional[List[int]] = None,
909
+ **kwargs,
910
+ ):
911
+ return self._batched_to_gpu_gen(starts=starts or [], ends=ends or [], **kwargs)
912
+
913
+ def get_shape(self, num_tokens: int) -> torch.Size:
914
+ if self.use_mla:
915
+ return torch.Size([num_tokens, self.hidden_dim_size])
916
+ return torch.Size([num_tokens, 2, self.hidden_dim_size])