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,99 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Optional, Union
4
+
5
+ # Third Party
6
+ import torch
7
+
8
+ # First Party
9
+ from lmcache.logging import init_logger
10
+ from lmcache.v1.cache_engine import LMCacheEngine
11
+ from lmcache.v1.config import LMCacheEngineConfig
12
+ from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
13
+ from lmcache.v1.metadata import LMCacheMetadata
14
+
15
+ logger = init_logger(__name__)
16
+
17
+
18
+ class LMCacheBypassLookupClient(LookupClientInterface):
19
+ """
20
+ Bypass lookup client that directly calls LMCacheEngine.lookup()
21
+ instead of using ZMQ communication. This is particularly useful
22
+ for MLA scenarios where only rank 0 needs to perform lookups.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ config: LMCacheEngineConfig,
28
+ metadata: LMCacheMetadata,
29
+ lmcache_engine: LMCacheEngine,
30
+ ):
31
+ """
32
+ Initialize the bypass lookup client.
33
+
34
+ Args:
35
+ config: The LMCacheEngine configuration
36
+ metadata: The LMCacheEngine metadata
37
+ lmcache_engine: The LMCacheEngine instance to use for lookups
38
+ """
39
+ assert isinstance(config, LMCacheEngineConfig), (
40
+ "LMCache v1 configuration should be passed."
41
+ )
42
+
43
+ self.lmcache_engine = lmcache_engine
44
+ self.config = config
45
+
46
+ # Use the token database from the provided LMCacheEngine
47
+ self.token_database = self.lmcache_engine.token_database
48
+ self.enable_blending = self.config.enable_blending
49
+
50
+ logger.info("LMCacheBypassLookupClient initialized")
51
+
52
+ def lookup(
53
+ self,
54
+ token_ids: Union[torch.Tensor, list[int]],
55
+ lookup_id: str,
56
+ request_configs: Optional[dict] = None,
57
+ ) -> Optional[int]:
58
+ try:
59
+ if not self.enable_blending:
60
+ # Process tokens to get hashes and offsets
61
+ hashes = []
62
+ offsets = []
63
+ for start, end, key in self.token_database.process_tokens(
64
+ token_ids, make_key=False
65
+ ):
66
+ hashes.append(key)
67
+ offsets.append(end - start)
68
+ if not hashes:
69
+ return 0
70
+
71
+ # Call LMCacheEngine lookup with hashes and offsets
72
+ result = self.lmcache_engine.lookup(
73
+ hashes=hashes,
74
+ offsets=offsets,
75
+ lookup_id=lookup_id,
76
+ pin=True,
77
+ request_configs=request_configs,
78
+ )
79
+ else:
80
+ # For blending mode, pass tokens directly
81
+ result = self.lmcache_engine.lookup(
82
+ tokens=token_ids,
83
+ lookup_id=lookup_id,
84
+ pin=True,
85
+ request_configs=request_configs,
86
+ )
87
+
88
+ return result
89
+
90
+ except Exception as e:
91
+ logger.error(f"Error in bypass lookup: {e}")
92
+ return 0
93
+
94
+ def supports_producer_reuse(self) -> bool:
95
+ return True
96
+
97
+ def close(self):
98
+ # No resources to clean up for bypass client
99
+ logger.info("LMCacheBypassLookupClient closed")
@@ -0,0 +1,87 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import Optional, Union
4
+
5
+ # Third Party
6
+ import torch
7
+
8
+ # First Party
9
+ from lmcache.logging import init_logger
10
+ from lmcache.utils import CacheEngineKey
11
+ from lmcache.v1.config import LMCacheEngineConfig
12
+ from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
13
+ from lmcache.v1.metadata import LMCacheMetadata
14
+
15
+ logger = init_logger(__name__)
16
+
17
+
18
+ class MooncakeLookupClient(LookupClientInterface):
19
+ def __init__(
20
+ self,
21
+ config: LMCacheEngineConfig,
22
+ metadata: LMCacheMetadata,
23
+ master_addr: str,
24
+ ):
25
+ # Third Party
26
+ from mooncake.store import MooncakeDistributedStore
27
+
28
+ self.store = MooncakeDistributedStore()
29
+ self.store.setup(
30
+ "localhost",
31
+ "P2PHANDSHAKE",
32
+ 0,
33
+ 16 * 1024 * 1024,
34
+ "tcp",
35
+ "",
36
+ master_addr,
37
+ )
38
+
39
+ # Initialize token database for processing tokens
40
+ assert isinstance(config, LMCacheEngineConfig), (
41
+ "LMCache v1 configuration is should be passed."
42
+ )
43
+
44
+ # First Party
45
+ from lmcache.v1.token_database import ChunkedTokenDatabase
46
+
47
+ assert not config.enable_blending, (
48
+ "LMCache v1 blending is not supported in MooncakeLookupClient yet."
49
+ )
50
+ self.token_database = ChunkedTokenDatabase(config, metadata)
51
+
52
+ def lookup(
53
+ self,
54
+ token_ids: Union[torch.Tensor, list[int]],
55
+ lookup_id: Optional[str] = None,
56
+ request_configs: Optional[dict] = None,
57
+ ) -> Optional[int]:
58
+ # process token_ids to cacheengine keys
59
+ keys = []
60
+ ends = []
61
+ for start, end, key in self.token_database.process_tokens(token_ids):
62
+ assert isinstance(key, CacheEngineKey)
63
+ keys.append(key.to_string())
64
+ ends.append(end)
65
+
66
+ # Use batch_is_exist to check all keys at once
67
+ # rets is list of int: 1 = found, 0 = not found, -1 = error
68
+ rets = self.store.batch_is_exist(keys)
69
+
70
+ # Find the first key that doesn't exist (ret != 1)
71
+ # This follows the same logic as cache engine's lookup method
72
+ for i, ret in enumerate(rets):
73
+ if ret != 1: # Not found or error
74
+ # Return the end position of the previous chunk
75
+ # If i == 0, no chunks were found, return 0
76
+ return ends[i - 1] if i > 0 else 0
77
+
78
+ # All keys were found, return the last end position
79
+ return ends[-1] if ends else 0
80
+
81
+ def supports_producer_reuse(self) -> bool:
82
+ """Return True as MooncakeLookupClient supports producer kvcache reuse"""
83
+ return True
84
+
85
+ def close(self):
86
+ # nothing here
87
+ pass
@@ -0,0 +1,77 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # Standard
4
+ from typing import Dict, Type
5
+ import importlib
6
+ import inspect
7
+ import pkgutil
8
+
9
+ # First Party
10
+ from lmcache.logging import init_logger
11
+ from lmcache.v1.lookup_client.record_strategies.base import (
12
+ AsyncRecorder,
13
+ RecordStrategy,
14
+ )
15
+
16
+ logger = init_logger(__name__)
17
+
18
+
19
+ def _discover_strategies() -> Dict[str, Type[RecordStrategy]]:
20
+ strategies = {}
21
+ # First Party
22
+ from lmcache.v1.lookup_client import record_strategies
23
+
24
+ for importer, modname, ispkg in pkgutil.iter_modules(
25
+ record_strategies.__path__, record_strategies.__name__ + "."
26
+ ):
27
+ try:
28
+ module = importlib.import_module(modname)
29
+ for name, obj in inspect.getmembers(module, inspect.isclass):
30
+ if issubclass(obj, RecordStrategy) and obj is not RecordStrategy:
31
+ # Use module name as strategy name
32
+ strategy_name = modname.split(".")[-1]
33
+ strategies[strategy_name] = obj
34
+ except Exception:
35
+ continue
36
+ return strategies
37
+
38
+
39
+ _strategies_cache = None
40
+
41
+
42
+ def _get_strategies() -> Dict[str, Type[RecordStrategy]]:
43
+ global _strategies_cache
44
+ if _strategies_cache is None:
45
+ _strategies_cache = _discover_strategies()
46
+ return _strategies_cache
47
+
48
+
49
+ def create_record_strategy(config) -> RecordStrategy:
50
+ strategies = _get_strategies()
51
+ strategy_name = config.chunk_statistics_strategy
52
+ chunk_size = config.chunk_size
53
+ if strategy_name not in strategies:
54
+ raise ValueError(
55
+ f"Unknown strategy: {strategy_name}. Available: {list(strategies.keys())}"
56
+ )
57
+ return strategies[strategy_name](config, chunk_size) # type: ignore[call-arg]
58
+
59
+
60
+ def list_record_strategies() -> list[str]:
61
+ return list(_get_strategies().keys())
62
+
63
+
64
+ __all__ = [
65
+ "AsyncRecorder",
66
+ "RecordStrategy",
67
+ "create_record_strategy",
68
+ "list_record_strategies",
69
+ ]
70
+
71
+
72
+ def __getattr__(name):
73
+ strategies = _get_strategies()
74
+ for strategy_class in strategies.values():
75
+ if strategy_class.__name__ == name:
76
+ return strategy_class
77
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,327 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # Standard
4
+ from abc import ABC, abstractmethod
5
+ from typing import Any, Union
6
+ import queue
7
+ import threading
8
+ import time
9
+
10
+ # Third Party
11
+ import torch
12
+
13
+ # First Party
14
+ from lmcache.logging import init_logger
15
+ from lmcache.v1.token_database import ChunkedTokenDatabase
16
+
17
+ logger = init_logger(__name__)
18
+
19
+
20
+ class RecordStrategy(ABC):
21
+ """Base class for chunk recording strategies."""
22
+
23
+ def __init__(self, chunk_size: int):
24
+ """Initialize the recording strategy.
25
+
26
+ Args:
27
+ chunk_size: Size of each token chunk for processing
28
+ """
29
+ self.chunk_size = chunk_size
30
+ self.total_chunks = 0
31
+ self.unique_chunks_count = 0
32
+ self.lock = threading.RLock()
33
+
34
+ self._token_db = ChunkedTokenDatabase()
35
+ self._token_db.chunk_size = chunk_size
36
+
37
+ def _compute_chunk_hashes(self, token_ids: list[int]) -> list[int]:
38
+ """Compute prefix hashes for all chunks using ChunkedTokenDatabase.
39
+
40
+ Args:
41
+ token_ids: List of token IDs to process
42
+
43
+ Returns:
44
+ List of hash values (integers) for each chunk.
45
+ """
46
+ chunk_hashes = []
47
+ for _, _, hash_val in self._token_db.process_tokens(
48
+ tokens=token_ids, make_key=False
49
+ ):
50
+ chunk_hashes.append(hash_val)
51
+ return chunk_hashes
52
+
53
+ def _compute_chunk_hashes_hex(self, token_ids: list[int]) -> list[str]:
54
+ """Compute prefix hashes for all chunks and return as hex strings.
55
+
56
+ Args:
57
+ token_ids: List of token IDs to process
58
+
59
+ Returns:
60
+ List of hash values (hex strings) for each chunk.
61
+ """
62
+ chunk_hashes = []
63
+ for hash_val in self._compute_chunk_hashes(token_ids):
64
+ if hash_val < 0:
65
+ hash_val = hash_val & ((1 << 64) - 1)
66
+ chunk_hashes.append(hex(hash_val))
67
+ return chunk_hashes
68
+
69
+ @abstractmethod
70
+ def preprocess(self, token_ids: list[int]) -> Any:
71
+ """Preprocess token IDs before recording.
72
+
73
+ This method is called to transform raw token IDs into a format suitable
74
+ for the specific recording strategy. For example, it might compute hash
75
+ positions for a bloom filter or convert hashes to hex strings for file storage.
76
+
77
+ Args:
78
+ token_ids: List of token IDs to preprocess
79
+
80
+ Returns:
81
+ Preprocessed data in strategy-specific format. The return type depends
82
+ on the concrete strategy implementation.
83
+ """
84
+ pass
85
+
86
+ @abstractmethod
87
+ def record(self, preprocessed_data: Any, lookup_id: str) -> None:
88
+ """Record the preprocessed chunk data.
89
+
90
+ This method performs the actual recording operation using the preprocessed
91
+ data. It should update internal statistics (total_chunks,
92
+ unique_chunks_count) and perform strategy-specific recording (e.g., update
93
+ bloom filter, write to file).
94
+
95
+ This method must be thread-safe as it may be called from async worker
96
+ threads.
97
+
98
+ Args:
99
+ preprocessed_data: Data returned from preprocess() method
100
+ lookup_id: Unique identifier for this lookup operation
101
+ """
102
+ pass
103
+
104
+ @abstractmethod
105
+ def reset(self) -> None:
106
+ """Reset all statistics and internal state.
107
+
108
+ This method should clear all recorded data and reset counters to their
109
+ initial state. It should be safe to call at any time.
110
+ """
111
+
112
+ def get_statistics(self) -> dict:
113
+ """Get current statistics.
114
+
115
+ Returns:
116
+ Dictionary containing statistics about recorded chunks, including:
117
+ - total_chunks: Total number of chunks processed
118
+ - unique_chunks: Number of unique chunks seen
119
+ - duplicate_chunks: Number of duplicate chunks
120
+ - reuse_rate: Ratio of duplicate to total chunks
121
+ """
122
+ with self.lock:
123
+ dup = self.total_chunks - self.unique_chunks_count
124
+ base_stats = {
125
+ "total_chunks": self.total_chunks,
126
+ "unique_chunks": self.unique_chunks_count,
127
+ "duplicate_chunks": dup,
128
+ "reuse_rate": dup / self.total_chunks if self.total_chunks > 0 else 0.0,
129
+ }
130
+ return base_stats
131
+
132
+ def setup_metrics(self, prometheus_logger) -> None:
133
+ """Setup Prometheus metrics for this strategy.
134
+
135
+ Args:
136
+ prometheus_logger: Prometheus logger instance to register metrics with
137
+ """
138
+ prometheus_logger.chunk_statistics_total_chunks.set_function(
139
+ lambda: self.total_chunks
140
+ )
141
+ prometheus_logger.chunk_statistics_unique_chunks.set_function(
142
+ lambda: self.unique_chunks_count
143
+ )
144
+ prometheus_logger.chunk_statistics_reuse_rate.set_function(
145
+ lambda: (self.total_chunks - self.unique_chunks_count) / self.total_chunks
146
+ if self.total_chunks > 0
147
+ else 0.0
148
+ )
149
+
150
+ def close(self) -> None: # noqa: B027
151
+ """Clean up resources.
152
+
153
+ This method is called when the strategy is no longer needed. Subclasses
154
+ should override this to clean up any resources (e.g., close file handles).
155
+ """
156
+ pass
157
+
158
+
159
+ class AsyncRecorder:
160
+ """Async processing infrastructure for RecordStrategy.
161
+
162
+ This class wraps a RecordStrategy and provides asynchronous processing
163
+ capabilities using a background worker thread and queue.
164
+ """
165
+
166
+ def __init__(
167
+ self,
168
+ strategy: RecordStrategy,
169
+ queue_capacity: int = 100000,
170
+ preprocess_in_caller: bool = False,
171
+ ):
172
+ """Initialize the async recorder.
173
+
174
+ Args:
175
+ strategy: The RecordStrategy instance to wrap
176
+ queue_capacity: Maximum number of items in the async queue
177
+ preprocess_in_caller: If True, preprocess in caller thread before queueing;
178
+ if False, preprocess in worker thread
179
+ """
180
+ self.strategy = strategy
181
+ self.queue_capacity = queue_capacity
182
+ self.preprocess_in_caller = preprocess_in_caller
183
+
184
+ self.async_queue: queue.Queue = queue.Queue(maxsize=queue_capacity)
185
+ self.async_shutdown = False
186
+ self.queue_full_blocks = 0
187
+ self.queue_max_size = 0
188
+ self.lock = threading.RLock()
189
+
190
+ self.async_worker_thread = threading.Thread(
191
+ target=self._async_worker,
192
+ daemon=True,
193
+ name=f"{strategy.__class__.__name__}AsyncWorker",
194
+ )
195
+ self.async_worker_thread.start()
196
+
197
+ def _async_worker(self) -> None:
198
+ """Background worker thread that processes queued items."""
199
+ while not self.async_shutdown:
200
+ try:
201
+ item = self.async_queue.get(timeout=0.1)
202
+ if item is None:
203
+ break
204
+ data, lookup_id = item
205
+ if self.preprocess_in_caller:
206
+ preprocessed_data = data
207
+ else:
208
+ preprocessed_data = self.strategy.preprocess(data)
209
+ self.strategy.record(preprocessed_data, lookup_id)
210
+ self.async_queue.task_done()
211
+ except queue.Empty:
212
+ continue
213
+ except Exception as e:
214
+ logger.error("Async worker error: %s", e, exc_info=True)
215
+
216
+ # Process remaining items
217
+ while not self.async_queue.empty():
218
+ try:
219
+ item = self.async_queue.get_nowait()
220
+ if item is not None:
221
+ data, lookup_id = item
222
+ if self.preprocess_in_caller:
223
+ preprocessed_data = data
224
+ else:
225
+ preprocessed_data = self.strategy.preprocess(data)
226
+ self.strategy.record(preprocessed_data, lookup_id)
227
+ self.async_queue.task_done()
228
+ except (queue.Empty, Exception):
229
+ break
230
+
231
+ def record_async(
232
+ self, token_ids: Union[torch.Tensor, list[int]], lookup_id: str
233
+ ) -> None:
234
+ """Record token IDs asynchronously.
235
+
236
+ Args:
237
+ token_ids: Token IDs to record (tensor or list)
238
+ lookup_id: Unique identifier for this lookup operation
239
+ """
240
+ if isinstance(token_ids, torch.Tensor):
241
+ token_ids = token_ids.tolist()
242
+
243
+ if self.preprocess_in_caller:
244
+ data = self.strategy.preprocess(token_ids)
245
+ else:
246
+ data = token_ids
247
+
248
+ self._queue_item((data, lookup_id))
249
+
250
+ def _queue_item(self, item, timeout: float = 10.0) -> None:
251
+ """Add item to async queue with timeout handling."""
252
+ try:
253
+ self.async_queue.put(item, block=True, timeout=timeout)
254
+ except queue.Full:
255
+ with self.lock:
256
+ self.queue_full_blocks += 1
257
+ self.async_queue.put(item, block=True)
258
+
259
+ def get_statistics(self) -> dict:
260
+ """Get statistics including async queue metrics.
261
+
262
+ Returns:
263
+ Dictionary containing strategy statistics plus async queue metrics
264
+ """
265
+ stats = self.strategy.get_statistics()
266
+ with self.lock:
267
+ queue_size = self.async_queue.qsize()
268
+ self.queue_max_size = max(self.queue_max_size, queue_size)
269
+ stats["async_queue"] = {
270
+ "capacity": self.queue_capacity,
271
+ "current_size": queue_size,
272
+ "max_size_reached": self.queue_max_size,
273
+ "full_blocks": self.queue_full_blocks,
274
+ "utilization": queue_size / self.queue_capacity
275
+ if self.queue_capacity > 0
276
+ else 0.0,
277
+ }
278
+ return stats
279
+
280
+ def wait_for_completion(self, timeout: float = 5.0) -> bool:
281
+ """Wait for async queue to be processed.
282
+
283
+ Args:
284
+ timeout: Maximum time to wait in seconds
285
+
286
+ Returns:
287
+ True if queue is empty, False if timeout occurred
288
+ """
289
+ start_time = time.time()
290
+ while time.time() - start_time < timeout:
291
+ if self.async_queue.empty():
292
+ time.sleep(0.01)
293
+ if self.async_queue.empty():
294
+ return True
295
+ time.sleep(0.01)
296
+ return self.async_queue.empty()
297
+
298
+ def reset(self) -> None:
299
+ """Reset strategy and clear async queue."""
300
+ self.wait_for_completion(timeout=5.0)
301
+ with self.lock:
302
+ self.strategy.reset()
303
+ self.queue_full_blocks = 0
304
+ self.queue_max_size = 0
305
+ self._clear_queue()
306
+
307
+ def _clear_queue(self) -> None:
308
+ """Clear all items from async queue."""
309
+ while not self.async_queue.empty():
310
+ try:
311
+ self.async_queue.get_nowait()
312
+ except queue.Empty:
313
+ break
314
+
315
+ def close(self) -> None:
316
+ """Shutdown async worker and clean up resources."""
317
+ self.async_shutdown = True
318
+ try:
319
+ self.async_queue.put(None, block=False)
320
+ except queue.Full:
321
+ pass
322
+
323
+ self.async_worker_thread.join(timeout=5.0)
324
+ if self.async_worker_thread.is_alive():
325
+ logger.warning("Async worker did not stop gracefully")
326
+
327
+ self.strategy.close()