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,890 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Any,
6
+ List,
7
+ Literal,
8
+ Optional,
9
+ Tuple,
10
+ TypedDict,
11
+ Union,
12
+ overload,
13
+ )
14
+
15
+ # Third Party
16
+ import torch
17
+
18
+ # First Party
19
+ from lmcache.logging import init_logger
20
+ from lmcache.utils import EngineType
21
+ from lmcache.v1.config import LMCacheEngineConfig
22
+
23
+ if TYPE_CHECKING:
24
+ # First Party
25
+ from lmcache.v1.gpu_connector.gpu_connectors import GPUConnectorInterface
26
+
27
+ # First Party
28
+ import lmcache.c_ops as lmc_ops
29
+
30
+ logger = init_logger(__name__)
31
+
32
+ # Error message for accessing non-existent attributes in GPU KV Cache
33
+ _ATTRIBUTE_NOT_EXIST_ERROR = "trying to access an attribute of the GPU KV Cache "
34
+ "that does not exist for the format detected {format}. "
35
+ "A misalignment with the GPUKVFormat must be resolved"
36
+
37
+
38
+ class LayoutHints(TypedDict, total=False):
39
+ """Hints passed from a serving engine to LMCache during KV cache
40
+ registration (``REGISTER_KV_CACHE``).
41
+
42
+ Serving engines may pass a plain ``dict`` that satisfies this
43
+ schema — importing this type is optional.
44
+
45
+ Keys:
46
+ kv_layout: Physical ordering of the KV cache dimensions.
47
+ ``"NHD"`` — heads after block-size (default for most
48
+ vLLM builds).
49
+ ``"HND"`` — heads before block-size (``VLLM_KV_CACHE_LAYOUT=HND``).
50
+ """
51
+
52
+ kv_layout: Literal["NHD", "HND"]
53
+
54
+
55
+ def permute_to_contiguous(tensor: torch.Tensor) -> torch.Tensor:
56
+ """Permute a tensor back to contiguous state (metadata-only, no copy).
57
+
58
+ Assumption: the tensor is only non-contiguous because of a previous
59
+ permutation. Raises if this assumption is not met.
60
+
61
+ The only known case is HND: vLLM allocates physically as HND
62
+ (e.g. [2, NB, NH, BS, HS]) but exposes an NHD logical view via
63
+ permute/transpose. This function recovers the underlying HND
64
+ physical shape so that ``discover_gpu_kv_format`` (with the
65
+ ``kv_layout="HND"`` hint) sees the true memory layout and selects
66
+ the correct HND format enum. The reverse — physically NHD but
67
+ logically permuted to HND — does not occur in practice.
68
+
69
+ Returns the tensor unchanged if already contiguous.
70
+ """
71
+ if tensor.is_contiguous():
72
+ return tensor
73
+
74
+ strides = tensor.stride()
75
+ perm = sorted(range(tensor.ndim), key=lambda i: strides[i], reverse=True)
76
+ result = tensor.permute(perm)
77
+
78
+ if not result.is_contiguous():
79
+ raise ValueError(
80
+ "tensor is non-contiguous for reasons other than permutation "
81
+ "(e.g., slicing or as_strided). Cannot recover contiguous view."
82
+ )
83
+ return result
84
+
85
+
86
+ def permute_kv_caches_to_contiguous(
87
+ kv_caches: List[torch.Tensor],
88
+ ) -> List[torch.Tensor]:
89
+ """Apply :func:`permute_to_contiguous` to each tensor in *kv_caches*.
90
+
91
+ The returned list shares the same underlying storage as the input.
92
+ """
93
+ return [permute_to_contiguous(t) for t in kv_caches]
94
+
95
+
96
+ def assert_contiguous(tensor: torch.Tensor) -> None:
97
+ """Assert that a tensor has a contiguous physical layout with zero offset.
98
+
99
+ LMCache transfer kernels assume logical and physical views match
100
+ for coalesced memory accesses. Do NOT blindly call ``.contiguous()``
101
+ or ``.permute()`` to fix failures here — identify the root cause.
102
+ """
103
+ assert tensor.storage_offset() == 0, (
104
+ f"expected storage_offset 0, got {tensor.storage_offset()}"
105
+ )
106
+ assert tensor.is_contiguous(), "tensor is not contiguous"
107
+
108
+
109
+ def any_non_contiguous(kv_caches: dict[str, torch.Tensor] | List[torch.Tensor]) -> bool:
110
+ """Return True if any tensor in *kv_caches* is non-contiguous."""
111
+ tensors = kv_caches.values() if isinstance(kv_caches, dict) else kv_caches
112
+ return not all(t.is_contiguous() for t in tensors)
113
+
114
+
115
+ @overload
116
+ def ensure_contiguous_kv_caches(
117
+ kv_caches: dict[str, torch.Tensor],
118
+ kv_layout: str | None = None,
119
+ ) -> dict[str, torch.Tensor]: ...
120
+
121
+
122
+ @overload
123
+ def ensure_contiguous_kv_caches(
124
+ kv_caches: List[torch.Tensor],
125
+ kv_layout: str | None = None,
126
+ ) -> List[torch.Tensor]: ...
127
+
128
+
129
+ def ensure_contiguous_kv_caches(
130
+ kv_caches: dict[str, torch.Tensor] | List[torch.Tensor],
131
+ kv_layout: str | None = None,
132
+ ) -> dict[str, torch.Tensor] | List[torch.Tensor]:
133
+ """Permute non-contiguous KV caches to contiguous physical shape.
134
+
135
+ LMCache assumes tensors have matching logical and physical views.
136
+ Known reasons for non-contiguity: HND format from vLLM.
137
+
138
+ Accepts both ``dict`` and ``list`` forms.
139
+ Returns *kv_caches* unchanged if already contiguous.
140
+ """
141
+ if not any_non_contiguous(kv_caches):
142
+ return kv_caches
143
+
144
+ if isinstance(kv_caches, dict):
145
+ result: dict[str, torch.Tensor] | List[torch.Tensor] = dict(
146
+ zip(
147
+ kv_caches.keys(),
148
+ permute_kv_caches_to_contiguous(list(kv_caches.values())),
149
+ strict=False,
150
+ )
151
+ )
152
+ else:
153
+ result = permute_kv_caches_to_contiguous(kv_caches)
154
+
155
+ if kv_layout == "HND":
156
+ logger.info("Permuted HND tensors to contiguous physical shape")
157
+ else:
158
+ logger.warning(
159
+ "Non-contiguous KV tensors detected with layout=%s; "
160
+ "permuted to contiguous. Please identify the underlying reason.",
161
+ kv_layout,
162
+ )
163
+
164
+ return result
165
+
166
+
167
+ def need_gpu_interm_buffer(lmcache_config: LMCacheEngineConfig):
168
+ """
169
+ Check if the GPU Connector needs to create an intermediate
170
+ buffer on the GPU
171
+ """
172
+ if lmcache_config.enable_pd:
173
+ return False
174
+ else:
175
+ return True
176
+
177
+
178
+ def assert_layerwise_gpu_connector(gpu_connector: "GPUConnectorInterface"):
179
+ """
180
+ Assert that a GPU Connector is a layerwise connector.
181
+ """
182
+ # Import at runtime to avoid circular dependency
183
+ # First Party
184
+ from lmcache.v1.gpu_connector.gpu_connectors import (
185
+ SGLangLayerwiseGPUConnector,
186
+ VLLMBufferLayerwiseGPUConnector,
187
+ VLLMPagedMemLayerwiseGPUConnector,
188
+ )
189
+ from lmcache.v1.gpu_connector.xpu_connectors import (
190
+ VLLMPagedMemLayerwiseXPUConnector,
191
+ )
192
+
193
+ assert isinstance(
194
+ gpu_connector,
195
+ (
196
+ VLLMPagedMemLayerwiseGPUConnector,
197
+ VLLMBufferLayerwiseGPUConnector,
198
+ SGLangLayerwiseGPUConnector,
199
+ VLLMPagedMemLayerwiseXPUConnector,
200
+ ),
201
+ )
202
+
203
+
204
+ def get_gpu_kv_shape_description(gpu_kv_format: "lmc_ops.GPUKVFormat") -> str:
205
+ """Return a human-readable shape description for the GPU KV format.
206
+
207
+ Uses short names matching the ``GPUKVFormat`` enum convention:
208
+ NB=num_blocks, NL=num_layers, BS=block_size, NH=num_heads,
209
+ HS=head_size, PBS=page_buffer_size (NB*BS).
210
+ """
211
+ _SHAPE_DESCRIPTIONS: dict["lmc_ops.GPUKVFormat", str] = {
212
+ lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS: "[NB, NL, 2, BS, NH, HS]",
213
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS: "NL x [2, NB, BS, NH, HS]",
214
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS: "NL x [NB, 2, BS, NH, HS]",
215
+ lmc_ops.GPUKVFormat.NL_X_NB_BS_HS: "NL x [NB, BS, HS]",
216
+ lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS: "2 x NL x [PBS, NH, HS]",
217
+ lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS: "NL x [PBS, 1, HS]",
218
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS: "NL x [2, NB, NH, BS, HS]",
219
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS: "NL x [NB, 2, NH, BS, HS]",
220
+ }
221
+ return _SHAPE_DESCRIPTIONS.get(gpu_kv_format, f"Unknown ({gpu_kv_format})")
222
+
223
+
224
+ def get_attention_backend(gpu_kv_format: "lmc_ops.GPUKVFormat") -> str:
225
+ """Return the attention backend name for the GPU KV format."""
226
+ _ATTENTION_BACKENDS: dict["lmc_ops.GPUKVFormat", str] = {
227
+ lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS: "vLLM CROSS_LAYER",
228
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS: "vLLM non-MLA flash attention",
229
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS: "vLLM non-MLA flash infer",
230
+ lmc_ops.GPUKVFormat.NL_X_NB_BS_HS: "vLLM MLA",
231
+ lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS: (
232
+ "SGLang MHA (flash attention and flash infer)"
233
+ ),
234
+ lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS: "SGLang MLA",
235
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS: (
236
+ "vLLM non-MLA flash attention (HND layout)"
237
+ ),
238
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS: (
239
+ "vLLM non-MLA flash infer (HND layout)"
240
+ ),
241
+ }
242
+ return _ATTENTION_BACKENDS.get(gpu_kv_format, f"Unknown ({gpu_kv_format})")
243
+
244
+
245
+ def get_concrete_gpu_kv_shape(
246
+ kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat"
247
+ ) -> str:
248
+ """Return the shape with actual numeric values substituted.
249
+
250
+ For example, instead of ``NL x [2, NB, BS, NH, HS]``
251
+ this returns ``80 x [2, 2048, 128, 8, 128]``.
252
+ """
253
+ nl = get_num_layers(kv_caches, gpu_kv_format)
254
+ hs = get_head_size(kv_caches, gpu_kv_format)
255
+
256
+ fmt = gpu_kv_format
257
+ F = lmc_ops.GPUKVFormat
258
+
259
+ if fmt == F.NB_NL_TWO_BS_NH_HS:
260
+ nb = get_num_blocks(kv_caches, fmt)
261
+ bs = get_block_size(kv_caches, fmt)
262
+ nh = get_num_heads(kv_caches, fmt)
263
+ return f"[{nb}, {nl}, 2, {bs}, {nh}, {hs}]"
264
+
265
+ if fmt == F.NL_X_TWO_NB_BS_NH_HS:
266
+ nb = get_num_blocks(kv_caches, fmt)
267
+ bs = get_block_size(kv_caches, fmt)
268
+ nh = get_num_heads(kv_caches, fmt)
269
+ return f"{nl} x [2, {nb}, {bs}, {nh}, {hs}]"
270
+
271
+ if fmt == F.NL_X_NB_TWO_BS_NH_HS:
272
+ nb = get_num_blocks(kv_caches, fmt)
273
+ bs = get_block_size(kv_caches, fmt)
274
+ nh = get_num_heads(kv_caches, fmt)
275
+ return f"{nl} x [{nb}, 2, {bs}, {nh}, {hs}]"
276
+
277
+ if fmt == F.NL_X_NB_BS_HS:
278
+ nb = get_num_blocks(kv_caches, fmt)
279
+ bs = get_block_size(kv_caches, fmt)
280
+ return f"{nl} x [{nb}, {bs}, {hs}]"
281
+
282
+ if fmt == F.TWO_X_NL_X_NBBS_NH_HS:
283
+ pbs = get_page_buffer_size(kv_caches, fmt)
284
+ nh = get_num_heads(kv_caches, fmt)
285
+ return f"2 x {nl} x [{pbs}, {nh}, {hs}]"
286
+
287
+ if fmt == F.NL_X_NBBS_ONE_HS:
288
+ pbs = get_page_buffer_size(kv_caches, fmt)
289
+ return f"{nl} x [{pbs}, 1, {hs}]"
290
+
291
+ if fmt == F.NL_X_TWO_NB_NH_BS_HS:
292
+ nb = get_num_blocks(kv_caches, fmt)
293
+ nh = get_num_heads(kv_caches, fmt)
294
+ bs = get_block_size(kv_caches, fmt)
295
+ return f"{nl} x [2, {nb}, {nh}, {bs}, {hs}]"
296
+
297
+ if fmt == F.NL_X_NB_TWO_NH_BS_HS:
298
+ nb = get_num_blocks(kv_caches, fmt)
299
+ nh = get_num_heads(kv_caches, fmt)
300
+ bs = get_block_size(kv_caches, fmt)
301
+ return f"{nl} x [{nb}, 2, {nh}, {bs}, {hs}]"
302
+
303
+ return f"Unknown ({gpu_kv_format})"
304
+
305
+
306
+ def legible_print_gpu_kv_format(gpu_kv_format: "lmc_ops.GPUKVFormat"):
307
+ """
308
+ Print the GPU KV Format in a legible way
309
+ """
310
+ shape = get_gpu_kv_shape_description(gpu_kv_format)
311
+ backend = get_attention_backend(gpu_kv_format)
312
+ if shape.startswith("Unknown"):
313
+ logger.warning(f"Unknown GPU KV Format: {gpu_kv_format}")
314
+ else:
315
+ logger.info("GPU KV Format: %s", shape)
316
+ logger.info("Currently used by:\n - %s", backend)
317
+
318
+
319
+ def _list_depth_tensor_dim(kv_caches: Any) -> Tuple[int, int]:
320
+ """
321
+ Get the number of external wrapping lists in the kv_caches.
322
+
323
+ Assumption: kv_caches is of the form
324
+ List[List[...List[torch.Tensor]]]
325
+ """
326
+ depth = 0
327
+ while isinstance(kv_caches, list):
328
+ depth += 1
329
+ if not kv_caches:
330
+ raise ValueError("encountered an empty list")
331
+ kv_caches = kv_caches[0]
332
+ if not isinstance(kv_caches, torch.Tensor):
333
+ raise ValueError("encountered a non-tensor inside")
334
+ return depth, kv_caches.ndim
335
+
336
+
337
+ def discover_gpu_kv_format(
338
+ kv_caches: Any,
339
+ serving_engine: EngineType,
340
+ layout_hints: "LayoutHints | None" = None,
341
+ ) -> "lmc_ops.GPUKVFormat":
342
+ """
343
+ Discover the GPU KV Cache Format from the kv_caches.
344
+
345
+ KEY: the logical view and physical views of the kv_caches should be made consistent
346
+ BEFORE format discovery
347
+
348
+ The logic is that "external" layers are lists and there is one tensor internally.
349
+ We "unwrap" layers until we find the tensor.
350
+
351
+ Args:
352
+ kv_caches: The KV cache tensors (possibly nested lists of tensors).
353
+ serving_engine: Which serving engine produced the caches.
354
+ layout_hints: See :class:`~lmcache.v1.multiprocess.custom_types.LayoutHints`.
355
+
356
+ Please see csrc/mem_kernels.cuh for the naming schema of the GPUKVFormat.
357
+ """
358
+ # list_depth: number of external wrapping lists
359
+ # tensor_dim: number of dimensions of the internal tensor
360
+ list_depth, tensor_dim = _list_depth_tensor_dim(kv_caches)
361
+ logger.info("list_depth: %d, tensor_dim: %d", list_depth, tensor_dim)
362
+ list_dims = []
363
+ ptr = kv_caches
364
+ for _ in range(list_depth):
365
+ list_dims.append(len(ptr))
366
+ ptr = ptr[0]
367
+ # ptr is now the tensor
368
+ assert_contiguous(ptr)
369
+
370
+ tensor_dims = list(ptr.shape)
371
+ dims_str = (
372
+ "".join(f"[{d}]" for d in list_dims) + f"[{', '.join(map(str, tensor_dims))}]"
373
+ )
374
+ logger.info("GPU KV Cache Dimensions: %s", dims_str)
375
+
376
+ if layout_hints is None:
377
+ layout_hints = {}
378
+
379
+ detected_format = None
380
+
381
+ if serving_engine == EngineType.VLLM:
382
+ kv_layout = layout_hints.get("kv_layout")
383
+ if kv_layout is None:
384
+ logger.warning(
385
+ "No KV Cache Layout hint provided when using vLLM, defaulting to NHD"
386
+ )
387
+ kv_layout = "NHD"
388
+ logger.info("vLLM KV cache layout: %s", kv_layout)
389
+ is_hnd = kv_layout == "HND"
390
+ if list_depth == 0:
391
+ # vllm cross layer
392
+ detected_format = lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS
393
+ elif list_depth == 1:
394
+ if tensor_dim == 5:
395
+ if kv_caches[0].shape[0] == 2:
396
+ # vllm non-MLA flash attention
397
+ if is_hnd:
398
+ detected_format = lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS
399
+ else:
400
+ detected_format = lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS
401
+ elif kv_caches[0].shape[1] == 2:
402
+ # vllm non-MLA flash infer
403
+ if is_hnd:
404
+ detected_format = lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS
405
+ else:
406
+ detected_format = lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS
407
+ elif tensor_dim == 3:
408
+ # vllm MLA
409
+ detected_format = lmc_ops.GPUKVFormat.NL_X_NB_BS_HS
410
+ elif serving_engine == EngineType.SGLANG:
411
+ if list_depth == 1:
412
+ if kv_caches[0].shape[1] == 1:
413
+ # sglang MLA
414
+ detected_format = lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS
415
+ elif list_depth == 2:
416
+ # sglang MHA (flash attention and flash infer)
417
+ detected_format = lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS
418
+
419
+ if detected_format is not None:
420
+ legible_print_gpu_kv_format(detected_format)
421
+ return detected_format
422
+ else:
423
+ raise ValueError(
424
+ "currently unsupported kv_caches format "
425
+ f"with list depth {list_depth} and tensor dimension {tensor_dim}"
426
+ )
427
+
428
+
429
+ def get_num_layers(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
430
+ """
431
+ Get the number of layers from the kv_caches
432
+ """
433
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
434
+ return kv_caches.shape[1]
435
+ elif gpu_kv_format in (
436
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
437
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
438
+ lmc_ops.GPUKVFormat.NL_X_NB_BS_HS,
439
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
440
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
441
+ ):
442
+ return len(kv_caches)
443
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
444
+ return len(kv_caches[0])
445
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
446
+ return len(kv_caches)
447
+ else:
448
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
449
+
450
+
451
+ def get_num_blocks(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
452
+ """
453
+ Get the number of blocks from the kv_caches
454
+ """
455
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
456
+ return kv_caches.shape[0]
457
+ elif gpu_kv_format in (
458
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
459
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
460
+ ):
461
+ # [2, num_blocks, ...] — shape[1] is num_blocks
462
+ return kv_caches[0].shape[1]
463
+ elif gpu_kv_format in (
464
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
465
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
466
+ ):
467
+ # [num_blocks, 2, ...] — shape[0] is num_blocks
468
+ return kv_caches[0].shape[0]
469
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
470
+ return kv_caches[0].shape[0]
471
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
472
+ raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
473
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
474
+ raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
475
+ else:
476
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
477
+
478
+
479
+ def get_block_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
480
+ """
481
+ Get the block size from the kv_caches
482
+ """
483
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
484
+ return kv_caches.shape[3]
485
+ elif gpu_kv_format in (
486
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
487
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
488
+ ):
489
+ # NHD: [..., BS, NH, HS] — block_size at shape[2]
490
+ return kv_caches[0].shape[2]
491
+ elif gpu_kv_format in (
492
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
493
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
494
+ ):
495
+ # HND: [..., NH, BS, HS] — block_size at shape[3]
496
+ return kv_caches[0].shape[3]
497
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
498
+ return kv_caches[0].shape[1]
499
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
500
+ raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
501
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
502
+ raise ValueError(_ATTRIBUTE_NOT_EXIST_ERROR.format(format=gpu_kv_format))
503
+ else:
504
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
505
+
506
+
507
+ def get_page_buffer_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
508
+ """
509
+ Get page buffer size (num_blocks * block_size) from the kv_caches
510
+ """
511
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
512
+ # [num_blocks, num_layers, 2, block_size, num_heads, head_size]
513
+ return kv_caches.shape[0] * kv_caches.shape[3]
514
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS:
515
+ # List[num_layers] of [2, num_blocks, block_size, num_heads, head_size]
516
+ return kv_caches[0].shape[1] * kv_caches[0].shape[2]
517
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS:
518
+ # List[num_layers] of [2, num_blocks, num_heads, block_size, head_size]
519
+ # num_blocks=shape[1], block_size=shape[3]
520
+ return kv_caches[0].shape[1] * kv_caches[0].shape[3]
521
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS:
522
+ # List[num_layers] of [num_blocks, 2, block_size, num_heads, head_size]
523
+ return kv_caches[0].shape[0] * kv_caches[0].shape[2]
524
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS:
525
+ # List[num_layers] of [num_blocks, 2, num_heads, block_size, head_size]
526
+ # num_blocks=shape[0], block_size=shape[3]
527
+ return kv_caches[0].shape[0] * kv_caches[0].shape[3]
528
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
529
+ # List[num_layers] of [num_blocks, block_size, head_size]
530
+ return kv_caches[0].shape[0] * kv_caches[0].shape[1]
531
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
532
+ # List[2] -> List[num_layers] of [page_buffer_size, num_heads, head_size]
533
+ return kv_caches[0][0].shape[0]
534
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
535
+ # List[num_layers] of [page_buffer_size, 1, head_size]
536
+ return kv_caches[0].shape[0]
537
+ else:
538
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
539
+
540
+
541
+ def get_num_heads(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
542
+ """
543
+ Get the number of heads from the kv_caches
544
+ """
545
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
546
+ return kv_caches.shape[4]
547
+ elif gpu_kv_format in (
548
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
549
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
550
+ ):
551
+ # NHD: [..., BS, NH, HS] — num_heads at shape[3]
552
+ return kv_caches[0].shape[3]
553
+ elif gpu_kv_format in (
554
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
555
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
556
+ ):
557
+ # HND: [..., NH, BS, HS] — num_heads at shape[2]
558
+ return kv_caches[0].shape[2]
559
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
560
+ # MLA: heads are absorbed into hidden dim, so num_heads = 1
561
+ return 1
562
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
563
+ return kv_caches[0][0].shape[1]
564
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
565
+ return kv_caches[0].shape[1]
566
+ else:
567
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
568
+
569
+
570
+ def get_hidden_dim_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
571
+ """
572
+ Get the hidden dimension from the kv_caches
573
+ """
574
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
575
+ return kv_caches.shape[4] * kv_caches.shape[5]
576
+ elif gpu_kv_format in (
577
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
578
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
579
+ ):
580
+ # NHD: [..., NH, HS] — hidden_dim = shape[3] * shape[4]
581
+ return kv_caches[0].shape[3] * kv_caches[0].shape[4]
582
+ elif gpu_kv_format in (
583
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
584
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
585
+ ):
586
+ # HND: [..., NH, BS, HS] — hidden_dim = NH * HS = shape[2] * shape[4]
587
+ return kv_caches[0].shape[2] * kv_caches[0].shape[4]
588
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
589
+ return kv_caches[0].shape[2]
590
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
591
+ return kv_caches[0][0].shape[1] * kv_caches[0][0].shape[2]
592
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
593
+ return kv_caches[0].shape[2]
594
+ else:
595
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
596
+
597
+
598
+ def get_head_size(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
599
+ """
600
+ Get the head size from the kv_caches
601
+ """
602
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
603
+ return kv_caches.shape[5]
604
+ elif gpu_kv_format in (
605
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
606
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
607
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
608
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
609
+ ):
610
+ # Both NHD [..., NH, HS] and HND [..., BS, HS] have head_size last
611
+ return kv_caches[0].shape[4]
612
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
613
+ return kv_caches[0].shape[2]
614
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
615
+ return kv_caches[0][0].shape[2]
616
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
617
+ return kv_caches[0].shape[2]
618
+ else:
619
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
620
+
621
+
622
+ def get_tokens_per_layer(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
623
+ """
624
+ Get the number of tokens per layer from the kv_caches
625
+ (num_blocks * block_size or page_buffer_size)
626
+ """
627
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
628
+ # [num_blocks, num_layers, 2, block_size, num_heads, head_size]
629
+ return kv_caches.shape[0] * kv_caches.shape[3]
630
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS:
631
+ # List[num_layers] of [2, num_blocks, block_size, num_heads, head_size]
632
+ k_cache_shape = kv_caches[0][0].shape
633
+ return k_cache_shape[0] * k_cache_shape[1]
634
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS:
635
+ # List[num_layers] of [2, num_blocks, num_heads, block_size, head_size]
636
+ # k_cache = kv_caches[0][0] → (NB, NH, BS, HS); tokens = NB * BS
637
+ k_cache_shape = kv_caches[0][0].shape
638
+ return k_cache_shape[0] * k_cache_shape[2]
639
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS:
640
+ # List[num_layers] of [num_blocks, 2, block_size, num_heads, head_size]
641
+ k_cache_shape = kv_caches[0][:, 0].shape
642
+ return k_cache_shape[0] * k_cache_shape[1]
643
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS:
644
+ # List[num_layers] of [num_blocks, 2, num_heads, block_size, head_size]
645
+ # k_cache = kv_caches[0][:, 0] → (NB, NH, BS, HS); tokens = NB * BS
646
+ k_cache_shape = kv_caches[0][:, 0].shape
647
+ return k_cache_shape[0] * k_cache_shape[2]
648
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
649
+ # List[num_layers] of [num_blocks, block_size, head_size]
650
+ return kv_caches[0].shape[0] * kv_caches[0].shape[1]
651
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
652
+ # List[2] -> List[num_layers] of [page_buffer_size, num_heads, head_size]
653
+ return kv_caches[0][0].shape[0]
654
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
655
+ # List[num_layers] of [page_buffer_size, 1, head_size]
656
+ return kv_caches[0].shape[0]
657
+ else:
658
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
659
+
660
+
661
+ def get_elements_per_layer(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> int:
662
+ """
663
+ Get the number of elements per layer from the kv_caches
664
+ (including both K and V for non-MLA)
665
+ """
666
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
667
+ # [num_blocks, num_layers, 2, block_size, num_heads, head_size]
668
+ # For one layer: [num_blocks, 2, block_size, num_heads, head_size]
669
+ num_blocks = kv_caches.shape[0]
670
+ block_size = kv_caches.shape[3]
671
+ num_heads = kv_caches.shape[4]
672
+ head_size = kv_caches.shape[5]
673
+ return num_blocks * 2 * block_size * num_heads * head_size
674
+ elif gpu_kv_format in (
675
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
676
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
677
+ ):
678
+ # [2, num_blocks, ...] — k_cache is kv_caches[0][0]
679
+ k_cache_shape = kv_caches[0][0].shape
680
+ return k_cache_shape.numel() * 2
681
+ elif gpu_kv_format in (
682
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
683
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
684
+ ):
685
+ # [num_blocks, 2, ...] — k_cache is kv_caches[0][:, 0]
686
+ k_cache_shape = kv_caches[0][:, 0].shape
687
+ return k_cache_shape.numel() * 2
688
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS:
689
+ # List[num_layers] of [num_blocks, block_size, head_size] (MLA)
690
+ return kv_caches[0].numel()
691
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
692
+ # List[2] -> List[num_layers] of
693
+ # [page_buffer_size, num_heads, head_size] (separate K and V)
694
+ return kv_caches[0][0].numel() * 2
695
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS:
696
+ # List[num_layers] of [page_buffer_size, 1, head_size] (MLA)
697
+ return kv_caches[0].numel()
698
+ else:
699
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
700
+
701
+
702
+ def assert_is_vllm_flash_attn_or_flash_infer(gpu_kv_format: "lmc_ops.GPUKVFormat"):
703
+ """
704
+ Ensure that we have a GPU KV Cache Format
705
+ that is either vLLM's flash attention or flash infer.
706
+ """
707
+ assert gpu_kv_format in (
708
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
709
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
710
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
711
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
712
+ )
713
+
714
+
715
+ def is_hnd(gpu_kv_format: "lmc_ops.GPUKVFormat") -> bool:
716
+ """
717
+ Check if the GPU KV Format uses HND physical layout
718
+ """
719
+ return gpu_kv_format in (
720
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
721
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
722
+ )
723
+
724
+
725
+ def assert_is_vllm_mla_or_flash_attn_or_flash_infer(
726
+ gpu_kv_format: "lmc_ops.GPUKVFormat",
727
+ ) -> None:
728
+ """
729
+ Ensure that we have a GPU KV Cache Format that is either
730
+ vLLM's MLA, flash attention, or flash infer.
731
+
732
+ Accepted formats:
733
+ - ``NL_X_TWO_NB_BS_NH_HS`` (flash attention, NHD)
734
+ - ``NL_X_NB_TWO_BS_NH_HS`` (flash infer, NHD)
735
+ - ``NL_X_TWO_NB_NH_BS_HS`` (flash attention, HND)
736
+ - ``NL_X_NB_TWO_NH_BS_HS`` (flash infer, HND)
737
+ - ``NL_X_NB_BS_HS`` (MLA)
738
+
739
+ Raises:
740
+ AssertionError: If *gpu_kv_format* is not one of the accepted formats.
741
+ """
742
+ assert gpu_kv_format in (
743
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
744
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
745
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
746
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
747
+ lmc_ops.GPUKVFormat.NL_X_NB_BS_HS,
748
+ )
749
+
750
+
751
+ def is_mla(gpu_kv_format: "lmc_ops.GPUKVFormat") -> bool:
752
+ """
753
+ Check if the GPU KV Format is MLA
754
+ """
755
+ return (
756
+ gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_BS_HS # vllm MLA
757
+ or gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS # sglang MLA
758
+ )
759
+
760
+
761
+ def get_dtype(kv_caches: Any, gpu_kv_format: "lmc_ops.GPUKVFormat") -> torch.dtype:
762
+ """
763
+ Get the dtype from the kv_caches
764
+ """
765
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NB_NL_TWO_BS_NH_HS:
766
+ return kv_caches.dtype
767
+ elif gpu_kv_format in (
768
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS,
769
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS,
770
+ lmc_ops.GPUKVFormat.NL_X_NB_BS_HS,
771
+ lmc_ops.GPUKVFormat.NL_X_TWO_NB_NH_BS_HS,
772
+ lmc_ops.GPUKVFormat.NL_X_NB_TWO_NH_BS_HS,
773
+ lmc_ops.GPUKVFormat.NL_X_NBBS_ONE_HS,
774
+ ):
775
+ return kv_caches[0].dtype
776
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.TWO_X_NL_X_NBBS_NH_HS:
777
+ return kv_caches[0][0].dtype
778
+ else:
779
+ raise ValueError(f"Unknown GPU KV Format: {gpu_kv_format}")
780
+
781
+
782
+ def _split_token2d_kv(token2d: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
783
+ """
784
+ Accepts either:
785
+ - [2, T, D]
786
+ - [T, 2, D]
787
+ Returns:
788
+ - k_tok: [T, D]
789
+ - v_tok: [T, D]
790
+ """
791
+ if token2d.dim() != 3:
792
+ raise ValueError(f"Expected token2d dim=3, got {token2d.shape}")
793
+ if token2d.shape[0] == 2: # [2, T, D]
794
+ return token2d[0], token2d[1]
795
+ if token2d.shape[1] == 2: # [T, 2, D]
796
+ return token2d[:, 0, :], token2d[:, 1, :]
797
+ raise ValueError(f"Unrecognized token2d layout: {token2d.shape}")
798
+
799
+
800
+ def _get_head_size_view(
801
+ kv_cache_layer: Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]],
802
+ *,
803
+ use_mla: bool,
804
+ gpu_kv_format: Optional["lmc_ops.GPUKVFormat"] = None,
805
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
806
+ """
807
+ Returns flattened views for index_copy/index_select.
808
+
809
+ If gpu_kv_format is provided, use it to interpret tensor layout explicitly.
810
+ If not provided, fall back to current structural behavior:
811
+ - MLA: expects Tensor [P, B, HS]
812
+ - Non-MLA: expects either
813
+ * Tensor [2, P, B, NH, HS] OR
814
+ * (k, v) tuple each [P, B, NH, HS]
815
+ (and also supports [P, 2, B, NH, HS] as a safe extension)
816
+ """
817
+ # -------------------------
818
+ # MLA
819
+ # -------------------------
820
+ if use_mla:
821
+ if not isinstance(kv_cache_layer, torch.Tensor):
822
+ raise ValueError("MLA expects kv_cache_layer as Tensor")
823
+ if kv_cache_layer.dim() != 3:
824
+ raise ValueError(f"MLA expects 3D [P,B,HS], got {kv_cache_layer.shape}")
825
+ p, b, hs = kv_cache_layer.shape
826
+ return kv_cache_layer.view(p * b, hs)
827
+
828
+ # -------------------------
829
+ # non-MLA (K/V)
830
+ # -------------------------
831
+ # If already provided (k, v) in canonical per-layer form, no format needed.
832
+ if not isinstance(kv_cache_layer, torch.Tensor):
833
+ k, v = kv_cache_layer
834
+ if k.dim() != 4 or v.dim() != 4:
835
+ raise ValueError(f"Expected (k,v) 4D [P,B,NH,HS], got {k.shape}, {v.shape}")
836
+ p, b, nh, hs = k.shape
837
+ if v.shape != (p, b, nh, hs):
838
+ raise ValueError(f"k/v shape mismatch: {k.shape} vs {v.shape}")
839
+ return k.view(p * b, nh * hs), v.view(p * b, nh * hs)
840
+
841
+ t = kv_cache_layer
842
+ if t.dim() != 5:
843
+ raise ValueError(f"Expected 5D tensor for non-MLA, got {t.shape}")
844
+
845
+ # If we have the format enum, decode explicitly.
846
+ if gpu_kv_format is not None:
847
+ if gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_TWO_NB_BS_NH_HS:
848
+ # per-layer: [2, NB, BS, NH, HS]
849
+ if t.shape[0] != 2:
850
+ raise ValueError(
851
+ f"{gpu_kv_format} expects [2,NB,BS,NH,HS], got {t.shape}"
852
+ )
853
+ k, v = t[0], t[1] # [NB,BS,NH,HS]
854
+
855
+ elif gpu_kv_format == lmc_ops.GPUKVFormat.NL_X_NB_TWO_BS_NH_HS:
856
+ # per-layer: [NB, 2, BS, NH, HS]
857
+ if t.shape[1] != 2:
858
+ raise ValueError(
859
+ f"{gpu_kv_format} expects [NB,2,BS,NH,HS], got {t.shape}"
860
+ )
861
+ k, v = t[:, 0], t[:, 1] # [NB,BS,NH,HS]
862
+
863
+ else:
864
+ # Other formats are either MLA-only or require upstream normalization.
865
+ raise NotImplementedError(
866
+ f"gpu_kv_format={gpu_kv_format} not supported in non-MLA path here. "
867
+ "Normalize to (k,v) tuple [NB,BS,NH,HS] per-layer before calling."
868
+ )
869
+
870
+ else:
871
+ # No enum available: Assumed [2,P,B,H,D] (or [2,NB,BS,NH,HS] per-layer).
872
+ # Also accept [P,2,B,H,D] (or [NB,2,BS,NH,HS]) to be more robust.
873
+ if t.shape[0] == 2:
874
+ k, v = t[0], t[1]
875
+ elif t.shape[1] == 2:
876
+ k, v = t[:, 0], t[:, 1]
877
+ else:
878
+ raise ValueError(
879
+ f"gpu_kv_format is None and tensor does not look like stacked KV. "
880
+ f"Expected axis0==2 or axis1==2, got {t.shape}"
881
+ )
882
+
883
+ if k.dim() != 4 or v.dim() != 4:
884
+ raise ValueError(f"Expected k/v 4D [NB,BS,NH,HS], got {k.shape}, {v.shape}")
885
+
886
+ nb, bs, nh, hs = k.shape
887
+ if v.shape != (nb, bs, nh, hs):
888
+ raise ValueError(f"k/v shape mismatch after decode: {k.shape} vs {v.shape}")
889
+
890
+ return k.view(nb * bs, nh * hs), v.view(nb * bs, nh * hs)