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,571 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Shared utilities for check modes"""
3
+
4
+ # Standard
5
+ from typing import Optional
6
+ import asyncio
7
+ import hashlib
8
+ import threading
9
+ import time
10
+
11
+ # Third Party
12
+ import torch
13
+
14
+ # First Party
15
+ from lmcache.utils import CacheEngineKey
16
+
17
+ # Import from lmcache with absolute paths
18
+ from lmcache.v1.memory_management import MemoryFormat, MemoryObj
19
+ from lmcache.v1.metadata import LMCacheMetadata
20
+ from lmcache.v1.storage_backend.remote_backend import RemoteBackend
21
+ from lmcache.v1.storage_backend.storage_manager import StorageManager
22
+
23
+ DTYPE_MAP = {
24
+ "float32": torch.float32,
25
+ "float16": torch.float16,
26
+ "bfloat16": torch.bfloat16,
27
+ }
28
+
29
+ DEFAULT_KV_DTYPE_STR = "bfloat16"
30
+ DEFAULT_OBJ_SIZE = 1024
31
+
32
+
33
+ def _compute_kv_shape(
34
+ obj_size: int,
35
+ ) -> tuple:
36
+ """Compute a kv_shape that yields the given obj_size.
37
+
38
+ The returned shape is in vllm format:
39
+ ``(num_layers, 2, num_tokens, num_heads, head_size)``.
40
+
41
+ The final KV_2LTD tensor has
42
+ ``2 * num_layers * num_tokens * (num_heads * head_size)``
43
+ elements, which equals *obj_size*.
44
+
45
+ We fix ``num_layers=1, num_heads=1`` and split the
46
+ remaining factor between ``num_tokens`` and ``head_size``
47
+ so that ``num_tokens * head_size = obj_size // 2``.
48
+ """
49
+ if obj_size % 2 != 0:
50
+ raise ValueError("obj_size must be even (got %d)" % obj_size)
51
+ half = obj_size // 2
52
+ # (num_layers, kv_dim, num_tokens, num_heads, head_size)
53
+ return (1, 2, half, 1, 1)
54
+
55
+
56
+ def parse_kv_dtype(kv_dtype_str: str) -> Optional[torch.dtype]:
57
+ """Parse a kv_dtype string to a torch.dtype.
58
+
59
+ Returns None if the string is not recognized.
60
+ """
61
+ return DTYPE_MAP.get(kv_dtype_str)
62
+
63
+
64
+ def _get_default_metadata(
65
+ model: str,
66
+ kv_dtype: torch.dtype = torch.bfloat16,
67
+ obj_size: Optional[int] = None,
68
+ ) -> LMCacheMetadata:
69
+ """Get default metadata for testing.
70
+
71
+ When *obj_size* is given the ``kv_shape`` is computed so
72
+ that the resulting KV_2LTD tensor has exactly *obj_size*
73
+ elements. Otherwise a small default shape is used.
74
+ """
75
+ if obj_size is not None:
76
+ kv_shape = _compute_kv_shape(obj_size)
77
+ else:
78
+ kv_shape = _compute_kv_shape(DEFAULT_OBJ_SIZE)
79
+ return LMCacheMetadata(
80
+ model_name=model,
81
+ world_size=8,
82
+ local_world_size=8,
83
+ worker_id=0,
84
+ local_worker_id=0,
85
+ kv_dtype=kv_dtype,
86
+ kv_shape=kv_shape,
87
+ )
88
+
89
+
90
+ def create_test_key(
91
+ model: str,
92
+ key_id: str = "test_key",
93
+ kv_dtype: torch.dtype = torch.bfloat16,
94
+ ) -> CacheEngineKey:
95
+ """Create a test CacheEngineKey."""
96
+ return CacheEngineKey(
97
+ model_name=model,
98
+ world_size=8,
99
+ worker_id=0,
100
+ chunk_hash=int(hashlib.sha256(key_id.encode()).hexdigest(), 16),
101
+ dtype=kv_dtype,
102
+ )
103
+
104
+
105
+ def create_test_memory_obj_for_storage_manager(
106
+ storage_manager: StorageManager, metadata: LMCacheMetadata
107
+ ) -> Optional[MemoryObj]:
108
+ """Create a test MemoryObj for testing with StorageManager."""
109
+ # The metadata.kv_shape is in vllm format:
110
+ # [num_layers, 2, num_tokens, num_heads, head_size]
111
+ # For KV_2LTD format, we need shape: [2, num_layers, num_tokens, hidden_dim]
112
+ # where hidden_dim = num_heads * head_size
113
+
114
+ vllm_shape = metadata.kv_shape # [num_layers, 2, num_tokens, num_heads, head_size]
115
+ num_layers = vllm_shape[0] # 8
116
+ kv_dim = vllm_shape[1] # 2 (K and V)
117
+ num_tokens = vllm_shape[2] # 16
118
+ num_heads = vllm_shape[3] # 8
119
+ head_size = vllm_shape[4] # 16
120
+
121
+ # Convert to KV_2LTD format shape: [2, num_layers, num_tokens, hidden_dim]
122
+ hidden_dim = num_heads * head_size
123
+ kv_2ltd_shape = torch.Size([kv_dim, num_layers, num_tokens, hidden_dim])
124
+
125
+ memory_obj = storage_manager.allocate(
126
+ kv_2ltd_shape,
127
+ metadata.kv_dtype,
128
+ fmt=MemoryFormat.KV_2LTD,
129
+ eviction=True,
130
+ busy_loop=False,
131
+ )
132
+ return memory_obj
133
+
134
+
135
+ def create_storage_manager_with_config(
136
+ model: str,
137
+ kv_dtype: torch.dtype = torch.bfloat16,
138
+ obj_size: Optional[int] = None,
139
+ ):
140
+ """Create storage manager with default configuration"""
141
+ # First Party
142
+ from lmcache.integration.vllm.utils import lmcache_get_or_create_config
143
+ from lmcache.v1.event_manager import EventManager
144
+
145
+ config = lmcache_get_or_create_config()
146
+ metadata = _get_default_metadata(model, kv_dtype=kv_dtype, obj_size=obj_size)
147
+
148
+ # Create event manager
149
+ event_manager = EventManager()
150
+
151
+ # Create storage manager
152
+ storage_manager = StorageManager(
153
+ config=config,
154
+ metadata=metadata,
155
+ event_manager=event_manager,
156
+ )
157
+
158
+ return storage_manager
159
+
160
+
161
+ def find_remote_backend(storage_manager: StorageManager) -> Optional[RemoteBackend]:
162
+ """Find remote backend from storage manager"""
163
+ for backend_name, backend in storage_manager.storage_backends.items():
164
+ if isinstance(backend, RemoteBackend):
165
+ return backend
166
+ return None
167
+
168
+
169
+ def wait_put_tasks_complete(
170
+ remote_backend: Optional[RemoteBackend], max_wait_time: float = 5.0
171
+ ):
172
+ """Wait for remote backend put tasks to complete"""
173
+ if remote_backend is None:
174
+ return
175
+
176
+ check_interval = 0.001
177
+ elapsed_time = 0.0
178
+
179
+ while elapsed_time < max_wait_time:
180
+ if not remote_backend.put_tasks:
181
+ break
182
+ time.sleep(check_interval)
183
+ elapsed_time += check_interval
184
+
185
+ # Log warning if timeout
186
+ remaining_tasks = len(remote_backend.put_tasks)
187
+ if remaining_tasks > 0:
188
+ print(
189
+ f"Warning: {remaining_tasks} remote put tasks still "
190
+ f"pending after {max_wait_time}s timeout"
191
+ )
192
+
193
+
194
+ def create_memory_objects_batch(
195
+ storage_manager: StorageManager, metadata: LMCacheMetadata, batch_size: int
196
+ ) -> list[MemoryObj]:
197
+ """Create a batch of memory objects for reuse"""
198
+ memory_objs = []
199
+ for i in range(batch_size):
200
+ memory_obj = create_test_memory_obj_for_storage_manager(
201
+ storage_manager, metadata
202
+ )
203
+ if memory_obj is not None:
204
+ memory_obj.ref_count_up()
205
+ memory_objs.append(memory_obj)
206
+ return memory_objs
207
+
208
+
209
+ async def flow_control_check(
210
+ remote_backend: Optional[RemoteBackend], concurrency: int, sleep_count: float = 1.0
211
+ ) -> float:
212
+ """Check flow control and wait if necessary"""
213
+ if remote_backend is None:
214
+ return sleep_count
215
+
216
+ high_watermark = 100 * concurrency
217
+ low_watermark = 10 * concurrency
218
+ current_tasks = len(remote_backend.put_tasks)
219
+
220
+ while current_tasks > high_watermark:
221
+ current_tasks = len(remote_backend.put_tasks)
222
+ if current_tasks > high_watermark:
223
+ # Too many pending tasks, wait before proceeding
224
+ sleep_sec = 0.1 * sleep_count
225
+ current_tasks = len(remote_backend.put_tasks)
226
+ await asyncio.sleep(sleep_sec)
227
+ current_tasks_after_sleep = len(remote_backend.put_tasks)
228
+ if current_tasks_after_sleep > low_watermark:
229
+ sleep_count *= 2.0
230
+ elif current_tasks_after_sleep == 0:
231
+ sleep_count /= 2.0
232
+ continue
233
+ if current_tasks <= low_watermark:
234
+ break
235
+
236
+ return sleep_count
237
+
238
+
239
+ async def run_perf_test_with_timeout(func, args_list, timeout=30.0):
240
+ """Common performance test framework with timeout handling"""
241
+ times = []
242
+ results = [] # Collect results for each operation
243
+ for i, args in enumerate(args_list):
244
+ try:
245
+ start = time.perf_counter()
246
+ result = await asyncio.wait_for(func(*args), timeout=timeout)
247
+ end = time.perf_counter()
248
+ times.append((end - start) * 1000)
249
+ results.append(result)
250
+ print(
251
+ f" Test {i + 1}/{len(args_list)} completed in "
252
+ f"{(end - start) * 1000:.2f}ms"
253
+ )
254
+ except asyncio.TimeoutError:
255
+ print(f" Test {i + 1}/{len(args_list)} timed out after {timeout}s")
256
+ times.append(timeout * 1000)
257
+ results.append(None)
258
+ except Exception as e:
259
+ print(f" Test {i + 1}/{len(args_list)} failed: {e}")
260
+ times.append(0)
261
+ results.append(None)
262
+
263
+ if times:
264
+ return {
265
+ "time_stats": {
266
+ "avg": sum(times) / len(times),
267
+ "max": max(times),
268
+ "min": min(times),
269
+ },
270
+ "results": results,
271
+ }
272
+ else:
273
+ return {"time_stats": {"avg": 0, "max": 0, "min": 0}, "results": []}
274
+
275
+
276
+ def _format_throughput(avg_ms: float, obj_bytes: int) -> str:
277
+ """Format throughput as a human-readable string."""
278
+ if avg_ms <= 0 or obj_bytes <= 0:
279
+ return "N/A"
280
+ bps = obj_bytes / (avg_ms / 1000.0)
281
+ if bps >= 1 << 30:
282
+ return "%.2f GB/s" % (bps / (1 << 30))
283
+ if bps >= 1 << 20:
284
+ return "%.2f MB/s" % (bps / (1 << 20))
285
+ return "%.2f KB/s" % (bps / (1 << 10))
286
+
287
+
288
+ def print_performance_results(
289
+ stats_data,
290
+ obj_bytes: int = 0,
291
+ throughput_ops: Optional[set] = None,
292
+ ):
293
+ """Print performance results in a formatted table.
294
+
295
+ Args:
296
+ stats_data: list of (op, stats, results, pass_count).
297
+ obj_bytes: size of one object in bytes. When > 0 a
298
+ throughput column is shown for operations listed
299
+ in *throughput_ops*.
300
+ throughput_ops: set of operation name prefixes that
301
+ should show throughput (e.g. {"STORE", "LOAD"}).
302
+ Defaults to common data-transfer operations.
303
+ """
304
+ if throughput_ops is None:
305
+ throughput_ops = {
306
+ "STORE",
307
+ "LOAD",
308
+ "PUT",
309
+ "GET",
310
+ }
311
+ show_tp = obj_bytes > 0
312
+
313
+ sep_len = 118 if show_tp else 100
314
+ tp_hdr = " | %s" % "Throughput".center(14) if show_tp else ""
315
+ print("\nPerformance Results:")
316
+ print("-" * sep_len)
317
+ print(
318
+ f"| {'Operation':<20} | {'Avg (ms)':>12} "
319
+ f"| {'Max (ms)':>12} "
320
+ f"| {'Min (ms)':>12} "
321
+ f"| {'Pass/All':>10} "
322
+ f"| {'Pass Rate':>10} |" + tp_hdr
323
+ )
324
+ print("-" * sep_len)
325
+ for op, stats, results, pass_count in stats_data:
326
+ total = len(results)
327
+ pass_all = f"{pass_count}/{total}"
328
+ pass_rate = pass_count / total * 100 if total > 0 else 0
329
+ tp_col = ""
330
+ if show_tp:
331
+ is_data_op = any(op.startswith(p) for p in throughput_ops)
332
+ if is_data_op:
333
+ tp_col = " | %s" % _format_throughput(
334
+ stats["avg"],
335
+ obj_bytes,
336
+ ).center(14)
337
+ else:
338
+ tp_col = " | %s" % "-".center(14)
339
+
340
+ print(
341
+ f"| {op:<20} "
342
+ f"| {stats['avg']:>12.6f} "
343
+ f"| {stats['max']:>12.6f} "
344
+ f"| {stats['min']:>12.6f} "
345
+ f"| {pass_all:>10} "
346
+ f"| {pass_rate:>9.1f}% |" + tp_col
347
+ )
348
+ print("-" * sep_len)
349
+
350
+
351
+ def validate_get_results(get_results, exist_keys, exist_memories, num_tests):
352
+ """Validate GET operation results and return statistics"""
353
+ content_valid_count = 0
354
+ for i, result in enumerate(get_results["results"]):
355
+ if result is None:
356
+ print(f" GET for key {exist_keys[i]} returned None result")
357
+ continue
358
+ try:
359
+ if result.tensor is None:
360
+ print(f" GET for key {exist_keys[i]} returned None tensor")
361
+ continue
362
+
363
+ if exist_memories[i].tensor is None:
364
+ print(f" Original memory object {i} has None tensor")
365
+ continue
366
+
367
+ # Compare data content
368
+ data_match = torch.equal(result.tensor, exist_memories[i].tensor)
369
+
370
+ if data_match:
371
+ content_valid_count += 1
372
+ else:
373
+ print(f" GET for key {exist_keys[i]} returned incorrect memory object")
374
+ print(" Data content mismatch detected")
375
+
376
+ except Exception as e:
377
+ print(f" Data comparison failed for key {exist_keys[i]}: {e}")
378
+ # Standard
379
+ import traceback
380
+
381
+ traceback.print_exc()
382
+
383
+ # Calculate pass rates
384
+ not_none_count = sum(1 for r in get_results["results"] if r is not None)
385
+ content_pass_rate = content_valid_count / num_tests * 100
386
+ print(f" Validation (not None): {not_none_count}/{num_tests} passed")
387
+ print(
388
+ f" Validation (content correct): {content_valid_count}/{num_tests}"
389
+ f" passed ({content_pass_rate:.1f}%)"
390
+ )
391
+ return content_valid_count, not_none_count
392
+
393
+
394
+ async def run_common_test_framework(
395
+ test_context,
396
+ model: str,
397
+ num_tests: int = 5,
398
+ settle_time: float = 0.0,
399
+ ):
400
+ """
401
+ Common test framework for both storage manager and remote backend tests.
402
+
403
+ Args:
404
+ test_context: A dictionary containing test-specific functions and objects:
405
+ - 'create_test_data_func': Function to create test data
406
+ - 'async_contains_func': Async function for contains operations
407
+ - 'async_put_func': Async function for put operations
408
+ - 'async_get_func': Async function for get operations
409
+ - 'validate_get_func': Function to validate get results
410
+ - 'test_object': The main test object (storage_manager or backend)
411
+ - 'extra_args': Extra arguments for test data creation (optional)
412
+ model: Model name for testing
413
+ num_tests: Number of tests to run
414
+ """
415
+ print("Testing basic operations...")
416
+
417
+ # Create test data using the provided function
418
+ extra_args = test_context.get("extra_args", [])
419
+ extra_kwargs = {}
420
+ if "kv_dtype" in test_context:
421
+ extra_kwargs["kv_dtype"] = test_context["kv_dtype"]
422
+ if extra_args:
423
+ non_exist_keys, exist_keys, exist_memories, num_tests = test_context[
424
+ "create_test_data_func"
425
+ ](test_context["test_object"], *extra_args, model, num_tests, **extra_kwargs)
426
+ else:
427
+ kv_dtype = test_context.get("kv_dtype")
428
+ obj_size = test_context.get("obj_size")
429
+ meta_kw = {}
430
+ if kv_dtype:
431
+ meta_kw["kv_dtype"] = kv_dtype
432
+ if obj_size is not None:
433
+ meta_kw["obj_size"] = obj_size
434
+ metadata = _get_default_metadata(model, **meta_kw)
435
+ non_exist_keys, exist_keys, exist_memories, num_tests = test_context[
436
+ "create_test_data_func"
437
+ ](test_context["test_object"], metadata, model, num_tests, **extra_kwargs)
438
+
439
+ # Phase 1: exists test (key does not exist)
440
+ print("Phase 1: Testing exists for non-existing keys...")
441
+
442
+ exists_non_exist_res = await run_perf_test_with_timeout(
443
+ test_context["async_contains_func"],
444
+ [(test_context["test_object"], key) for key in non_exist_keys],
445
+ )
446
+ exists_non_exist_stats = exists_non_exist_res["time_stats"]
447
+ # Validation: All non-existing keys should return False
448
+ exists_non_exist_pass_count = sum(
449
+ 1 for r in exists_non_exist_res["results"] if r is False
450
+ )
451
+ pass_rate = exists_non_exist_pass_count / len(non_exist_keys) * 100
452
+ print(
453
+ f" Validation: {exists_non_exist_pass_count}/{len(non_exist_keys)} "
454
+ f"passed ({pass_rate:.1f}%)"
455
+ )
456
+
457
+ # Phase 2: put test (create new key)
458
+ print("Phase 2: Testing put operations...")
459
+
460
+ put_res = await run_perf_test_with_timeout(
461
+ test_context["async_put_func"],
462
+ [
463
+ (test_context["test_object"], exist_keys[i], exist_memories[i])
464
+ for i in range(num_tests)
465
+ ],
466
+ )
467
+ put_stats = put_res["time_stats"]
468
+ # Validation: All PUT operations should return True
469
+ put_pass_count = sum(1 for r in put_res["results"] if r is True)
470
+ pass_rate = put_pass_count / num_tests * 100
471
+ print(f" Validation: {put_pass_count}/{num_tests} passed ({pass_rate:.1f}%)")
472
+
473
+ if settle_time > 0:
474
+ print(" Waiting %.1fs for data to settle..." % settle_time)
475
+ await asyncio.sleep(settle_time)
476
+
477
+ # Phase 3: exists test (key exists)
478
+ print("Phase 3: Testing exists for existing keys...")
479
+
480
+ exists_exist_res = await run_perf_test_with_timeout(
481
+ test_context["async_contains_func"],
482
+ [(test_context["test_object"], key) for key in exist_keys],
483
+ )
484
+ exists_exist_stats = exists_exist_res["time_stats"]
485
+ # Validation: All existing keys should return True
486
+ exists_exist_pass_count = sum(1 for r in exists_exist_res["results"] if r is True)
487
+ pass_rate = exists_exist_pass_count / num_tests * 100
488
+ print(
489
+ f" Validation: {exists_exist_pass_count}/{num_tests} passed ({pass_rate:.1f}%)"
490
+ )
491
+
492
+ # Phase 4: get test (key exists)
493
+ print("Phase 4: Testing get operations...")
494
+
495
+ get_res = await run_perf_test_with_timeout(
496
+ test_context["async_get_func"],
497
+ [(test_context["test_object"], key) for key in exist_keys],
498
+ )
499
+ get_stats = get_res["time_stats"]
500
+ # Validation: Check for non-None results and content correctness
501
+ content_valid_count, not_none_count = test_context["validate_get_func"](
502
+ get_res, exist_keys, exist_memories, num_tests
503
+ )
504
+ # Use content_valid_count as the pass_count for GET operations
505
+ get_pass_count = content_valid_count
506
+
507
+ stats_data = [
508
+ (
509
+ "EXISTS (non-exist)",
510
+ exists_non_exist_stats,
511
+ exists_non_exist_res["results"],
512
+ exists_non_exist_pass_count,
513
+ ),
514
+ ("PUT", put_stats, put_res["results"], put_pass_count),
515
+ (
516
+ "EXISTS (exist)",
517
+ exists_exist_stats,
518
+ exists_exist_res["results"],
519
+ exists_exist_pass_count,
520
+ ),
521
+ ("GET", get_stats, get_res["results"], get_pass_count),
522
+ ]
523
+
524
+ # Compute per-object byte size for throughput display
525
+ tp_obj_size = test_context.get("obj_size") or DEFAULT_OBJ_SIZE
526
+ tp_kv_dtype = test_context.get("kv_dtype") or torch.bfloat16
527
+ obj_bytes = tp_obj_size * torch.tensor([], dtype=tp_kv_dtype).element_size()
528
+
529
+ # Use common performance results printing
530
+ print_performance_results(stats_data, obj_bytes=obj_bytes)
531
+
532
+
533
+ class EventLoopManager:
534
+ """Manages a dedicated event loop in a separate thread"""
535
+
536
+ def __init__(self):
537
+ self.loop = None
538
+ self.thread = None
539
+ self._loop_started = threading.Event()
540
+
541
+ def start(self):
542
+ """Start the event loop in a separate thread"""
543
+ if self.thread is not None and self.thread.is_alive():
544
+ return
545
+
546
+ self.loop = asyncio.new_event_loop()
547
+ self.thread = threading.Thread(target=self._run_loop, daemon=True)
548
+ self.thread.start()
549
+ self._loop_started.wait()
550
+
551
+ def _run_loop(self):
552
+ """Run the event loop"""
553
+ asyncio.set_event_loop(self.loop)
554
+ self._loop_started.set()
555
+ try:
556
+ self.loop.run_forever()
557
+ except Exception as e:
558
+ print(f"Event loop error: {e}")
559
+ finally:
560
+ self.loop.close()
561
+
562
+ def stop(self):
563
+ """Stop the event loop and thread"""
564
+ if self.loop and not self.loop.is_closed():
565
+ self.loop.call_soon_threadsafe(self.loop.stop)
566
+ if self.thread and self.thread.is_alive():
567
+ self.thread.join(timeout=5.0)
568
+
569
+ def get_loop(self):
570
+ """Get the event loop"""
571
+ return self.loop
@@ -0,0 +1,2 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
File without changes
@@ -0,0 +1,39 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import TYPE_CHECKING
4
+ import abc
5
+
6
+ # Third Party
7
+ import torch
8
+
9
+ if TYPE_CHECKING:
10
+ # First Party
11
+ from lmcache.v1.compute.attention.metadata import LMCAttnMetadata
12
+
13
+
14
+ class AttentionInterface(metaclass=abc.ABCMeta):
15
+ @abc.abstractmethod
16
+ def forward_contiguous(
17
+ self,
18
+ query: torch.Tensor,
19
+ key: torch.Tensor,
20
+ value: torch.Tensor,
21
+ output: torch.Tensor,
22
+ attn_metadata: "LMCAttnMetadata",
23
+ **kwargs,
24
+ ) -> torch.Tensor:
25
+ """
26
+ Perform forward pass of the attention mechanism.
27
+ """
28
+ raise NotImplementedError
29
+
30
+ @abc.abstractmethod
31
+ def init_attn_metadata(
32
+ self,
33
+ input_ids: torch.Tensor,
34
+ **kwargs,
35
+ ) -> "LMCAttnMetadata":
36
+ """
37
+ Initialize attention metadata.
38
+ """
39
+ raise NotImplementedError