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
lmcache/utils.py ADDED
@@ -0,0 +1,665 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Future
3
+ from __future__ import annotations
4
+
5
+ # Standard
6
+ from dataclasses import dataclass, field
7
+ from enum import Enum
8
+ from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
9
+ import asyncio
10
+ import hashlib
11
+ import re
12
+ import threading
13
+ import traceback
14
+
15
+ try:
16
+ # Third Party
17
+ from nvtx import annotate # type: ignore
18
+ except ImportError:
19
+
20
+ def annotate(*args, **kwargs):
21
+ """Dummy decorator when nvtx is not available."""
22
+
23
+ def decorator(func):
24
+ return func
25
+
26
+ return decorator
27
+
28
+
29
+ # Third Party
30
+ import torch
31
+
32
+ # First Party
33
+ from lmcache.logging import init_logger
34
+
35
+ if TYPE_CHECKING:
36
+ # First Party
37
+ from lmcache.v1.memory_management import MemoryFormat
38
+
39
+ logger = init_logger(__name__)
40
+
41
+ # Type definition
42
+ KVCache = Tuple[Tuple[torch.Tensor, torch.Tensor], ...]
43
+
44
+
45
+ # Math utility functions
46
+ def cdiv(a: int, b: int) -> int:
47
+ """Ceiling division."""
48
+ return -(a // -b)
49
+
50
+
51
+ def round_down(x: int, y: int) -> int:
52
+ """Round down x to the nearest multiple of y."""
53
+ return (x // y) * y
54
+
55
+
56
+ def compress_slot_mapping(slots: list[int]) -> list[Union[int, list[int]]]:
57
+ """Compress a list of slot indices into ranges while preserving order.
58
+
59
+ Consecutive slots (3 or more) are represented as [start, end] ranges.
60
+ Single elements or pairs are kept as individual integers.
61
+ For example: [1, 2, 3, 4, 5, 9, 10, 11, 12] -> [[1, 5], [9, 12]]
62
+ Order-preserving: [5, 3, 1, 2, 4] -> [5, 3, 1, 2, 4] (no compression)
63
+ Mixed: [1, 2, 3, 4, 5, 7, 8] -> [[1, 5], 7, 8]
64
+
65
+ Args:
66
+ slots: List of slot indices (order is preserved).
67
+
68
+ Returns:
69
+ List of integers or [start, end] ranges. Ranges are only used
70
+ when there are 3 or more consecutive elements.
71
+ """
72
+ if not slots:
73
+ return []
74
+
75
+ result: list[Union[int, list[int]]] = []
76
+ range_start = slots[0]
77
+ range_end = slots[0]
78
+
79
+ for slot in slots[1:]:
80
+ if slot == range_end + 1:
81
+ # Extend current range
82
+ range_end = slot
83
+ else:
84
+ # Close current range and start a new one
85
+ _append_range_or_elements(result, range_start, range_end)
86
+ range_start = slot
87
+ range_end = slot
88
+
89
+ # Append the last range
90
+ _append_range_or_elements(result, range_start, range_end)
91
+ return result
92
+
93
+
94
+ def _append_range_or_elements(
95
+ result: list[Union[int, list[int]]], start: int, end: int
96
+ ) -> None:
97
+ """Helper to append range or individual elements based on length.
98
+
99
+ Only compresses to [start, end] if there are 3 or more consecutive elements.
100
+ """
101
+ length = end - start + 1
102
+ if length >= 3:
103
+ # Compress: 3 or more consecutive elements
104
+ result.append([start, end])
105
+ else:
106
+ # Don't compress: 1 or 2 elements
107
+ for i in range(start, end + 1):
108
+ result.append(i)
109
+
110
+
111
+ def decompress_slot_mapping(compressed: list[Union[int, list[int]]]) -> list[int]:
112
+ """Decompress slot ranges back to a list of slot indices.
113
+
114
+ Inverse operation of compress_slot_mapping.
115
+ For example: [[1, 5], [9, 12]] -> [1, 2, 3, 4, 5, 9, 10, 11, 12]
116
+ Mixed: [[1, 5], 7, 8] -> [1, 2, 3, 4, 5, 7, 8]
117
+
118
+ Args:
119
+ compressed: List of integers or [start, end] ranges from
120
+ compress_slot_mapping.
121
+
122
+ Returns:
123
+ List of slot indices.
124
+ """
125
+ slots: list[int] = []
126
+ for item in compressed:
127
+ if isinstance(item, list):
128
+ start, end = item
129
+ slots.extend(range(start, end + 1))
130
+ else:
131
+ slots.append(item)
132
+ return slots
133
+
134
+
135
+ def parse_mixed_slot_mapping(
136
+ slot_mapping_str: str,
137
+ ) -> Tuple[Optional[list[int]], Optional[dict]]:
138
+ """Parse mixed format slot_mapping string.
139
+
140
+ Supports two formats:
141
+ 1. Single numbers: "1,2,3,17,19"
142
+ 2. Range format: "[9,12]" (represents 9,10,11,12)
143
+ 3. Mixed format: "1,2,3,[9,12],17,19" (represents 1,2,3,9,10,11,12,17,19)
144
+
145
+ Args:
146
+ slot_mapping_str: String containing slot mapping information.
147
+
148
+ Returns:
149
+ Tuple of (slot_indices list, error dict).
150
+ If error dict is not None, slot_indices will be None.
151
+ """
152
+ try:
153
+ # Remove all whitespace
154
+ clean_str = "".join(slot_mapping_str.split())
155
+
156
+ # Split by comma but preserve range expressions
157
+ parts = []
158
+ buffer = ""
159
+ in_brackets = False
160
+
161
+ for char in clean_str:
162
+ if char == "[":
163
+ if in_brackets:
164
+ raise ValueError("Nested brackets not allowed")
165
+ in_brackets = True
166
+ buffer += char
167
+ elif char == "]":
168
+ if not in_brackets:
169
+ raise ValueError("Unmatched closing bracket")
170
+ in_brackets = False
171
+ buffer += char
172
+ parts.append(buffer)
173
+ buffer = ""
174
+ elif char == "," and not in_brackets:
175
+ if buffer:
176
+ parts.append(buffer)
177
+ buffer = ""
178
+ else:
179
+ buffer += char
180
+
181
+ # Add the last part if any
182
+ if buffer:
183
+ parts.append(buffer)
184
+
185
+ if in_brackets:
186
+ raise ValueError("Unclosed bracket")
187
+
188
+ # Parse each part
189
+ compressed: list[Union[int, list[int]]] = []
190
+
191
+ for part in parts:
192
+ part = part.strip()
193
+ if not part:
194
+ continue
195
+
196
+ # Check if it's a range format [start,end]
197
+ range_match = re.match(r"^\[(\d+),(\d+)\]$", part)
198
+ if range_match:
199
+ start = int(range_match.group(1))
200
+ end = int(range_match.group(2))
201
+ if start > end:
202
+ raise ValueError(f"Range start {start} must be <= end {end}")
203
+ compressed.append([start, end])
204
+ else:
205
+ # Single number
206
+ try:
207
+ num = int(part)
208
+ compressed.append(num)
209
+ except ValueError as ve:
210
+ raise ValueError(f"Invalid slot format: '{part}'") from ve
211
+
212
+ # Decompress to individual slot indices
213
+ slot_indices = decompress_slot_mapping(compressed)
214
+ return slot_indices, None
215
+
216
+ except Exception as e:
217
+ return None, {
218
+ "error": "Invalid slot_mapping format",
219
+ "message": (
220
+ f"slot_mapping must be comma-separated integers "
221
+ f"or ranges like [start,end]: {str(e)}"
222
+ ),
223
+ }
224
+
225
+
226
+ try:
227
+ # First Party
228
+ from lmcache import _version # type: ignore[attr-defined]
229
+
230
+ VERSION = getattr(_version, "__version__", "")
231
+ COMMIT_ID = getattr(_version, "__commit_id__", "")
232
+ except ImportError:
233
+ VERSION = ""
234
+ COMMIT_ID = ""
235
+
236
+
237
+ def get_version():
238
+ version_display = VERSION if VERSION else "NA"
239
+ commit_id_display = COMMIT_ID if COMMIT_ID else "NA"
240
+ return f"{version_display}-{commit_id_display}"
241
+
242
+
243
+ def convert_tokens_to_list(
244
+ tokens: Optional[Union[torch.Tensor, list[int]]], token_start: int, token_end: int
245
+ ) -> List[int]:
246
+ """Convert tokens to a list.
247
+ token_start and token_end delineate tokens to convert"""
248
+ if tokens is None:
249
+ return []
250
+
251
+ return (
252
+ tokens.tolist()[token_start : token_end + 1]
253
+ if isinstance(tokens, torch.Tensor)
254
+ else tokens[token_start : token_end + 1]
255
+ )
256
+
257
+
258
+ @dataclass
259
+ class DiskCacheMetadata:
260
+ path: str
261
+ size: int # in bytes
262
+ shape: Optional[torch.Size] = None
263
+ dtype: Optional[torch.dtype] = None
264
+ cached_positions: Optional[torch.Tensor] = None
265
+ fmt: Optional[MemoryFormat] = None
266
+ pin_count: int = 0
267
+
268
+ def pin(self) -> bool:
269
+ self.pin_count += 1
270
+ return True
271
+
272
+ def unpin(self) -> bool:
273
+ self.pin_count -= 1
274
+ return True
275
+
276
+ @property
277
+ def is_pinned(self) -> bool:
278
+ return self.pin_count > 0
279
+
280
+ @property
281
+ def can_evict(self) -> bool:
282
+ """
283
+ Check if the disk cache can be evicted.
284
+ """
285
+ return not self.is_pinned
286
+
287
+
288
+ TORCH_DTYPE_TO_STR_DTYPE = {
289
+ torch.half: "half",
290
+ torch.float16: "half",
291
+ torch.bfloat16: "bfloat16",
292
+ torch.float: "float",
293
+ torch.float32: "float",
294
+ torch.double: "double",
295
+ torch.float64: "double",
296
+ torch.int8: "int8",
297
+ torch.uint8: "uint8",
298
+ torch.int16: "int16",
299
+ torch.int32: "int32",
300
+ torch.int64: "int64",
301
+ torch.bool: "bool",
302
+ }
303
+
304
+ # FP8 variants (PyTorch ≥2.1)
305
+ if hasattr(torch, "float8_e4m3fn"):
306
+ TORCH_DTYPE_TO_STR_DTYPE[torch.float8_e4m3fn] = "fp8_e4m3fn"
307
+ if hasattr(torch, "float8_e4m3fnuz"):
308
+ TORCH_DTYPE_TO_STR_DTYPE[torch.float8_e4m3fnuz] = "fp8_e4m3fnuz"
309
+ if hasattr(torch, "float8_e5m2"):
310
+ TORCH_DTYPE_TO_STR_DTYPE[torch.float8_e5m2] = "fp8_e5m2"
311
+ if hasattr(torch, "float8_e5m2fnuz"):
312
+ TORCH_DTYPE_TO_STR_DTYPE[torch.float8_e5m2fnuz] = "fp8_e5m2fnuz"
313
+
314
+ STR_DTYPE_TO_TORCH_DTYPE = {v: k for k, v in TORCH_DTYPE_TO_STR_DTYPE.items()}
315
+
316
+
317
+ def parse_cache_key(key_str: str) -> Union[CacheEngineKey, LayerCacheEngineKey]:
318
+ """Parse a key string into either a CacheEngineKey or LayerCacheEngineKey.
319
+
320
+ Args:
321
+ key_str: String in format:
322
+ CacheEngineKey:
323
+ model_name@world_size@worker_id@chunk_hash@dtype[@tag%value...]
324
+ LayerCacheEngineKey:
325
+ model_name@world_size@worker_id@chunk_hash@dtype@layer_id[@tag%value...]
326
+
327
+ Returns:
328
+ CacheEngineKey if no layer_id, LayerCacheEngineKey if valid layer_id
329
+ """
330
+ parts = key_str.strip().split("@")
331
+ # parts[0]=model, [1]=world_size, [2]=worker_id, [3]=chunk_hash, [4]=dtype
332
+ # parts[5]=layer_id OR tag%value
333
+ # If parts[5] exists and is a digit (not containing '%'), it's a LayerCacheEngineKey
334
+ if len(parts) >= 6 and parts[5].isdigit():
335
+ return LayerCacheEngineKey.from_string(key_str)
336
+ return CacheEngineKey.from_string(key_str)
337
+
338
+
339
+ @dataclass(slots=True)
340
+ class CacheEngineKey:
341
+ model_name: str
342
+ world_size: int
343
+ worker_id: int
344
+ chunk_hash: int
345
+ dtype: torch.dtype
346
+ request_configs: Optional[dict] = field(default_factory=dict)
347
+ tags: Optional[tuple] = field(init=False, default=None)
348
+ _dtype_str: str = field(init=False, default="")
349
+
350
+ def __post_init__(self):
351
+ tag_list = None
352
+ if self.request_configs is not None:
353
+ for k, v in self.request_configs.items():
354
+ if k.startswith("lmcache.tag."):
355
+ if tag_list is None:
356
+ tag_list = []
357
+ tag_list.append((k[len("lmcache.tag.") :], v))
358
+ if self.dtype not in TORCH_DTYPE_TO_STR_DTYPE:
359
+ raise ValueError(f"Unsupported dtype in CacheEngineKey: {self.dtype}")
360
+ self._dtype_str = TORCH_DTYPE_TO_STR_DTYPE[self.dtype]
361
+ # use tuple to save tags
362
+ self.tags = None if tag_list is None else tuple(tag_list)
363
+
364
+ def __hash__(self):
365
+ return hash(
366
+ (
367
+ self.model_name,
368
+ self.world_size,
369
+ self.worker_id,
370
+ self.chunk_hash,
371
+ self._dtype_str,
372
+ self.tags,
373
+ )
374
+ )
375
+
376
+ def __eq__(self, other):
377
+ if type(self) is type(other):
378
+ return (
379
+ self.model_name == other.model_name
380
+ and self.world_size == other.world_size
381
+ and self.worker_id == other.worker_id
382
+ and self.chunk_hash == other.chunk_hash
383
+ and self.dtype == other.dtype
384
+ and self.tags == other.tags
385
+ )
386
+
387
+ return False
388
+
389
+ def to_string(self):
390
+ s = (
391
+ f"{self.model_name}@{self.world_size}"
392
+ f"@{self.worker_id}@{self.chunk_hash_hex}@{self._dtype_str}"
393
+ )
394
+ if self.tags is not None and len(self.tags) != 0:
395
+ tags = [f"{k}%{v}" for k, v in self.tags]
396
+ s += "@" + "@".join(tags)
397
+ return s
398
+
399
+ def split_layers(self, num_layers: int) -> List["LayerCacheEngineKey"]:
400
+ """Split the key into multiple keys for each layer"""
401
+ keys = []
402
+ for layer_id in range(num_layers):
403
+ keys.append(
404
+ LayerCacheEngineKey(
405
+ model_name=self.model_name,
406
+ world_size=self.world_size,
407
+ worker_id=self.worker_id,
408
+ chunk_hash=self.chunk_hash,
409
+ dtype=self.dtype,
410
+ request_configs=self.request_configs,
411
+ layer_id=layer_id,
412
+ )
413
+ )
414
+ return keys
415
+
416
+ def get_first_layer(self) -> "LayerCacheEngineKey":
417
+ """Return the key for the first layer"""
418
+ key = LayerCacheEngineKey(
419
+ model_name=self.model_name,
420
+ world_size=self.world_size,
421
+ worker_id=self.worker_id,
422
+ chunk_hash=self.chunk_hash,
423
+ dtype=self.dtype,
424
+ request_configs=self.request_configs,
425
+ layer_id=0,
426
+ )
427
+ return key
428
+
429
+ @staticmethod
430
+ def from_string(s):
431
+ parts = s.split("@")
432
+ if len(parts) < 5:
433
+ raise ValueError(f"Invalid key string: {s}")
434
+ request_configs = None
435
+ if len(parts) >= 6:
436
+ request_configs = {}
437
+ for kv in parts[5:]:
438
+ kvs = kv.split("%", 1)
439
+ if len(kvs) != 2:
440
+ raise ValueError(f"Invalid key string: {s}")
441
+ request_configs["lmcache.tag." + kvs[0]] = kvs[1]
442
+ return CacheEngineKey(
443
+ model_name=parts[0],
444
+ world_size=int(parts[1]),
445
+ worker_id=int(parts[2]),
446
+ chunk_hash=int(parts[3], 16),
447
+ dtype=STR_DTYPE_TO_TORCH_DTYPE[parts[4]],
448
+ request_configs=request_configs,
449
+ )
450
+
451
+ def to_dict(self):
452
+ # Note(Kuntai): this is used for serializing CacheEngineKey via msgpack.
453
+ msg = {
454
+ "__type__": "CacheEngineKey",
455
+ "model_name": self.model_name,
456
+ "world_size": self.world_size,
457
+ "worker_id": self.worker_id,
458
+ "chunk_hash": self.chunk_hash,
459
+ "dtype": self._dtype_str,
460
+ }
461
+ if self.request_configs is not None and len(self.request_configs) != 0:
462
+ msg["request_configs"] = [
463
+ f"{k}%{v}" for k, v in self.request_configs.items()
464
+ ]
465
+ return msg
466
+
467
+ @staticmethod
468
+ def from_dict(d):
469
+ request_configs = None
470
+ if request_configs_list := d.get("request_configs"):
471
+ request_configs = {}
472
+ for kv in request_configs_list:
473
+ kvs = kv.split("%", 1)
474
+ if len(kvs) != 2:
475
+ raise ValueError(f"Invalid key dict: {d}")
476
+ request_configs[kvs[0]] = kvs[1]
477
+ return CacheEngineKey(
478
+ model_name=d["model_name"],
479
+ world_size=d["world_size"],
480
+ worker_id=d["worker_id"],
481
+ chunk_hash=d["chunk_hash"],
482
+ dtype=STR_DTYPE_TO_TORCH_DTYPE[d["dtype"]],
483
+ request_configs=request_configs,
484
+ )
485
+
486
+ def with_new_worker_id(self, new_worker_id: int) -> "CacheEngineKey":
487
+ # Reconstruct the cache engine key with new worker id
488
+ return CacheEngineKey(
489
+ self.model_name,
490
+ world_size=self.world_size,
491
+ worker_id=new_worker_id,
492
+ chunk_hash=self.chunk_hash,
493
+ dtype=self.dtype,
494
+ request_configs=self.request_configs,
495
+ )
496
+
497
+ @property
498
+ def chunk_hash_hex(self) -> str:
499
+ if isinstance(self.chunk_hash, bytes):
500
+ return self.chunk_hash.hex()
501
+ return f"{self.chunk_hash:x}"
502
+
503
+
504
+ @dataclass(slots=True)
505
+ class LayerCacheEngineKey(CacheEngineKey):
506
+ """A key for the layer cache engine"""
507
+
508
+ layer_id: int = 0
509
+
510
+ def __hash__(self):
511
+ return hash(
512
+ (
513
+ self.model_name,
514
+ self.world_size,
515
+ self.worker_id,
516
+ self.chunk_hash,
517
+ self._dtype_str,
518
+ self.tags,
519
+ self.layer_id,
520
+ )
521
+ )
522
+
523
+ def __eq__(self, other):
524
+ if super(LayerCacheEngineKey, self).__eq__(other):
525
+ return self.layer_id == other.layer_id
526
+
527
+ return False
528
+
529
+ def to_string(self):
530
+ s = (
531
+ f"{self.model_name}@{self.world_size}"
532
+ f"@{self.worker_id}@{self.chunk_hash_hex}@{self._dtype_str}@{self.layer_id}"
533
+ )
534
+ if self.tags is not None and len(self.tags) != 0:
535
+ tags = [f"{k}%{v}" for k, v in self.tags]
536
+ s += "@" + "@".join(tags)
537
+ return s
538
+
539
+ def split_layers(self, num_layers: int) -> List["LayerCacheEngineKey"]:
540
+ """Split the key into multiple keys for each layer"""
541
+ keys = []
542
+ for layer_id in range(num_layers):
543
+ keys.append(
544
+ LayerCacheEngineKey(
545
+ model_name=self.model_name,
546
+ world_size=self.world_size,
547
+ worker_id=self.worker_id,
548
+ chunk_hash=self.chunk_hash,
549
+ dtype=self.dtype,
550
+ request_configs=self.request_configs,
551
+ layer_id=layer_id,
552
+ )
553
+ )
554
+ return keys
555
+
556
+ @staticmethod
557
+ def from_string(s):
558
+ parts = s.split("@")
559
+ if len(parts) < 6:
560
+ raise ValueError(f"Invalid key string: {s}")
561
+ request_configs = None
562
+ if len(parts) >= 7:
563
+ request_configs = {}
564
+ for kv in parts[6:]:
565
+ kvs = kv.split("%", 1)
566
+ if len(kvs) != 2:
567
+ raise ValueError(f"Invalid key string: {s}")
568
+ request_configs["lmcache.tag." + kvs[0]] = kvs[1]
569
+ return LayerCacheEngineKey(
570
+ model_name=parts[0],
571
+ world_size=int(parts[1]),
572
+ worker_id=int(parts[2]),
573
+ chunk_hash=int(parts[3], 16),
574
+ dtype=STR_DTYPE_TO_TORCH_DTYPE[parts[4]],
575
+ request_configs=request_configs,
576
+ layer_id=int(parts[5]),
577
+ )
578
+
579
+
580
+ @dataclass
581
+ class CacheStoreEvent:
582
+ block_hashes: list[int]
583
+ parent_block_hash: int | None
584
+ token_ids: list[int]
585
+ block_size: int
586
+
587
+ # Deprecated, use lora_name instead
588
+ # Retained for backwards compatibility
589
+ # Remove when vLLM removes it from BlockStored
590
+ lora_id: int | None
591
+
592
+ medium: str | None
593
+ lora_name: str | None
594
+
595
+
596
+ class EngineType(Enum):
597
+ VLLM = "vllm"
598
+ SGLANG = "sglang"
599
+ MOCK = "mock"
600
+
601
+
602
+ ##### NVTX annotation #####
603
+ _NVTX_COLORS = ["green", "blue", "purple", "rapids"]
604
+
605
+
606
+ def _get_color_for_nvtx(name):
607
+ m = hashlib.sha256()
608
+ m.update(name.encode())
609
+ hash_value = int(m.hexdigest(), 16)
610
+ idx = hash_value % len(_NVTX_COLORS)
611
+ return _NVTX_COLORS[idx]
612
+
613
+
614
+ def _lmcache_nvtx_annotate(func, domain="lmcache"):
615
+ """Decorator for applying nvtx annotations to methods in lmcache."""
616
+ return annotate(
617
+ message=func.__qualname__,
618
+ color=_get_color_for_nvtx(func.__qualname__),
619
+ domain=domain,
620
+ )(func)
621
+
622
+
623
+ ##### Observability Threading related #####
624
+ _shared_observability_lock = threading.Lock()
625
+
626
+
627
+ def thread_safe(func):
628
+ def wrapper(*args, **kwargs):
629
+ with _shared_observability_lock:
630
+ result = func(*args, **kwargs)
631
+ return result
632
+
633
+ return wrapper
634
+
635
+
636
+ #### Thread/asyncio-related utilities ####
637
+ def handle_thread_exception(args):
638
+ logger.error(
639
+ f"Thread {args.thread.name} crashed: {args.exc_type.__name__}: {args.exc_value}"
640
+ )
641
+
642
+
643
+ def start_loop_in_thread_with_exceptions(loop: asyncio.AbstractEventLoop):
644
+ # The loop must be set in the *same* thread where it runs.
645
+ asyncio.set_event_loop(loop)
646
+
647
+ # Catch unhandled exceptions from callbacks/tasks in this loop:
648
+ def loop_excepthook(loop, context):
649
+ msg = context.get("message", "Unhandled exception in event loop")
650
+ exc = context.get("exception")
651
+ logger.error(f"[asyncio] {msg}")
652
+ if exc:
653
+ traceback.print_exception(type(exc), exc, exc.__traceback__)
654
+
655
+ loop.set_exception_handler(loop_excepthook)
656
+ loop.run_forever()
657
+
658
+
659
+ #### Placeholder for dpsk broadcast functionality ####
660
+ def mock_up_broadcast_fn(t: torch.Tensor, i: int) -> None:
661
+ raise NotImplementedError("Calling invalid broadcast function")
662
+
663
+
664
+ def mock_up_broadcast_object_fn(a: Any, i: int) -> None:
665
+ raise NotImplementedError("Calling invalid broadcast object function")
lmcache/v1/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+