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,407 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Optional, Union
4
+ import threading
5
+ import time
6
+
7
+ # Third Party
8
+ import msgspec
9
+ import torch
10
+ import zmq
11
+
12
+ # First Party
13
+ from lmcache.logging import init_logger
14
+ from lmcache.v1.cache_engine import LMCacheEngine
15
+ from lmcache.v1.config import LMCacheEngineConfig
16
+ from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
17
+ from lmcache.v1.lookup_client.async_lookup_message import (
18
+ LookupCleanupMsg,
19
+ LookupRequestMsg,
20
+ LookupResponseMsg,
21
+ )
22
+ from lmcache.v1.metadata import LMCacheMetadata
23
+ from lmcache.v1.rpc_utils import (
24
+ get_zmq_context,
25
+ get_zmq_rpc_path_lmcache,
26
+ get_zmq_socket,
27
+ )
28
+
29
+ logger = init_logger(__name__)
30
+
31
+
32
+ # NOTE(Jiayi): Prefetch could load extra redundant cache if multiple
33
+ # workers has different hit tokens.
34
+ class LMCacheAsyncLookupClient(LookupClientInterface):
35
+ """
36
+ ZMQ-based lookup client that communicates with a lookup server.
37
+
38
+ Related extra_config:
39
+ - lookup_server_worker_ids:
40
+ is a config to control create lookup server on some workers.
41
+ if mla is not enabled, default is [];
42
+ if mla is enabled, default is [0];
43
+ - if lookup_server_worker_ids is [], start lookup server on all workers
44
+ - if lookup_server_worker_ids is [0], start lookup server on worker0
45
+ - if lookup_server_worker_ids is [0, 3, 6], start lookup server on
46
+ worker0, worker3 and worker6
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ config: LMCacheEngineConfig,
52
+ metadata: LMCacheMetadata,
53
+ ):
54
+ # lookup_id -> first lookup time
55
+ # this helps us support timeout semantics
56
+ self.first_lookup_time: dict[str, float] = {}
57
+ self.config = config
58
+
59
+ self.ctx = get_zmq_context(use_asyncio=False)
60
+ kv_connector_extra_config = metadata.kv_connector_extra_config or {}
61
+ rpc_port = kv_connector_extra_config.get("lmcache_rpc_port", 0)
62
+ engine_id = metadata.engine_id
63
+ assert engine_id is not None, "engine_id is required for RPC communication"
64
+ self.world_size = metadata.world_size
65
+ self.lookup_server_worker_ids = config.get_lookup_server_worker_ids(
66
+ metadata.use_mla, metadata.world_size
67
+ )
68
+
69
+ self.push_sockets = []
70
+ if len(self.lookup_server_worker_ids) > 0:
71
+ ranks = self.lookup_server_worker_ids
72
+ self.world_size = len(self.lookup_server_worker_ids)
73
+ else:
74
+ ranks = [i for i in range(self.world_size)]
75
+
76
+ for rank in ranks:
77
+ worker_socket_path = get_zmq_rpc_path_lmcache(
78
+ engine_id, "lookup_worker", rpc_port, rank
79
+ )
80
+ logger.info(
81
+ "lmcache lookup client connect to rank %s with worker socket path %s",
82
+ rank,
83
+ worker_socket_path,
84
+ )
85
+
86
+ push_socket = get_zmq_socket(
87
+ self.ctx,
88
+ worker_socket_path,
89
+ "ipc",
90
+ zmq.PUSH, # type: ignore[attr-defined]
91
+ "connect",
92
+ )
93
+
94
+ self.push_sockets.append(push_socket)
95
+
96
+ scheduler_socket_path = get_zmq_rpc_path_lmcache(
97
+ engine_id, "lookup_scheduler", rpc_port, 0
98
+ )
99
+ self.pull_socket = get_zmq_socket(
100
+ self.ctx,
101
+ scheduler_socket_path,
102
+ "ipc",
103
+ zmq.PULL, # type: ignore[attr-defined]
104
+ "bind",
105
+ )
106
+ logger.info(
107
+ "lmcache lookup client connect to scheduler with socket path %s",
108
+ scheduler_socket_path,
109
+ )
110
+
111
+ # First Party
112
+ from lmcache.v1.token_database import (
113
+ ChunkedTokenDatabase,
114
+ SegmentTokenDatabase,
115
+ TokenDatabase,
116
+ )
117
+
118
+ self.token_database: TokenDatabase
119
+ if config.enable_blending:
120
+ self.token_database = SegmentTokenDatabase(config, metadata)
121
+ else:
122
+ self.token_database = ChunkedTokenDatabase(config, metadata)
123
+
124
+ # A lock is needed since we need another thread to pull
125
+ # responses from the lookup_and_prefetch server
126
+ # (e.g., worker process).
127
+ self.lock = threading.Lock()
128
+
129
+ # map from lookup_id (i.e., req_id) to req's status.
130
+ # None indicates ongoing.
131
+ # int indicates number of hit tokens.
132
+ self.reqs_status: dict[str, Optional[int]] = {}
133
+
134
+ # map from lookup_id (i.e., req_id) to number of hit tokens for each worker
135
+ self.res_for_each_worker: dict[str, list[int]] = {}
136
+
137
+ # The two parts are [lookup_id (i.e., req_id), num_hit_tokens]
138
+ self.num_parts = 2
139
+
140
+ # Track lookup_ids that have been aborted for cleanup
141
+ self.aborted_lookups: set[str] = set()
142
+
143
+ self.running = True
144
+
145
+ self.thread = threading.Thread(
146
+ target=self.process_responses_from_workers,
147
+ daemon=True,
148
+ name="async-lookup-client-thread",
149
+ )
150
+ self.thread.start()
151
+
152
+ # default backoff time
153
+ self.lookup_backoff_time = 0.01
154
+ if config.extra_config is not None:
155
+ self.lookup_backoff_time = float(
156
+ config.extra_config.get("lookup_backoff_time", self.lookup_backoff_time)
157
+ )
158
+
159
+ def lookup_cache(self, lookup_id: str) -> Optional[int]:
160
+ """
161
+ -1 means not found;
162
+ None means ongoing;
163
+ int >= 0 means number of hit tokens
164
+ """
165
+ # Check if any aborted lookups are finished, send cleanup messages
166
+ self._cleanup_finished_aborted_lookups()
167
+
168
+ with self.lock:
169
+ if (req_status := self.reqs_status.get(lookup_id, -1)) == -1:
170
+ self.reqs_status[lookup_id] = None
171
+ self.first_lookup_time[lookup_id] = time.time()
172
+ elif req_status is None:
173
+ time.sleep(self.lookup_backoff_time)
174
+ if (
175
+ time.time() - self.first_lookup_time[lookup_id]
176
+ ) * 1000 > self.config.lookup_timeout_ms:
177
+ logger.warning(
178
+ (
179
+ "Request %s is still waiting for async lookup "
180
+ "after %d seconds, returning 0 lmcache cached tokens "
181
+ "so vllm can recompute"
182
+ ),
183
+ lookup_id,
184
+ self.config.lookup_timeout_ms // 1000,
185
+ )
186
+ self.cancel_lookup(lookup_id)
187
+ self.first_lookup_time.pop(lookup_id, None)
188
+ return 0
189
+
190
+ return req_status
191
+
192
+ # TODO(Jiayi): Consider batching here
193
+ def lookup(
194
+ self,
195
+ token_ids: Union[torch.Tensor, list[int]],
196
+ lookup_id: str,
197
+ request_configs: Optional[dict] = None,
198
+ ) -> Optional[int]:
199
+ hashes: list[int] = []
200
+ offsets = []
201
+ for start, end, hash_val in self.token_database.process_tokens(
202
+ token_ids, make_key=False
203
+ ):
204
+ hashes.append(hash_val) # type: ignore[arg-type]
205
+ offsets.append(end - start)
206
+
207
+ # Create structured message
208
+ msg = LookupRequestMsg(
209
+ lookup_id=lookup_id,
210
+ hashes=hashes,
211
+ offsets=offsets,
212
+ request_configs=request_configs,
213
+ )
214
+
215
+ # Serialize message using msgspec
216
+ msg_buf = msgspec.msgpack.encode(msg)
217
+
218
+ for i in range(self.world_size):
219
+ self.push_sockets[i].send(msg_buf, copy=False)
220
+ time.sleep(self.lookup_backoff_time)
221
+ return None
222
+
223
+ def process_responses_from_workers(self):
224
+ while self.running:
225
+ try:
226
+ msg_buf = self.pull_socket.recv(copy=False)
227
+ # Deserialize message using msgspec
228
+ msg = msgspec.msgpack.decode(msg_buf, type=LookupResponseMsg)
229
+ lookup_id = msg.lookup_id
230
+ res = msg.num_hit_tokens
231
+
232
+ with self.lock:
233
+ if lookup_id not in self.res_for_each_worker:
234
+ self.res_for_each_worker[lookup_id] = [res]
235
+ else:
236
+ self.res_for_each_worker[lookup_id].append(res)
237
+ all_res = self.res_for_each_worker[lookup_id]
238
+
239
+ if len(all_res) == self.world_size:
240
+ self.res_for_each_worker.pop(lookup_id)
241
+
242
+ # NOTE: it is possible that the number of hit
243
+ # tokens is different across (TP and PP) ranks, so we
244
+ # can use the minimum value as the number of
245
+ # hit tokens.
246
+ self.reqs_status[lookup_id] = min(all_res)
247
+
248
+ except Exception as e:
249
+ logger.error("Error processing response from worker: %s", e)
250
+
251
+ def clear_lookup_status(self, lookup_id: str) -> None:
252
+ with self.lock:
253
+ self.reqs_status.pop(lookup_id, None)
254
+ self.first_lookup_time.pop(lookup_id, None)
255
+
256
+ def cancel_lookup(self, lookup_id: str) -> None:
257
+ """Mark lookup as aborted. Cleanup will happen after task finishes."""
258
+ self.aborted_lookups.add(lookup_id)
259
+
260
+ def _cleanup_finished_aborted_lookups(self) -> None:
261
+ """Check for finished aborted lookups and send cleanup messages to workers."""
262
+ # A lookup whose status is None is still loading.
263
+ # We wait for it to finish before cleanup.
264
+ finished_lookups = [
265
+ lookup_id
266
+ for lookup_id in self.aborted_lookups
267
+ if self.reqs_status.get(lookup_id) is not None
268
+ ]
269
+ if finished_lookups:
270
+ self.aborted_lookups.difference_update(finished_lookups)
271
+
272
+ # Tell the server to free the reserved memory buffers for each aborted lookup.
273
+ for lookup_id in finished_lookups:
274
+ self._send_cleanup_message(lookup_id)
275
+ self.clear_lookup_status(lookup_id)
276
+
277
+ def _send_cleanup_message(self, lookup_id: str) -> None:
278
+ """Send cleanup message to workers to release memory objects."""
279
+ msg = LookupCleanupMsg(lookup_id=lookup_id)
280
+ msg_buf = msgspec.msgpack.encode(msg)
281
+
282
+ for i in range(self.world_size):
283
+ self.push_sockets[i].send(msg_buf, copy=False)
284
+ logger.debug("Sent cleanup message for lookup_id=%s", lookup_id)
285
+
286
+ def supports_producer_reuse(self) -> bool:
287
+ """Return True as LMCacheLookupClient supports producer kvcache reuse"""
288
+ return True
289
+
290
+ def close(self):
291
+ self.running = False
292
+ try:
293
+ if self.thread.is_alive():
294
+ self.thread.join(timeout=1.0)
295
+ for s in self.push_sockets:
296
+ s.close(linger=0) # type: ignore[arg-type]
297
+ self.pull_socket.close(linger=0) # type: ignore[arg-type]
298
+ self.ctx.term()
299
+ except Exception as e:
300
+ logger.warning("Failed to join thread during close: %s", e)
301
+
302
+
303
+ class LMCacheAsyncLookupServer:
304
+ """ZMQ-based async lookup server that handles lookup and prefetch
305
+ requests using LMCacheEngine."""
306
+
307
+ def __init__(
308
+ self,
309
+ lmcache_engine: LMCacheEngine,
310
+ metadata: LMCacheMetadata,
311
+ ):
312
+ self.ctx = zmq.Context() # type: ignore[attr-defined]
313
+ kv_connector_extra_config = metadata.kv_connector_extra_config or {}
314
+ rpc_port = kv_connector_extra_config.get("lmcache_rpc_port", 0)
315
+ assert metadata.engine_id is not None, (
316
+ "engine_id is required for RPC communication"
317
+ )
318
+ worker_socket_path = get_zmq_rpc_path_lmcache(
319
+ metadata.engine_id, "lookup_worker", rpc_port, metadata.worker_id
320
+ )
321
+ scheduler_socket_path = get_zmq_rpc_path_lmcache(
322
+ metadata.engine_id, "lookup_scheduler", rpc_port, 0
323
+ )
324
+ self.push_socket = get_zmq_socket(
325
+ self.ctx,
326
+ scheduler_socket_path,
327
+ "ipc",
328
+ zmq.PUSH, # type: ignore[attr-defined]
329
+ "connect",
330
+ )
331
+ self.pull_socket = get_zmq_socket(
332
+ self.ctx,
333
+ worker_socket_path,
334
+ "ipc",
335
+ zmq.PULL, # type: ignore[attr-defined]
336
+ "bind",
337
+ )
338
+
339
+ self.lmcache_engine = lmcache_engine
340
+ self.running = True
341
+
342
+ logger.info(
343
+ "lmcache lookup server start with"
344
+ " scheduler socket path %s, "
345
+ "worker socket path %s",
346
+ scheduler_socket_path,
347
+ worker_socket_path,
348
+ )
349
+ self.thread = threading.Thread(
350
+ target=self.process_requests_from_scheduler,
351
+ daemon=True,
352
+ name="async-lookup-server-thread",
353
+ )
354
+ self.thread.start()
355
+
356
+ def process_requests_from_scheduler(self):
357
+ while self.running:
358
+ try:
359
+ msg_buf = self.pull_socket.recv(copy=False)
360
+ # rely on msgspec to automatically discriminate
361
+ # between LookupRequestMsg and LookupCleanupMsg
362
+ msg = msgspec.msgpack.decode(
363
+ msg_buf,
364
+ type=Union[LookupRequestMsg, LookupCleanupMsg],
365
+ )
366
+
367
+ if isinstance(msg, LookupRequestMsg):
368
+ # Handle lookup request
369
+ self.lmcache_engine.async_lookup_and_prefetch(
370
+ lookup_id=msg.lookup_id,
371
+ hashes=msg.hashes,
372
+ offsets=msg.offsets,
373
+ pin=True,
374
+ request_configs=msg.request_configs,
375
+ )
376
+
377
+ elif isinstance(msg, LookupCleanupMsg):
378
+ # Handle cleanup request - release memory objects for aborted lookup
379
+ self.lmcache_engine.cleanup_memory_objs(msg.lookup_id)
380
+
381
+ else:
382
+ logger.warning("Unknown message type: %s", type(msg))
383
+
384
+ except Exception as e:
385
+ logger.error("Error processing request from scheduler: %s", e)
386
+
387
+ def send_response_to_scheduler(self, lookup_id: str, num_hit_tokens: int):
388
+ # Create structured response message
389
+ msg = LookupResponseMsg(
390
+ lookup_id=lookup_id,
391
+ num_hit_tokens=num_hit_tokens,
392
+ )
393
+
394
+ # Serialize message using msgspec
395
+ msg_buf = msgspec.msgpack.encode(msg)
396
+ self.push_socket.send(msg_buf, copy=False)
397
+
398
+ def close(self):
399
+ self.running = False
400
+ try:
401
+ if self.thread.is_alive():
402
+ self.thread.join(timeout=1.0)
403
+ self.push_socket.close(linger=0) # type: ignore[arg-type]
404
+ self.pull_socket.close(linger=0) # type: ignore[arg-type]
405
+ self.ctx.term()
406
+ except Exception as e:
407
+ logger.warning("Failed to join thread during close: %s", e)
@@ -0,0 +1,285 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Optional, Union
4
+ import json
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.cache_engine import LMCacheEngine
13
+ from lmcache.v1.config import LMCacheEngineConfig
14
+ from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
15
+ from lmcache.v1.metadata import LMCacheMetadata
16
+ from lmcache.v1.rpc.transport import (
17
+ RpcClientTransport,
18
+ RpcServerTransport,
19
+ )
20
+
21
+ logger = init_logger(__name__)
22
+
23
+
24
+ class LMCacheLookupClient(LookupClientInterface):
25
+ """
26
+ Lookup client that communicates with a lookup server
27
+ via an injected RpcClientTransport.
28
+
29
+ The client is decoupled from the underlying communication
30
+ mechanism. The transport layer handles connection management,
31
+ retries, and error recovery.
32
+
33
+ Related extra_config:
34
+ - lookup_server_worker_ids:
35
+ is a config to control create lookup server on some
36
+ workers.
37
+ if mla is not enabled, default is [];
38
+ if mla is enabled, default is [0];
39
+ - if lookup_server_worker_ids is [], start lookup
40
+ server on all workers
41
+ - if lookup_server_worker_ids is [0], start lookup
42
+ server on worker0
43
+ - if lookup_server_worker_ids is [0, 3, 6], start
44
+ lookup server on worker0, worker3 and worker6
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ config: LMCacheEngineConfig,
50
+ metadata: LMCacheMetadata,
51
+ transport: RpcClientTransport,
52
+ ):
53
+ self.config = config
54
+ self.transport = transport
55
+
56
+ # NOTE: map from lookup_id (i.e., req_id) to
57
+ # req's status.
58
+ # int indicates number of hit tokens.
59
+ # The assumption here is that once a request is
60
+ # looked up, the following lookups of the same
61
+ # request must have the same result.
62
+ self.reqs_status: dict[str, int] = {}
63
+
64
+ # First Party
65
+ from lmcache.v1.token_database import (
66
+ ChunkedTokenDatabase,
67
+ SegmentTokenDatabase,
68
+ TokenDatabase,
69
+ )
70
+
71
+ self.enable_blending = config.enable_blending
72
+ self.token_database: TokenDatabase
73
+ if self.enable_blending:
74
+ self.token_database = SegmentTokenDatabase(config, metadata)
75
+ else:
76
+ self.token_database = ChunkedTokenDatabase(config, metadata)
77
+
78
+ def lookup_cache(self, lookup_id: str) -> Optional[int]:
79
+ """
80
+ "-1 means not found;
81
+ None means ongoing; (not supported in sync client)
82
+ int >= 0 means number of hit tokens
83
+ """
84
+ return self.reqs_status.get(lookup_id, -1)
85
+
86
+ def lookup(
87
+ self,
88
+ token_ids: Union[torch.Tensor, list[int]],
89
+ lookup_id: str,
90
+ request_configs: Optional[dict] = None,
91
+ ) -> Optional[int]:
92
+ request_configs_str = ""
93
+ if request_configs is not None and len(request_configs) != 0:
94
+ request_configs_str = json.dumps(request_configs)
95
+
96
+ # NOTE(Jiayi): We cannot only send hashes when
97
+ # blending enabled because the blender need the
98
+ # input embedding.
99
+ if not self.enable_blending:
100
+ hashes = []
101
+ offsets = []
102
+
103
+ for (
104
+ start,
105
+ end,
106
+ key,
107
+ ) in self.token_database.process_tokens(token_ids, make_key=False):
108
+ hashes.append(key)
109
+ offsets.append(end - start)
110
+
111
+ # if the token database returns no hashes,
112
+ # return 0
113
+ if not hashes:
114
+ return 0
115
+
116
+ msg_buf = [
117
+ hashes,
118
+ offsets,
119
+ lookup_id,
120
+ request_configs_str,
121
+ ]
122
+ else:
123
+ msg_buf = [
124
+ token_ids,
125
+ lookup_id,
126
+ request_configs_str,
127
+ ]
128
+
129
+ responses = self.transport.send_and_recv_all(msg_buf)
130
+
131
+ # Transport returns empty list on failure
132
+ if not responses:
133
+ return 0
134
+
135
+ results = [int.from_bytes(resp, "big") for resp in responses]
136
+
137
+ assert len(results) == self.transport.world_size
138
+ if len(set(results)) > 1:
139
+ logger.warning(
140
+ "Lookup results (number of hit tokens) "
141
+ "differ across (TP and PP) ranks: %s.",
142
+ results,
143
+ )
144
+ # NOTE: it is possible that the number of hit
145
+ # tokens is different across (TP and PP) ranks,
146
+ # so we can use the minimum value.
147
+ num_hit_toks = min(results)
148
+ self.reqs_status[lookup_id] = num_hit_toks
149
+
150
+ return num_hit_toks
151
+
152
+ def clear_lookup_status(self, lookup_id: str) -> None:
153
+ self.reqs_status.pop(lookup_id, None)
154
+
155
+ def supports_producer_reuse(self) -> bool:
156
+ """Return True as LMCacheLookupClient supports
157
+ producer kvcache reuse"""
158
+ return True
159
+
160
+ def __enter__(self):
161
+ return self
162
+
163
+ def __exit__(self, exc_type, exc_val, exc_tb):
164
+ self.close()
165
+ return False
166
+
167
+ def close(self):
168
+ self.transport.close()
169
+
170
+
171
+ class LMCacheLookupServer:
172
+ """Lookup server that handles lookup requests using
173
+ LMCacheEngine, with an injected RpcServerTransport."""
174
+
175
+ def __init__(
176
+ self,
177
+ lmcache_engine: LMCacheEngine,
178
+ metadata: LMCacheMetadata,
179
+ transport: RpcServerTransport,
180
+ ):
181
+ self.transport = transport
182
+ self.lmcache_engine = lmcache_engine
183
+ self.running = True
184
+ self.enable_blending = lmcache_engine.config.enable_blending
185
+
186
+ def process_request():
187
+ while self.running:
188
+ try:
189
+ result = self.transport.recv_request()
190
+ if result is None:
191
+ continue
192
+
193
+ identity, data_frames = result
194
+
195
+ # Validate frame structure
196
+ if len(data_frames) < 3:
197
+ logger.warning("Malformed request received: not enough frames.")
198
+ continue
199
+
200
+ # Validate and decode lookup_id
201
+ lookup_id_bytes = data_frames[-2]
202
+ request_configs_bytes = data_frames[-1]
203
+
204
+ if not isinstance(lookup_id_bytes, (bytes, str)):
205
+ logger.warning(
206
+ "Malformed request received: lookup_id is not bytes or str."
207
+ )
208
+ continue
209
+
210
+ if not isinstance(request_configs_bytes, (bytes, str)):
211
+ logger.warning(
212
+ "Malformed request received: "
213
+ "request_configs is not bytes or str."
214
+ )
215
+ continue
216
+
217
+ # Decode to strings
218
+ if isinstance(lookup_id_bytes, bytes):
219
+ lookup_id = lookup_id_bytes.decode("utf-8")
220
+ else:
221
+ lookup_id = lookup_id_bytes
222
+
223
+ if isinstance(request_configs_bytes, bytes):
224
+ request_configs_str = request_configs_bytes.decode("utf-8")
225
+ else:
226
+ request_configs_str = request_configs_bytes
227
+
228
+ request_configs = (
229
+ json.loads(request_configs_str) if request_configs_str else None
230
+ )
231
+
232
+ if not self.enable_blending:
233
+ hashes = data_frames[0]
234
+ offsets = data_frames[1]
235
+ lookup_result = self.lmcache_engine.lookup(
236
+ hashes=hashes,
237
+ offsets=offsets,
238
+ lookup_id=lookup_id,
239
+ pin=True,
240
+ request_configs=request_configs,
241
+ )
242
+ else:
243
+ tokens = data_frames[0]
244
+ lookup_result = self.lmcache_engine.lookup(
245
+ tokens=tokens,
246
+ lookup_id=lookup_id,
247
+ pin=True,
248
+ request_configs=request_configs,
249
+ )
250
+ response = lookup_result.to_bytes(4, "big")
251
+ self.transport.send_response(identity, response)
252
+ except json.JSONDecodeError as e:
253
+ logger.error(f"Error decoding JSON in lookup request: {e}")
254
+ except UnicodeDecodeError as e:
255
+ logger.error(f"Error decoding UTF-8 in lookup request: {e}")
256
+ except Exception as e:
257
+ logger.error(f"Error processing lookup request: {e}")
258
+
259
+ logger.info("lmcache lookup server started")
260
+ self.thread = threading.Thread(
261
+ target=process_request,
262
+ daemon=True,
263
+ name="lookup-server-thread",
264
+ )
265
+ self.thread.start()
266
+
267
+ def __enter__(self):
268
+ return self
269
+
270
+ def __exit__(self, exc_type, exc_val, exc_tb):
271
+ self.close()
272
+ return False
273
+
274
+ def close(self):
275
+ # Stop the processing thread first
276
+ self.running = False
277
+
278
+ # Wait for thread to finish with timeout
279
+ if self.thread.is_alive():
280
+ self.thread.join(timeout=2.0)
281
+ if self.thread.is_alive():
282
+ logger.warning("Lookup server thread did not terminate gracefully")
283
+
284
+ # Close transport after thread is stopped
285
+ self.transport.close()