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,110 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+ import platform
6
+
7
+ # Third Party
8
+ import psutil
9
+ import torch
10
+
11
+ try:
12
+ # First Party
13
+ from lmcache.c_ops import get_gpu_pci_bus_id
14
+ except ImportError:
15
+ # Fallback if c_ops is not available
16
+ get_gpu_pci_bus_id = None
17
+
18
+ # First Party
19
+ from lmcache.logging import init_logger
20
+ from lmcache.v1.config import LMCacheEngineConfig
21
+
22
+ logger = init_logger(__name__)
23
+
24
+
25
+ @dataclass
26
+ class NUMAMapping:
27
+ gpu_to_numa_mapping: dict[int, int]
28
+
29
+
30
+ class SystemMemoryDetector:
31
+ @staticmethod
32
+ def get_available_memory_gb() -> float:
33
+ """
34
+ Get system available memory in GB using psutil.
35
+ This method is cross-platform and doesn't require subprocess calls.
36
+
37
+ Returns:
38
+ Available memory in GB, or 0.0 if detection fails.
39
+ """
40
+ try:
41
+ # Use psutil to get virtual memory information
42
+ memory = psutil.virtual_memory()
43
+ available_gb = memory.available / (1024**3)
44
+
45
+ system = platform.system()
46
+ logger.info(f"{system} system available memory: {available_gb:.2f} GB")
47
+ return available_gb
48
+
49
+ except Exception as e:
50
+ logger.warning(f"Failed to get system available memory using psutil: {e}")
51
+ return 0.0
52
+
53
+
54
+ class NUMADetector:
55
+ @staticmethod
56
+ def get_numa_mapping(config: LMCacheEngineConfig) -> Optional[NUMAMapping]:
57
+ """
58
+ Get NUMA mapping.
59
+ """
60
+ assert config.numa_mode in ["manual", "auto", None], (
61
+ "NUMA mode must be either 'auto', 'manual', or None."
62
+ f" Current mode: {config.numa_mode}"
63
+ )
64
+
65
+ numa_mapping: Optional[NUMAMapping] = None
66
+ if config.numa_mode == "manual":
67
+ numa_mapping = NUMADetector._read_from_config(config)
68
+ elif config.numa_mode == "auto":
69
+ numa_mapping = NUMADetector._read_from_sys()
70
+
71
+ return numa_mapping
72
+
73
+ @staticmethod
74
+ def _read_from_config(config) -> NUMAMapping:
75
+ """
76
+ Read NUMA mapping from the LMCache configuration.
77
+ """
78
+
79
+ assert config.extra_config is not None, (
80
+ "NUMA mode is set but extra_config is None. "
81
+ "Please ensure the configuration is properly set."
82
+ )
83
+
84
+ assert "gpu_to_numa_mapping" in config.extra_config, (
85
+ "NUMA mode is set to `manual` but gpu_to_numa_mapping is None. "
86
+ "Please ensure the configuration is properly set."
87
+ )
88
+
89
+ gpu_to_numa_mapping = config.extra_config.get("gpu_to_numa_mapping")
90
+
91
+ return NUMAMapping(gpu_to_numa_mapping)
92
+
93
+ @staticmethod
94
+ def _read_from_sys() -> Optional[NUMAMapping]:
95
+ """
96
+ Read NUMA mapping from system configuration.
97
+ """
98
+
99
+ try:
100
+ device_index = torch.cuda.current_device()
101
+ pci_bus_id = get_gpu_pci_bus_id(device_index).lower()
102
+
103
+ numa_node_file = f"/sys/bus/pci/devices/{pci_bus_id}/numa_node"
104
+ with open(numa_node_file) as f:
105
+ numa_node = int(f.read())
106
+
107
+ return NUMAMapping(gpu_to_numa_mapping={device_index: numa_node})
108
+ except Exception as e:
109
+ logger.warning(f"Failed to auto read NUMA mapping from system: {e}")
110
+ return None
@@ -0,0 +1,551 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """
3
+ vLLM compatibility notes:
4
+ - PR#20511: Introduced kv_cache_utils.init_none_hash()
5
+ https://github.com/vllm-project/vllm/pull/20511
6
+ - PR#23673: Renamed sha256_cbor_64bit to sha256_cbor
7
+ https://github.com/vllm-project/vllm/pull/23673
8
+ - PR#27151: Moved hash functions to vllm.utils.hashing module
9
+ https://github.com/vllm-project/vllm/pull/27151
10
+
11
+ TODO(baoloongmao): Move this to vllm_v1_adapter to decouple from vLLM
12
+ """
13
+
14
+ # Standard
15
+ from typing import Any, Iterable, List, Optional, Tuple, Union
16
+ import abc
17
+ import os
18
+
19
+ # Third Party
20
+ from transformers import AutoTokenizer
21
+ import torch
22
+
23
+ # First Party
24
+ from lmcache.logging import init_logger
25
+ from lmcache.utils import CacheEngineKey, _lmcache_nvtx_annotate
26
+ from lmcache.v1.config import LMCacheEngineConfig
27
+ from lmcache.v1.metadata import LMCacheMetadata
28
+
29
+ logger = init_logger(__name__)
30
+
31
+ NONE_HASH = 0
32
+
33
+ # Type alias for process_tokens return value
34
+ # (start_index, end_index, cache_engine_key|hash)
35
+ ProcessTokensResult = Tuple[int, int, Union[CacheEngineKey, int]]
36
+
37
+
38
+ class TokenDatabase(metaclass=abc.ABCMeta):
39
+ """TokenDatabase is used to convert input tokens into list of
40
+ cache engine keys. There are multiple ways to implement this:
41
+
42
+ - ChunkedTokenDatabase: It processes tokens into chunks and convert
43
+ each chunk into a cache engine key using prefix hash.
44
+
45
+ - SegmentTokenDatabase: It processes tokens into segments based on
46
+ special separators and convert each segment into a cache engine key.
47
+ """
48
+
49
+ @abc.abstractmethod
50
+ def __init__(
51
+ self,
52
+ config: Optional[LMCacheEngineConfig] = None,
53
+ metadata: Optional[LMCacheMetadata] = None,
54
+ ):
55
+ global NONE_HASH
56
+
57
+ hash_algorithm: str = (
58
+ config.pre_caching_hash_algorithm if config is not None else "builtin"
59
+ )
60
+
61
+ # Get hash function with vLLM version compatibility
62
+ self.hash_func = self._get_vllm_hash_func(hash_algorithm)
63
+
64
+ # Initialize NONE_HASH (vLLM >= PR#20511)
65
+ # NOTE: For centralized cache sharing, ensure PYTHONHASHSEED is
66
+ # set consistently across all processes (e.g., export PYTHONHASHSEED=0).
67
+ try:
68
+ # Third Party
69
+ from vllm.v1.core import kv_cache_utils
70
+
71
+ if hasattr(kv_cache_utils, "init_none_hash"):
72
+ kv_cache_utils.init_none_hash(self.hash_func)
73
+ NONE_HASH = kv_cache_utils.NONE_HASH
74
+ logger.info(
75
+ f"Initialized NONE_HASH={NONE_HASH} from vLLM (>= PR#20511)"
76
+ )
77
+ else:
78
+ NONE_HASH = 0
79
+ logger.info("Using default NONE_HASH=0 (vLLM < PR#20511)")
80
+ except (ImportError, AttributeError):
81
+ NONE_HASH = 0
82
+ logger.info("Using default NONE_HASH=0 (vLLM not available)")
83
+
84
+ logger.info(f"Using hash algorithm: {hash_algorithm}")
85
+ self.metadata = metadata
86
+ # Whether only the first rank should save cache. This flag is also used
87
+ # to control the logical world_size embedded into CacheEngineKey.
88
+ self.save_only_first_rank = False
89
+ if config is not None and metadata is not None:
90
+ # save_only_first_rank only works when use MLA, follow the same
91
+ # semantics as LMCacheEngine and memory allocator.
92
+ self.save_only_first_rank = (
93
+ config.get_extra_config_value("save_only_first_rank", metadata.use_mla)
94
+ and metadata.use_mla
95
+ )
96
+
97
+ def _get_vllm_hash_func(self, hash_algorithm: str):
98
+ """Get hash function from vLLM with version compatibility.
99
+
100
+ Tries multiple import paths to support different vLLM versions:
101
+ - vllm.utils.hashing.get_hash_fn_by_name (>= PR#27151)
102
+ - vllm.utils.get_hash_fn_by_name (< PR#27151)
103
+ - Direct imports as fallback
104
+ - sha256_cbor_64bit -> sha256_cbor rename (PR#23673)
105
+ """
106
+ # Try get_hash_fn_by_name from both locations (PR#27151)
107
+ for module_path in ["vllm.utils.hashing", "vllm.utils"]:
108
+ try:
109
+ module = __import__(module_path, fromlist=["get_hash_fn_by_name"])
110
+ get_hash_fn_by_name = module.get_hash_fn_by_name
111
+ return self._try_get_hash(
112
+ get_hash_fn_by_name, hash_algorithm, module_path
113
+ )
114
+ except (ImportError, AttributeError, ValueError):
115
+ continue
116
+
117
+ # Try direct imports as fallback (for older vLLM versions)
118
+ func_names = (
119
+ ["sha256_cbor", "sha256_cbor_64bit"]
120
+ if hash_algorithm in ("sha256_cbor", "sha256_cbor_64bit")
121
+ else [hash_algorithm]
122
+ )
123
+ for module_path in ["vllm.utils.hashing", "vllm.utils"]:
124
+ for func_name in func_names:
125
+ try:
126
+ module = __import__(module_path, fromlist=[func_name])
127
+ hash_func = getattr(module, func_name)
128
+ logger.info(
129
+ f"Loaded '{func_name}' from {module_path} (direct import)"
130
+ )
131
+ return hash_func
132
+ except (ImportError, AttributeError):
133
+ continue
134
+
135
+ # Fallback to builtin hash
136
+ logger.warning(
137
+ f"Could not load '{hash_algorithm}' from vLLM. Using builtin hash. "
138
+ "This may cause inconsistencies in distributed caching."
139
+ )
140
+
141
+ # Check PYTHONHASHSEED when using builtin hash
142
+ if os.getenv("PYTHONHASHSEED") is None:
143
+ logger.warning(
144
+ "Using builtin hash without PYTHONHASHSEED set. "
145
+ "For production environments (non-testing scenarios), you MUST set "
146
+ "PYTHONHASHSEED to ensure consistent hashing across processes. "
147
+ "Example: export PYTHONHASHSEED=0"
148
+ )
149
+
150
+ return hash
151
+
152
+ def _try_get_hash(self, get_hash_fn_by_name, hash_algorithm: str, module_name: str):
153
+ """Try to get hash function, handling sha256_cbor_64bit rename."""
154
+ # Handle sha256_cbor_64bit -> sha256_cbor rename (PR#23673)
155
+ names_to_try = (
156
+ ["sha256_cbor", "sha256_cbor_64bit"]
157
+ if hash_algorithm in ("sha256_cbor", "sha256_cbor_64bit")
158
+ else [hash_algorithm]
159
+ )
160
+
161
+ for name in names_to_try:
162
+ try:
163
+ hash_func = get_hash_fn_by_name(name)
164
+ logger.info(f"Loaded '{name}' from {module_name}")
165
+ return hash_func
166
+ except ValueError:
167
+ continue
168
+ raise ValueError(f"Hash function '{hash_algorithm}' not found in {module_name}")
169
+
170
+ @abc.abstractmethod
171
+ def process_tokens(
172
+ self,
173
+ tokens: Optional[Union[torch.Tensor, List[int]]] = None,
174
+ hashes: Optional[List[int]] = None,
175
+ offsets: Optional[List[int]] = None,
176
+ mask: Optional[torch.Tensor] = None,
177
+ make_key: bool = True,
178
+ request_configs: Optional[dict] = None,
179
+ ) -> Iterable[ProcessTokensResult]:
180
+ """Process the tokens and return the corresponding cache engine keys.
181
+
182
+ :param Optional[Union[torch.Tensor, List[int]]] tokens: The tokens to process.
183
+
184
+ :param Optional[List[int]] hashes: The hashes to process. If provided,
185
+ it will be used instead of tokens to generate cache engine keys.
186
+
187
+ :param Optional[List[int]] offsets: The number of tokens in each chunk.
188
+
189
+ :param Optional[torch.Tensor] mask: The mask for the tokens. Should
190
+ have the same length as tokens. And the mask should ALWAYS be like
191
+ FFFFFTTTTTTT, where True means the tokens needs to be matched,
192
+ and the Falses will ALWAYS be at the PREFIX of the tensor.
193
+
194
+ :param bool make_key: Whether to make the cache engine key or not.
195
+ If False, the hash value will be returned instead.
196
+
197
+ :param Optional[dict] request_configs: The configs of the request.
198
+
199
+ :returns: A iterable of tuples with three elements. The first element
200
+ is the start index of the tokens for the key. The second element
201
+ is the end index of the tokens for the key. The third element is
202
+ the cache engine key (or hash) for the tokens.
203
+ """
204
+
205
+ raise NotImplementedError
206
+
207
+ def _make_key_by_hash(
208
+ self, chunk_hash: int, request_configs: Optional[dict] = None
209
+ ):
210
+ assert self.metadata is not None
211
+ # When save_only_first_rank is enabled (for MLA), we deliberately
212
+ # collapse the CacheEngineKey.world_size to 1 so that cache keys
213
+ # become world-size agnostic across compatible deployments.
214
+ return CacheEngineKey(
215
+ self.metadata.model_name,
216
+ self.metadata.world_size if not self.save_only_first_rank else 1,
217
+ self.metadata.worker_id,
218
+ chunk_hash,
219
+ self.metadata.kv_dtype,
220
+ request_configs,
221
+ )
222
+
223
+ def _canonicalize_hash_inputs(
224
+ self,
225
+ prefix_hash: Optional[int],
226
+ tokens_tuple: Tuple[int, ...],
227
+ extra_keys: Optional[List[Any]],
228
+ ) -> Tuple[int, Tuple[int, ...], Tuple[Any, ...]]:
229
+ """
230
+ Canonicalize hash inputs so that semantically identical requests
231
+ produce structurally identical hash inputs across instances.
232
+ - prefix_hash: int or NONE_HASH if None
233
+ - tokens_tuple: tuple of token IDs
234
+ - extra_keys: tuple of additional keys, empty if None
235
+ """
236
+ return (
237
+ prefix_hash if prefix_hash is not None else NONE_HASH,
238
+ tokens_tuple,
239
+ tuple(extra_keys) if extra_keys is not None else (),
240
+ )
241
+
242
+ def _hash_tokens(
243
+ self,
244
+ tokens: Union[torch.Tensor, List[int]],
245
+ prefix_hash: Optional[int] = None,
246
+ extra_keys: Optional[list[Any]] = None,
247
+ ) -> int:
248
+ if isinstance(tokens, torch.Tensor):
249
+ tokens_tuple = tuple(tokens.cpu().tolist())
250
+ elif isinstance(tokens, list):
251
+ tokens_tuple = tuple(tokens)
252
+ else:
253
+ raise ValueError(f"Unsupported tokens type: {type(tokens)}")
254
+
255
+ # Ignore extra keys for now
256
+ # Extra keys are for multi-modal inputs and
257
+ # request specific metadata (e.g., LoRA ID).
258
+ # Use default values for None to maintain a fixed tuple structure for hashing.
259
+
260
+ # Use helper to canonicalize inputs to ensure consistent hashing
261
+ # This replaces the logic that was causing inconsistency
262
+ canon_prefix, canon_tokens, canon_extra = self._canonicalize_hash_inputs(
263
+ prefix_hash, tokens_tuple, extra_keys
264
+ )
265
+
266
+ return self.hash_func((canon_prefix, canon_tokens, canon_extra))
267
+
268
+
269
+ class ChunkedTokenDatabase(TokenDatabase):
270
+ def __init__(
271
+ self,
272
+ config: Optional[LMCacheEngineConfig] = None,
273
+ metadata: Optional[LMCacheMetadata] = None,
274
+ ):
275
+ super(ChunkedTokenDatabase, self).__init__(config, metadata)
276
+
277
+ if config is not None:
278
+ self.config = config
279
+ self.chunk_size = config.chunk_size
280
+
281
+ # Check for cross-process cache sharing setup
282
+ if os.getenv("PYTHONHASHSEED") is None:
283
+ if config.remote_url is not None:
284
+ logger.warning(
285
+ "Centralized cache sharing detected "
286
+ "but PYTHONHASHSEED not set. "
287
+ "For consistent caching, set: export PYTHONHASHSEED=0 "
288
+ "before the engine starts."
289
+ )
290
+ if config.enable_pd:
291
+ logger.error(
292
+ "P/D Disaggregation detected "
293
+ "but PYTHONHASHSEED not set. "
294
+ "For consistent caching, set: export PYTHONHASHSEED=0 "
295
+ "before the engine starts. "
296
+ "This will cause incorrect KV cache transfer."
297
+ )
298
+ else: # Default values
299
+ self.config = None
300
+ self.chunk_size = 256
301
+
302
+ def _get_init_hash(self) -> int:
303
+ return NONE_HASH
304
+
305
+ def _chunk_tokens(
306
+ self,
307
+ tokens: Union[torch.Tensor, List[int]],
308
+ ) -> Iterable[Union[torch.Tensor, List[int]]]:
309
+ """
310
+ Chunk the tokens into chunks of size self.chunk_size.
311
+
312
+ :param tokens: the input tokens, with shape [seq_len]
313
+ device: the target device after chunking
314
+
315
+ :return: a generator of chunks of tokens, each with
316
+ shape [chunk_size]
317
+ """
318
+ save_unfull_chunk = (
319
+ self.config.save_unfull_chunk if self.config is not None else True
320
+ )
321
+ end = (
322
+ len(tokens)
323
+ if save_unfull_chunk
324
+ else (len(tokens) - len(tokens) % self.chunk_size)
325
+ )
326
+ for i in range(0, end, self.chunk_size):
327
+ yield tokens[i : i + self.chunk_size]
328
+
329
+ def _prefix_hash(
330
+ self,
331
+ token_chunks: Iterable[Union[torch.Tensor, List[int]]],
332
+ ) -> Iterable[int]:
333
+ prefix_hash = self._get_init_hash()
334
+ for token_chunk in token_chunks:
335
+ prefix_hash = self._hash_tokens(token_chunk, prefix_hash)
336
+ yield prefix_hash
337
+
338
+ @_lmcache_nvtx_annotate
339
+ def process_tokens(
340
+ self,
341
+ tokens: Optional[Union[torch.Tensor, List[int]]] = None,
342
+ hashes: Optional[List[int]] = None,
343
+ offsets: Optional[List[int]] = None,
344
+ mask: Optional[torch.Tensor] = None,
345
+ make_key: bool = True,
346
+ request_configs: Optional[dict] = None,
347
+ ) -> Iterable[ProcessTokensResult]:
348
+ """Process the tokens/hashes and return the corresponding cache engine keys.
349
+
350
+ :param Optional[Union[torch.Tensor, List[int]]] tokens: The tokens to process.
351
+
352
+ :param Optional[List[int]] hashes: The hashes to process. If provided,
353
+ it will be used instead of tokens to generate cache engine keys.
354
+
355
+ :param Optional[List[int]] offsets: The number of tokens in each chunk.
356
+
357
+ :param Optional[torch.Tensor] mask: The mask for the tokens. Should
358
+ have the same length as tokens. And the mask should ALWAYS be like
359
+ FFFFFTTTTTTT, where True means the tokens needs to be matched,
360
+ and the Falses will ALWAYS be at the PREFIX of the tensor.
361
+
362
+ :param bool make_key: Whether to make the cache engine key or not.
363
+ If False, the hash value will be returned instead.
364
+
365
+ :param Optional[dict] request_configs: The configs of the request.
366
+
367
+ :returns: A iterable of tuples with three elements. The first element
368
+ is the start index of the tokens for the key. The second element
369
+ is the end index of the tokens for the key. The third element is
370
+ the cache engine key (or hash) for the tokens.
371
+
372
+ :raises: ValueError if the number of Falses in the mask is not a
373
+ multiple of the chunk size.
374
+ """
375
+ if mask is not None:
376
+ num_falses = mask.numel() - mask.long().sum().item()
377
+ else:
378
+ num_falses = 0
379
+
380
+ if num_falses % self.chunk_size != 0:
381
+ raise ValueError(
382
+ "The number of Falses in the mask is not a multiple of the chunk size."
383
+ )
384
+
385
+ if tokens is not None:
386
+ total_len = len(tokens)
387
+ token_chunks = self._chunk_tokens(tokens)
388
+ prefix_hashes = self._prefix_hash(token_chunks)
389
+ for chunk_id, hash_val in enumerate(prefix_hashes):
390
+ start_idx = chunk_id * self.chunk_size
391
+ end_idx = min(start_idx + self.chunk_size, total_len)
392
+ if start_idx < num_falses:
393
+ continue
394
+ else:
395
+ if make_key:
396
+ yield (
397
+ start_idx,
398
+ end_idx,
399
+ self._make_key_by_hash(hash_val, request_configs),
400
+ )
401
+ else:
402
+ yield start_idx, end_idx, hash_val
403
+ elif hashes is not None:
404
+ assert offsets is not None, (
405
+ "If hashes are provided, offsets must also be provided."
406
+ )
407
+ start_idx = 0
408
+ for hash_val, offset in zip(hashes, offsets, strict=False):
409
+ end_idx = start_idx + offset
410
+ if make_key:
411
+ yield (
412
+ start_idx,
413
+ end_idx,
414
+ self._make_key_by_hash(hash_val, request_configs),
415
+ )
416
+ else:
417
+ yield start_idx, end_idx, hash_val
418
+ start_idx = end_idx
419
+ else:
420
+ raise ValueError("Either tokens or hashes must be provided.")
421
+
422
+
423
+ class SegmentTokenDatabase(TokenDatabase):
424
+ """
425
+ Currently, we still use special separators to identify chunks.
426
+ In the future, we might need to implement a fast substring match.
427
+ """
428
+
429
+ def __init__(self, config: LMCacheEngineConfig, metadata: LMCacheMetadata):
430
+ super(SegmentTokenDatabase, self).__init__(config, metadata)
431
+
432
+ self.tokenizer = AutoTokenizer.from_pretrained(metadata.model_name)
433
+
434
+ # TODO (Jiayi): figure out how to decide when
435
+ # to use `1:` (whether there's a special starting token
436
+ # in the beginning)
437
+ self.sep_tokens = self.tokenizer.encode(config.blend_special_str)[1:]
438
+ self.sep_tokens = torch.tensor(self.sep_tokens, device="cpu")
439
+ self.sep_len = len(self.sep_tokens)
440
+
441
+ def _fast_split_by_subtensor(self, tokens: torch.Tensor) -> Iterable[torch.Tensor]:
442
+ """Match the `sep_tokens` with sliding windows"""
443
+
444
+ if self.sep_len == 0 or len(tokens) < self.sep_len:
445
+ yield tokens
446
+
447
+ # Unfold into sliding windows
448
+ # shape: (num_tokens-sep_len+1, sep_len)
449
+ windows = tokens.unfold(0, self.sep_len, 1)
450
+
451
+ # Compare each window with sep_tokens
452
+ matches = (
453
+ (windows == self.sep_tokens).all(dim=1).nonzero(as_tuple=True)[0].tolist()
454
+ )
455
+
456
+ # Split based on matches
457
+ start = 0
458
+ for idx in matches:
459
+ yield tokens[start:idx]
460
+ start = idx + self.sep_len
461
+ # yield last chunk
462
+ yield tokens[start:]
463
+
464
+ def process_tokens(
465
+ self,
466
+ tokens: Optional[Union[torch.Tensor, List[int]]] = None,
467
+ hashes: Optional[List[int]] = None,
468
+ offsets: Optional[List[int]] = None,
469
+ mask: Optional[torch.Tensor] = None,
470
+ make_key: bool = True,
471
+ request_configs: Optional[dict] = None,
472
+ ) -> Iterable[ProcessTokensResult]:
473
+ """Process the tokens and return the corresponding cache engine keys.
474
+
475
+ :param Union[torch.Tensor, List[int]] tokens: The tokens to process.
476
+
477
+ :param Optional[List[int]] hashes: The hashes to process. If provided,
478
+ it will be used instead of tokens to generate cache engine keys.
479
+
480
+ :param Optional[List[int]] offsets: The number of tokens in each chunk.
481
+
482
+ :param Optional[torch.Tensor] mask: The mask for the tokens. Should
483
+ have the same length as tokens. And the mask should ALWAYS be like
484
+ FFFFFTTTTTTT, where True means the tokens needs to be matched,
485
+ and the Falses will ALWAYS be at the PREFIX of the tensor.
486
+
487
+ :param bool make_key: Whether to make the cache engine key or not.
488
+ If False, the hash value will be returned instead.
489
+
490
+ :param Optional[dict] request_configs: The configs of the request.
491
+
492
+ :returns: A iterable of tuples with three elements. The first element
493
+ is the start index of the tokens for the key. The second element
494
+ is the end index of the tokens for the key. The third element is
495
+ the cache engine key for the tokens.
496
+
497
+ """
498
+
499
+ if tokens is not None:
500
+ if not isinstance(tokens, torch.Tensor):
501
+ tokens = torch.tensor(tokens, dtype=torch.long, device="cpu")
502
+ else:
503
+ tokens = tokens.to(device="cpu", dtype=torch.long)
504
+
505
+ if mask is not None:
506
+ num_falses = mask.numel() - mask.long().sum().item()
507
+ else:
508
+ num_falses = 0
509
+ assert num_falses < len(tokens), (
510
+ "The number of Falses in the mask shouldn't "
511
+ "be less than the length of tokens."
512
+ )
513
+
514
+ token_chunks = self._fast_split_by_subtensor(tokens)
515
+ start_idx = 0
516
+ for idx, token_chunk in enumerate(token_chunks):
517
+ token_chunk_len = len(token_chunk)
518
+ end_idx = start_idx + token_chunk_len
519
+ if idx > 0:
520
+ start_idx += self.sep_len
521
+ end_idx += self.sep_len
522
+ if start_idx >= num_falses:
523
+ if make_key:
524
+ yield (
525
+ start_idx,
526
+ end_idx,
527
+ self._make_key_by_hash(
528
+ self._hash_tokens(token_chunk), request_configs
529
+ ),
530
+ )
531
+ else:
532
+ yield start_idx, end_idx, self._hash_tokens(token_chunk)
533
+ start_idx = end_idx
534
+ elif hashes is not None:
535
+ assert offsets is not None, (
536
+ "If hashes are provided, offsets must also be provided."
537
+ )
538
+ start_idx = 0
539
+ for hash_val, offset in zip(hashes, offsets, strict=False):
540
+ end_idx = start_idx + offset
541
+ if make_key:
542
+ yield (
543
+ start_idx,
544
+ end_idx,
545
+ self._make_key_by_hash(hash_val, request_configs),
546
+ )
547
+ else:
548
+ yield start_idx, end_idx, hash_val
549
+ start_idx = end_idx
550
+ else:
551
+ raise ValueError("Either tokens or hashes must be provided.")