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,267 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from collections import defaultdict
4
+ from dataclasses import dataclass, field
5
+ from typing import Optional
6
+
7
+ # Third Party
8
+ import torch
9
+
10
+ # First Party
11
+ from lmcache.logging import init_logger
12
+
13
+ logger = init_logger(__name__)
14
+
15
+
16
+ @dataclass
17
+ class KVLayerGroupInfo:
18
+ """Information about a group of layers with the same KV cache structure.
19
+
20
+ Layers within the same group have identical shape and dtype for their KV cache.
21
+ Different groups may have different shapes (especially head_size) and/or dtypes.
22
+ """
23
+
24
+ """ List of layer names belonging to this group """
25
+ layer_names: list[str]
26
+ """ List of layer indices (0-based) belonging to this group """
27
+ layer_indices: list[int]
28
+ """ Shape of the KV cache tensor for layers in this group """
29
+ """ For MHA: typically [2, num_blocks, block_size, num_heads, head_size] """
30
+ """ For MLA: typically [num_blocks, block_size, head_size] """
31
+ shape: torch.Size
32
+ """ Data type of the KV cache tensor for layers in this group """
33
+ dtype: torch.dtype
34
+
35
+ # Internal sets for fast membership checking
36
+ _layer_indices_set: set[int] = field(init=False, repr=False)
37
+ _layer_names_set: set[str] = field(init=False, repr=False)
38
+
39
+ def __post_init__(self):
40
+ """Initialize sets for fast membership checking."""
41
+ self._layer_indices_set = set(self.layer_indices)
42
+ self._layer_names_set = set(self.layer_names)
43
+
44
+ def __repr__(self) -> str:
45
+ if not self.layer_indices:
46
+ indices_repr = "[]"
47
+ else:
48
+ indices_repr = f"{self.layer_indices[0]}-{self.layer_indices[-1]}"
49
+ return (
50
+ f"KVLayerGroupInfo(layers={len(self.layer_names)}, "
51
+ f"indices={indices_repr}, "
52
+ f"shape={self.shape}, dtype={self.dtype})"
53
+ )
54
+
55
+ @property
56
+ def num_layers(self) -> int:
57
+ """Return the number of layers in this group."""
58
+ return len(self.layer_names)
59
+
60
+ @property
61
+ def hidden_dim_size(self) -> int:
62
+ """Return the size of the hidden dimension in this group."""
63
+ # hidden_dim_size = num_heads * head_size
64
+ if len(self.shape) == 5:
65
+ # MHA
66
+ return self.shape[3] * self.shape[4]
67
+ elif len(self.shape) == 3:
68
+ # MLA
69
+ return self.shape[2]
70
+ else:
71
+ raise ValueError(f"Invalid shape: {self.shape}")
72
+
73
+ def contains_layer(self, layer_idx: int) -> bool:
74
+ """Check if a layer index belongs to this group."""
75
+ return layer_idx in self._layer_indices_set
76
+
77
+ def contains_layer_name(self, layer_name: str) -> bool:
78
+ """Check if a layer name belongs to this group."""
79
+ return layer_name in self._layer_names_set
80
+
81
+
82
+ @dataclass
83
+ class KVLayerGroupsManager:
84
+ """Manager for KV layer groups with the same structure.
85
+
86
+ This class encapsulates the functionality for managing groups of layers
87
+ that have identical KV cache structure (shape and dtype).
88
+ """
89
+
90
+ kv_layer_groups: list[KVLayerGroupInfo] = field(default_factory=list)
91
+
92
+ @property
93
+ def num_groups(self) -> int:
94
+ """Return the number of KV layer groups."""
95
+ return len(self.kv_layer_groups)
96
+
97
+ def get_group_by_layer_idx(self, layer_idx: int) -> Optional[KVLayerGroupInfo]:
98
+ """Get the KVLayerGroupInfo for a given layer index.
99
+
100
+ Args:
101
+ layer_idx: The 0-based index of the layer.
102
+
103
+ Returns:
104
+ The KVLayerGroupInfo containing this layer, or None if not found.
105
+ """
106
+ for group in self.kv_layer_groups:
107
+ if group.contains_layer(layer_idx):
108
+ return group
109
+ return None
110
+
111
+ def get_group_by_layer_name(self, layer_name: str) -> Optional[KVLayerGroupInfo]:
112
+ """Get the KVLayerGroupInfo for a given layer name.
113
+
114
+ Args:
115
+ layer_name: The name of the layer.
116
+
117
+ Returns:
118
+ The KVLayerGroupInfo containing this layer, or None if not found.
119
+ """
120
+ for group in self.kv_layer_groups:
121
+ if group.contains_layer_name(layer_name):
122
+ return group
123
+ return None
124
+
125
+ def get_layer_shape(self, layer_idx: int) -> Optional[torch.Size]:
126
+ """Get the shape of the KV cache for a given layer index.
127
+
128
+ Args:
129
+ layer_idx: The 0-based index of the layer.
130
+
131
+ Returns:
132
+ The shape, or None if layer not found.
133
+ """
134
+ group = self.get_group_by_layer_idx(layer_idx)
135
+ return group.shape if group else None
136
+
137
+ def get_layer_dtype(self, layer_idx: int) -> Optional[torch.dtype]:
138
+ """Get the dtype of the KV cache for a given layer index.
139
+
140
+ Args:
141
+ layer_idx: The 0-based index of the layer.
142
+
143
+ Returns:
144
+ The dtype, or None if layer not found.
145
+ """
146
+ group = self.get_group_by_layer_idx(layer_idx)
147
+ return group.dtype if group else None
148
+
149
+ def build_kv_layer_groups(self, kv_caches: dict[str, torch.Tensor]) -> None:
150
+ """Build KV layer groups structure by analyzing each layer's shape and dtype.
151
+
152
+ Layers with the same shape and dtype are grouped together. This is useful
153
+ because different layers may have different structures (especially the
154
+ last dimension head_size may differ between groups), and different groups
155
+ may have different dtypes.
156
+
157
+ If layer groups are already built (non-empty list), this method does nothing.
158
+
159
+ Args:
160
+ kv_caches: Dictionary mapping layer names to KV cache tensors.
161
+ """
162
+ # Skip if already built (non-empty list)
163
+ if len(self.kv_layer_groups) > 0:
164
+ return
165
+
166
+ if len(kv_caches) == 0:
167
+ logger.debug("No KV caches available, skipping KV layer groups building")
168
+ return
169
+
170
+ # Group layers by (shape, dtype) in a single loop
171
+ groups_dict: dict[tuple[torch.Size, torch.dtype], list[tuple[str, int]]] = (
172
+ defaultdict(list)
173
+ )
174
+
175
+ for idx, (layer_name, kv_cache) in enumerate(kv_caches.items()):
176
+ # Supports two KV cache formats:
177
+ # - Single-tensor format: a single tensor with shape
178
+ # [2, num_blocks, block_size, num_heads, head_size].
179
+ # - List/tuple format (e.g., TPU/HPU): [k_tensor, v_tensor],
180
+ # where each tensor has shape
181
+ # [num_blocks, block_size, num_heads, head_size].
182
+ if isinstance(kv_cache, (tuple, list)):
183
+ if len(kv_cache) != 2:
184
+ raise ValueError(
185
+ f"Expected 2 tensors (k, v) for layer {layer_name}, "
186
+ f"got {len(kv_cache)}"
187
+ )
188
+ # Prepend the count as a leading dimension to produce the
189
+ # same canonical shape as the single-tensor format
190
+ # (e.g., [2, num_blocks, ...] for k+v), so downstream
191
+ # indexing (e.g., hidden_dim_size) is unaffected.
192
+ shape = torch.Size([len(kv_cache)] + list(kv_cache[0].shape))
193
+ dtype = kv_cache[0].dtype
194
+ else:
195
+ shape = kv_cache.shape
196
+ dtype = kv_cache.dtype
197
+ key = (shape, dtype)
198
+ groups_dict[key].append((layer_name, idx))
199
+
200
+ # Build KVLayerGroupInfo list
201
+ # Sort groups by the first layer index to maintain order
202
+ def _get_first_layer_index(shape_dtype_key):
203
+ """Get the index of the first layer in a layer group."""
204
+ layer_group = groups_dict[
205
+ shape_dtype_key
206
+ ] # list of (layer_name, layer_index) tuples
207
+ first_layer_info = layer_group[0] # first (layer_name, layer_index) tuple
208
+ layer_index = first_layer_info[1] # extract the layer index
209
+ return layer_index
210
+
211
+ sorted_keys = sorted(groups_dict.keys(), key=_get_first_layer_index)
212
+
213
+ kv_layer_groups: list[KVLayerGroupInfo] = []
214
+ for shape, dtype in sorted_keys:
215
+ layers = groups_dict[(shape, dtype)]
216
+ layer_names, layer_indices = zip(*layers, strict=False)
217
+
218
+ group_info = KVLayerGroupInfo(
219
+ layer_names=list(layer_names),
220
+ layer_indices=list(layer_indices),
221
+ shape=shape,
222
+ dtype=dtype,
223
+ )
224
+ kv_layer_groups.append(group_info)
225
+
226
+ # Store the built groups
227
+ self.kv_layer_groups = kv_layer_groups
228
+
229
+ # Print the group structure
230
+ logger.info("KV layer groups: %s", kv_layer_groups)
231
+
232
+ def build_kv_layer_groups_from_list(self, kv_caches: list[torch.Tensor]) -> None:
233
+ """Build KV layer groups from a plain list of tensors (one per layer).
234
+
235
+ Layers with the same shape and dtype are grouped together.
236
+ If layer groups are already built (non-empty list), this method does nothing.
237
+
238
+ Args:
239
+ kv_caches: List of KV cache tensors, one per layer.
240
+ """
241
+ if len(self.kv_layer_groups) > 0:
242
+ return
243
+
244
+ if not kv_caches:
245
+ logger.debug("No KV caches available, skipping KV layer groups building")
246
+ return
247
+
248
+ groups_dict: dict[tuple[torch.Size, torch.dtype], list[int]] = defaultdict(list)
249
+ for idx, tensor in enumerate(kv_caches):
250
+ key = (tensor.shape, tensor.dtype)
251
+ groups_dict[key].append(idx)
252
+
253
+ sorted_keys = sorted(groups_dict.keys(), key=lambda k: groups_dict[k][0])
254
+
255
+ kv_layer_groups: list[KVLayerGroupInfo] = []
256
+ for shape, dtype in sorted_keys:
257
+ indices = groups_dict[(shape, dtype)]
258
+ group_info = KVLayerGroupInfo(
259
+ layer_names=[str(i) for i in indices],
260
+ layer_indices=indices,
261
+ shape=shape,
262
+ dtype=dtype,
263
+ )
264
+ kv_layer_groups.append(group_info)
265
+
266
+ self.kv_layer_groups = kv_layer_groups
267
+ logger.info("KV layer groups (from list): %s", kv_layer_groups)
@@ -0,0 +1,284 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import List, Optional, Union
4
+ import ctypes
5
+ import threading
6
+
7
+ # Third Party
8
+ import torch
9
+
10
+ # First Party
11
+ from lmcache.logging import init_logger
12
+ from lmcache.v1.memory_management import (
13
+ AddressManager,
14
+ MemoryAllocatorInterface,
15
+ MemoryFormat,
16
+ MemoryObj,
17
+ TensorMemoryAllocator,
18
+ )
19
+ from lmcache.v1.system_detection import NUMAMapping
20
+ import lmcache.c_ops as lmc_ops
21
+
22
+ logger = init_logger(__name__)
23
+
24
+
25
+ # Helper functions
26
+ def get_numa_id(numa_mapping: NUMAMapping) -> int:
27
+ """
28
+ Get the NUMA ID for the current GPU
29
+
30
+ Args:
31
+ numa_mapping (NUMAMapping): The NUMA mapping object.
32
+
33
+ Returns:
34
+ int: The NUMA ID for the current GPU.
35
+
36
+ Raises:
37
+ KeyError: If GPU id is not detected in the numa mapping.
38
+ """
39
+ gpu_id = torch.cuda.current_device() if torch.cuda.is_available() else 0
40
+ return numa_mapping.gpu_to_numa_mapping[gpu_id]
41
+
42
+
43
+ def align_to(size: int, align_size: int) -> int:
44
+ """
45
+ Align the given size to the nearest multiple of align_size.
46
+
47
+ Args:
48
+ size (int): The size to align.
49
+ align_size (int): The alignment size, MUST BE a power of two.
50
+
51
+ Returns:
52
+ int: The aligned size.
53
+ """
54
+ return (size + align_size - 1) & (~(align_size - 1))
55
+
56
+
57
+ # Main class
58
+ class LazyMemoryAllocator(MemoryAllocatorInterface):
59
+ """
60
+ Allocates CPU (numa) pinned memory with a initial size and expand
61
+ the size to the required size in the background.
62
+
63
+ Background expansion logic:
64
+ - After registering X GB memory, we call sbrk and updates _curr_size
65
+ - Once everything is registered, the background thread stops
66
+ """
67
+
68
+ PIN_CHUNK_SIZE = 1 << 26 # 64 MB pin chunk
69
+ COMMIT_SIZE = 1 << 30 # Do a commit every 1 GB
70
+ LOG_INTERVAL = 10 << 30 # Log expansion progress every 10 GB
71
+
72
+ def __init__(
73
+ self,
74
+ init_size: int,
75
+ final_size: int,
76
+ align_bytes: int = AddressManager.ALIGN_BYTES,
77
+ numa_mapping: NUMAMapping | None = None,
78
+ ):
79
+ """
80
+ Args:
81
+ init_size (int): Initial size of the memory allocation in bytes.
82
+ final_size (int): Final size of the memory allocation in bytes.
83
+ align_bytes (int, optional): Alignment in for the underlying allocations
84
+ """
85
+ # Whether using NUMA allocation
86
+ self._use_numa = numa_mapping is not None
87
+ # Currently pinned size, only accessed by the expansion thread
88
+ self._curr_size = align_to(init_size, self.PIN_CHUNK_SIZE)
89
+ # Final size of the allocation, only accessed by the expansion thread
90
+ self._final_size = align_to(final_size, self.PIN_CHUNK_SIZE)
91
+ # Underlying buffer for the memory allocation
92
+ self._buffer: torch.Tensor
93
+ # CUDA runtime API
94
+ self._cudart = torch.cuda.cudart()
95
+
96
+ # List of (ptr, size) for pinned memory chunks
97
+ self._pin_record: list[tuple[int, int]] = []
98
+
99
+ # Detect numa mapping
100
+ if numa_mapping is not None:
101
+ numa_id = get_numa_id(numa_mapping)
102
+ ptr = lmc_ops.alloc_numa_ptr(self._final_size, numa_id)
103
+ arr_type = ctypes.c_uint8 * self._final_size
104
+ buf = arr_type.from_address(ptr)
105
+ self._buffer = torch.frombuffer(buf, dtype=torch.uint8)
106
+ else:
107
+ self._buffer = torch.empty(
108
+ self._final_size, dtype=torch.uint8, device="cpu", pin_memory=False
109
+ )
110
+
111
+ # Pin the first `curr_size` bytes (aligned to the internal chunk size)
112
+ self._pin_memory_chunk(0, self._curr_size)
113
+
114
+ # Create the tensor memory allocator
115
+ self._allocator = TensorMemoryAllocator(
116
+ tensor=self._buffer,
117
+ align_bytes=align_bytes,
118
+ init_address_space=self._curr_size,
119
+ )
120
+
121
+ # Get the address manager
122
+ # NOTE(ApostaC): this assumes the tensor memory allocator owns the address
123
+ # manager, which creates extra coupling in the code.
124
+ # NOTE(ApostaC): this also assumes that the behavior of the allocation is
125
+ # completely determined by the address manager.
126
+ self._address_manager = self._allocator.address_manager
127
+
128
+ # Launch the background expansion thread
129
+ self._stop_expand = threading.Event()
130
+ self._expand_thread = threading.Thread(
131
+ target=self._expand_worker, daemon=True, name="lazy-mem-expand-thread"
132
+ )
133
+ self._expand_thread.start()
134
+
135
+ # Public methods
136
+ def allocate(
137
+ self,
138
+ shapes: Union[torch.Size, list[torch.Size]],
139
+ dtypes: Union[torch.dtype, list[torch.dtype]],
140
+ fmt: MemoryFormat = MemoryFormat.UNDEFINED,
141
+ allocator_type: Optional[str] = None,
142
+ ) -> Optional[MemoryObj]:
143
+ obj = self._allocator.allocate(shapes, dtypes, fmt, allocator_type)
144
+ # HACK(ApostaC): reset the parent allocator to this lazy allocator
145
+ # There should be a cleaner way to decouple lazy allocator and
146
+ # tensor memory allocator
147
+ if obj is not None:
148
+ obj.parent_allocator = self
149
+ return obj
150
+
151
+ def batched_allocate(
152
+ self,
153
+ shapes: Union[torch.Size, list[torch.Size]],
154
+ dtypes: Union[torch.dtype, list[torch.dtype]],
155
+ batch_size: int,
156
+ fmt: MemoryFormat = MemoryFormat.UNDEFINED,
157
+ allocator_type: Optional[str] = None,
158
+ ) -> Optional[List[MemoryObj]]:
159
+ # HACK(ApostaC): reset the parent allocator to this lazy allocator
160
+ # There should be a cleaner way to decouple lazy allocator and
161
+ # tensor memory allocator
162
+ ret = self._allocator.batched_allocate(
163
+ shapes, dtypes, batch_size, fmt, allocator_type
164
+ )
165
+
166
+ if ret is None:
167
+ return ret
168
+
169
+ for obj in ret:
170
+ obj.parent_allocator = self
171
+ return ret
172
+
173
+ def free(
174
+ self,
175
+ memory_obj: MemoryObj,
176
+ allocator_type: Optional[str] = None,
177
+ ):
178
+ self._allocator.free(memory_obj, allocator_type)
179
+
180
+ def batched_free(
181
+ self,
182
+ memory_objs: List[MemoryObj],
183
+ allocator_type: Optional[str] = None,
184
+ update_stats: bool = True,
185
+ ):
186
+ self._allocator.batched_free(memory_objs, allocator_type, update_stats)
187
+
188
+ def close(self):
189
+ # Stop the background expansion thread
190
+ self._stop_expand.set()
191
+ self._expand_thread.join()
192
+
193
+ # Unpin all pinned memory chunks
194
+ for ptr, size in self._pin_record:
195
+ self._cudart.cudaHostUnregister(ptr)
196
+ self._pin_record.clear()
197
+
198
+ # Free the underlying buffer if using NUMA allocation
199
+ if self._use_numa:
200
+ lmc_ops.free_numa_ptr(self._buffer.data_ptr(), self._final_size)
201
+
202
+ def memcheck(self) -> bool:
203
+ return self._allocator.memcheck()
204
+
205
+ def get_underlying_buffer(self) -> torch.Tensor:
206
+ """
207
+ Get the underlying buffer tensor. Will be used by RDMA registrations.
208
+ """
209
+ return self._buffer
210
+
211
+ def get_address_manager(self) -> AddressManager:
212
+ """
213
+ Get the address manager used by this allocator.
214
+ """
215
+ return self._address_manager
216
+
217
+ # Helper functions
218
+ def _pin_memory_chunk(self, offset: int, size: int):
219
+ """
220
+ Pin a chunk of memory.
221
+
222
+ Args:
223
+ offset (int): Offset in the buffer to pin.
224
+ size (int): Size of the memory chunk in bytes.
225
+ """
226
+ assert offset & (self.PIN_CHUNK_SIZE - 1) == 0, (
227
+ "Offset must be aligned to PIN_CHUNK_SIZE"
228
+ )
229
+ assert size & (self.PIN_CHUNK_SIZE - 1) == 0, (
230
+ "Size must be aligned to PIN_CHUNK_SIZE"
231
+ )
232
+ assert offset + size <= self._final_size, "Pinning exceeds buffer size"
233
+
234
+ ptr = self._buffer.data_ptr() + offset
235
+ # Use flag: cudaHostRegisterMapped (0x02)
236
+ self._cudart.cudaHostRegister(ptr, size, 2)
237
+ self._pin_record.append((ptr, size))
238
+
239
+ def _commit_expansion(self, expand_size: int):
240
+ """
241
+ Call sbrk in the address manager to commit the expansion.
242
+ """
243
+ self._address_manager.sbrk(expand_size)
244
+
245
+ def _log_expansion_progress(self, expanded_since_last_log: int):
246
+ """
247
+ Log the cumulative expansion progress since the last log.
248
+ """
249
+ percent = 100.0 * self._curr_size / self._final_size
250
+ logger.info(
251
+ "LazyMemoryAllocator: Expanded %s MB pinned memory, "
252
+ "now total is %s MB / %s MB (%.1f%%)",
253
+ expanded_since_last_log >> 20,
254
+ self._curr_size >> 20,
255
+ self._final_size >> 20,
256
+ percent,
257
+ )
258
+
259
+ def _expand_worker(self):
260
+ """
261
+ Background worker to expand the pinned memory.
262
+ """
263
+ last_commit_size = self._curr_size
264
+ last_log_size = self._curr_size
265
+ while self._curr_size < self._final_size and not self._stop_expand.is_set():
266
+ # Expand chunk by chunk and commit
267
+ for i in range(self.COMMIT_SIZE // self.PIN_CHUNK_SIZE):
268
+ if self._curr_size >= self._final_size:
269
+ break
270
+ self._pin_memory_chunk(self._curr_size, self.PIN_CHUNK_SIZE)
271
+ self._curr_size += self.PIN_CHUNK_SIZE
272
+
273
+ expand_size = self._curr_size - last_commit_size
274
+ self._commit_expansion(expand_size)
275
+ last_commit_size = self._curr_size
276
+
277
+ # Log every LOG_INTERVAL bytes, and always on the final commit.
278
+ expanded_since_last_log = self._curr_size - last_log_size
279
+ if (
280
+ expanded_since_last_log >= self.LOG_INTERVAL
281
+ or self._curr_size >= self._final_size
282
+ ):
283
+ self._log_expansion_progress(expanded_since_last_log)
284
+ last_log_size = self._curr_size
@@ -0,0 +1,25 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # First Party
3
+ from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
4
+ from lmcache.v1.lookup_client.chunk_statistics_lookup_client import (
5
+ ChunkStatisticsLookupClient,
6
+ )
7
+ from lmcache.v1.lookup_client.factory import LookupClientFactory
8
+ from lmcache.v1.lookup_client.lmcache_lookup_client import (
9
+ LMCacheLookupClient,
10
+ LMCacheLookupServer,
11
+ )
12
+ from lmcache.v1.lookup_client.lmcache_lookup_client_bypass import (
13
+ LMCacheBypassLookupClient,
14
+ )
15
+ from lmcache.v1.lookup_client.mooncake_lookup_client import MooncakeLookupClient
16
+
17
+ __all__ = [
18
+ "LookupClientInterface",
19
+ "LookupClientFactory",
20
+ "MooncakeLookupClient",
21
+ "LMCacheBypassLookupClient",
22
+ "LMCacheLookupClient",
23
+ "LMCacheLookupServer",
24
+ "ChunkStatisticsLookupClient",
25
+ ]
@@ -0,0 +1,77 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Optional, Union
4
+ import abc
5
+
6
+ # Third Party
7
+ import torch
8
+
9
+
10
+ class LookupClientInterface(metaclass=abc.ABCMeta):
11
+ """Abstract interface for lookup clients."""
12
+
13
+ def lookup_cache(self, lookup_id: str) -> Optional[int]:
14
+ """
15
+ Lookup the cache for the given lookup ID.
16
+
17
+ Args:
18
+ lookup_id: The lookup ID to lookup
19
+
20
+ Returns:
21
+ -1 means not found;
22
+ None means ongoing; (this semantic is not supported in sync lookup clients)
23
+ int >= 0 means number of hit tokens
24
+ """
25
+ return None
26
+
27
+ @abc.abstractmethod
28
+ def lookup(
29
+ self,
30
+ token_ids: Union[torch.Tensor, list[int]],
31
+ lookup_id: str,
32
+ request_configs: Optional[dict] = None,
33
+ ) -> Optional[int]:
34
+ """
35
+ Perform lookup for the given token IDs.
36
+ Should be called for first lookup and pinning. Subsequent lookups for the same
37
+ request should call lookup_cache instead.
38
+
39
+ Caller should handle overlaps between tokens that exist in LMCache
40
+ and tokens that are already computed by the caller.
41
+
42
+ Args:
43
+ token_ids: The token IDs to lookup
44
+
45
+ lookup_id: The lookup ID to associate with the lookup
46
+
47
+ request_configs: The configs of the request,
48
+ includes tags and the other configs
49
+
50
+ Returns:
51
+ The number of tokens that exist inside LMCache.
52
+ None indicates the lookup/prefetch is in progress.
53
+ """
54
+ raise NotImplementedError
55
+
56
+ @abc.abstractmethod
57
+ def close(self) -> None:
58
+ """Close the lookup client and clean up resources."""
59
+ raise NotImplementedError
60
+
61
+ def supports_producer_reuse(self) -> bool:
62
+ """
63
+ Return whether this lookup client supports producer KV cache reuse.
64
+
65
+ Returns:
66
+ True if producer reuse is supported, False otherwise
67
+ """
68
+ return False
69
+
70
+ def clear_lookup_status(self, lookup_id: str) -> None:
71
+ """
72
+ Clear temporary lookup status for a given lookup ID.
73
+
74
+ Args:
75
+ lookup_id: The lookup ID whose status needs to be cleared.
76
+ """
77
+ return
@@ -0,0 +1,50 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Dict, Optional
4
+
5
+ # Third Party
6
+ import msgspec
7
+
8
+
9
+ class AsyncLookupMsg(msgspec.Struct, tag=True): # type: ignore
10
+ """Base class for async lookup messages"""
11
+
12
+ def describe(self) -> str:
13
+ return ""
14
+
15
+
16
+ class LookupRequestMsg(AsyncLookupMsg):
17
+ """Async lookup request message from scheduler to worker"""
18
+
19
+ lookup_id: str
20
+ hashes: list[int]
21
+ offsets: list[int]
22
+ request_configs: Optional[Dict[str, str]] = None
23
+
24
+ def describe(self) -> str:
25
+ return (
26
+ f"Async lookup request for lookup_id={self.lookup_id} "
27
+ f"with {len(self.hashes)} hashes"
28
+ )
29
+
30
+
31
+ class LookupResponseMsg(AsyncLookupMsg):
32
+ """Async lookup response message from worker to scheduler"""
33
+
34
+ lookup_id: str
35
+ num_hit_tokens: int
36
+
37
+ def describe(self) -> str:
38
+ return (
39
+ f"Async lookup response for lookup_id={self.lookup_id} "
40
+ f"with {self.num_hit_tokens} hit tokens"
41
+ )
42
+
43
+
44
+ class LookupCleanupMsg(AsyncLookupMsg):
45
+ """Cleanup message from scheduler to worker to release memory objects"""
46
+
47
+ lookup_id: str
48
+
49
+ def describe(self) -> str:
50
+ return f"Cleanup memory for lookup_id={self.lookup_id}"