lmcache-cli 0.5.7.dev0__py3-none-any.whl → 0.5.7rc3.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 (434) hide show
  1. lmcache/_version.py +3 -3
  2. lmcache/cli/commands/bench/engine_bench/command.py +92 -0
  3. lmcache/cli/commands/bench/engine_bench/config.py +63 -0
  4. lmcache/cli/commands/bench/engine_bench/interactive/schema.py +102 -0
  5. lmcache/cli/commands/bench/engine_bench/quality/scoring.py +8 -6
  6. lmcache/cli/commands/bench/engine_bench/request_sender.py +25 -6
  7. lmcache/cli/commands/bench/engine_bench/stats.py +1 -0
  8. lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +81 -7
  9. lmcache/cli/commands/bench/engine_bench/workloads/kv_tier_pressure.py +909 -0
  10. lmcache/cli/commands/bench/engine_bench/workloads/rag_qa_quality.py +10 -3
  11. lmcache/cli/commands/bench/server_bench/__init__.py +21 -5
  12. lmcache/cli/commands/bench/server_bench/allocator.py +78 -0
  13. lmcache/cli/commands/bench/server_bench/arguments.py +226 -0
  14. lmcache/cli/commands/bench/server_bench/buffers.py +119 -0
  15. lmcache/cli/commands/bench/server_bench/cases/base.py +10 -0
  16. lmcache/cli/commands/bench/server_bench/cases/baseline.py +79 -56
  17. lmcache/cli/commands/bench/server_bench/cases/concurrent.py +87 -0
  18. lmcache/cli/commands/bench/server_bench/client.py +293 -339
  19. lmcache/cli/commands/bench/server_bench/command.py +66 -244
  20. lmcache/cli/commands/bench/server_bench/config.py +13 -1
  21. lmcache/cli/commands/bench/server_bench/helpers.py +5 -262
  22. lmcache/cli/commands/bench/trace_analyze/__init__.py +39 -0
  23. lmcache/cli/commands/memory.py +78 -0
  24. lmcache/cli/commands/server.py +4 -0
  25. lmcache/cli/commands/trace/_stats.py +5 -3
  26. lmcache/inprocess/__init__.py +2 -0
  27. lmcache/inprocess/integration/__init__.py +2 -0
  28. lmcache/{integration → inprocess/integration}/base_service_factory.py +14 -12
  29. lmcache/inprocess/integration/tensorrt_llm/tensorrt_adapter.py +377 -0
  30. lmcache/inprocess/integration/vllm/__init__.py +2 -0
  31. lmcache/inprocess/integration/vllm/lmcache_connector_v1.py +210 -0
  32. lmcache/{integration → inprocess/integration}/vllm/lmcache_connector_v1_085.py +3 -1
  33. lmcache/{integration → inprocess/integration}/vllm/vllm_ec_adapter.py +1 -1
  34. lmcache/{integration → inprocess/integration}/vllm/vllm_service_factory.py +16 -12
  35. lmcache/{integration → inprocess/integration}/vllm/vllm_v1_adapter.py +6 -4
  36. lmcache/{v1 → inprocess}/standalone/__main__.py +19 -7
  37. lmcache/{v1 → inprocess}/standalone/manager.py +4 -2
  38. lmcache/{v1 → inprocess}/standalone/standalone_service_factory.py +5 -5
  39. lmcache/{tools → inprocess/tools}/controller_benchmark/README.md +2 -2
  40. lmcache/{tools → inprocess/tools}/controller_benchmark/__main__.py +3 -3
  41. lmcache/{tools → inprocess/tools}/controller_benchmark/benchmark.py +4 -4
  42. lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/admit.py +1 -1
  43. lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/deregister.py +1 -1
  44. lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/evict.py +1 -1
  45. lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/heartbeat.py +1 -1
  46. lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/p2p_lookup.py +1 -1
  47. lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/register.py +1 -1
  48. lmcache/{v1 → inprocess/v1}/api_server/__main__.py +14 -7
  49. lmcache/{v1 → inprocess/v1}/basic_check.py +1 -1
  50. lmcache/inprocess/v1/cache_controller/__init__.py +11 -0
  51. lmcache/{v1 → inprocess/v1}/cache_controller/commands/__init__.py +2 -2
  52. lmcache/{v1 → inprocess/v1}/cache_controller/commands/base.py +1 -1
  53. lmcache/{v1 → inprocess/v1}/cache_controller/commands/full_sync.py +2 -2
  54. lmcache/{v1 → inprocess/v1}/cache_controller/controller_manager.py +10 -7
  55. lmcache/inprocess/v1/cache_controller/controllers/__init__.py +11 -0
  56. lmcache/{v1 → inprocess/v1}/cache_controller/controllers/full_sync_tracker.py +2 -2
  57. lmcache/{v1 → inprocess/v1}/cache_controller/controllers/kv_controller.py +9 -7
  58. lmcache/{v1 → inprocess/v1}/cache_controller/controllers/registration_controller.py +9 -6
  59. lmcache/{v1 → inprocess/v1}/cache_controller/executor.py +2 -2
  60. lmcache/{v1 → inprocess/v1}/cache_controller/full_sync_sender.py +4 -4
  61. lmcache/{v1 → inprocess/v1}/cache_controller/message.py +2 -2
  62. lmcache/{v1 → inprocess/v1}/cache_controller/utils.py +8 -2
  63. lmcache/{v1 → inprocess/v1}/cache_controller/worker.py +8 -8
  64. lmcache/{v1 → inprocess/v1}/cache_engine.py +14 -19
  65. lmcache/{v1 → inprocess/v1}/check/__init__.py +4 -2
  66. lmcache/{v1 → inprocess/v1}/check/check_mode_gen.py +2 -2
  67. lmcache/{v1 → inprocess/v1}/check/check_mode_test_l2_adapter.py +2 -2
  68. lmcache/{v1 → inprocess/v1}/check/check_mode_test_remote.py +3 -3
  69. lmcache/{v1 → inprocess/v1}/check/check_mode_test_storage_manager.py +2 -2
  70. lmcache/{v1 → inprocess/v1}/check/utils.py +1 -1
  71. lmcache/{v1 → inprocess/v1}/ec_engine.py +1 -1
  72. lmcache/inprocess/v1/gpu_connector/__init__.py +184 -0
  73. lmcache/{v1 → inprocess/v1}/gpu_connector/gpu_connectors.py +2 -511
  74. lmcache/{v1 → inprocess/v1}/gpu_connector/hpu_connector.py +1 -1
  75. lmcache/{v1 → inprocess/v1}/gpu_connector/mock_gpu_connector.py +1 -1
  76. lmcache/{v1 → inprocess/v1}/gpu_connector/musa_connectors.py +5 -687
  77. lmcache/inprocess/v1/gpu_connector/utils.py +41 -0
  78. lmcache/{v1 → inprocess/v1}/gpu_connector/xpu_connectors.py +1 -565
  79. lmcache/{v1 → inprocess/v1}/health_monitor/base.py +5 -5
  80. lmcache/{v1 → inprocess/v1}/health_monitor/checks/remote_backend_check.py +6 -6
  81. lmcache/{v1 → inprocess/v1}/hidden_state_store.py +4 -3
  82. lmcache/{v1 → inprocess/v1}/internal_api_server/api_registry.py +15 -4
  83. lmcache/{v1 → inprocess/v1}/internal_api_server/api_server.py +1 -1
  84. lmcache/inprocess/v1/internal_api_server/controller/__init__.py +0 -0
  85. lmcache/inprocess/v1/internal_api_server/vllm/__init__.py +0 -0
  86. lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/cache_api.py +1 -1
  87. lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/chunk_statistics_api.py +2 -2
  88. lmcache/inprocess/v1/lookup_client/__init__.py +27 -0
  89. lmcache/{v1 → inprocess/v1}/lookup_client/chunk_statistics_lookup_client.py +5 -5
  90. lmcache/{v1 → inprocess/v1}/lookup_client/factory.py +23 -17
  91. lmcache/{v1 → inprocess/v1}/lookup_client/hit_limit_lookup_client.py +1 -1
  92. lmcache/{v1 → inprocess/v1}/lookup_client/lmcache_async_lookup_client.py +12 -9
  93. lmcache/{v1 → inprocess/v1}/lookup_client/lmcache_lookup_client.py +6 -6
  94. lmcache/{v1 → inprocess/v1}/lookup_client/lmcache_lookup_client_bypass.py +2 -2
  95. lmcache/{v1 → inprocess/v1}/lookup_client/mooncake_lookup_client.py +5 -3
  96. lmcache/{v1 → inprocess/v1}/lookup_client/record_strategies/__init__.py +2 -2
  97. lmcache/{v1 → inprocess/v1}/lookup_client/record_strategies/base.py +1 -1
  98. lmcache/{v1 → inprocess/v1}/lookup_client/record_strategies/file_hash.py +1 -1
  99. lmcache/{v1 → inprocess/v1}/lookup_client/record_strategies/memory_bloom_filter.py +1 -1
  100. lmcache/{v1 → inprocess/v1}/manager.py +31 -38
  101. lmcache/inprocess/v1/offload_server/__init__.py +19 -0
  102. lmcache/{v1 → inprocess/v1}/offload_server/zmq_server.py +4 -4
  103. lmcache/inprocess/v1/protocol.py +148 -0
  104. lmcache/{v1 → inprocess/v1}/rpc/__init__.py +2 -2
  105. lmcache/{v1 → inprocess/v1}/rpc/zmq_transport.py +3 -3
  106. lmcache/inprocess/v1/rpc_utils.py +218 -0
  107. lmcache/{v1 → inprocess/v1}/transfer_channel/__init__.py +3 -3
  108. lmcache/{v1 → inprocess/v1}/transfer_channel/abstract.py +2 -2
  109. lmcache/{v1 → inprocess/v1}/transfer_channel/mock_memory_channel.py +1 -1
  110. lmcache/{v1 → inprocess/v1}/transfer_channel/nixl_channel.py +3 -3
  111. lmcache/{v1 → inprocess/v1}/transfer_channel/py_socket_channel.py +4 -4
  112. lmcache/integration/atom/multi_process_adapter.py +5 -4
  113. lmcache/integration/sglang/unified_kv_adapter.py +4 -6
  114. lmcache/integration/sglang/unified_lmcache_mp_connector.py +15 -3
  115. lmcache/integration/tensorrt_llm/__init__.py +2 -2
  116. lmcache/integration/tensorrt_llm/tensorrt_adapter.py +12 -372
  117. lmcache/integration/tensorrt_llm/tensorrt_mp_adapter.py +14 -5
  118. lmcache/integration/vllm/experimental/dispatcher.py +1 -1
  119. lmcache/integration/vllm/experimental/token_drop.py +66 -0
  120. lmcache/integration/vllm/kv_cache_group_edits.py +427 -28
  121. lmcache/integration/vllm/kv_cache_groups.py +8 -5
  122. lmcache/integration/vllm/lazy_offload_manager.py +19 -12
  123. lmcache/integration/vllm/lmcache_connector_v1.py +3 -1
  124. lmcache/integration/vllm/lmcache_mp_connector.py +171 -26
  125. lmcache/integration/vllm/lmcache_mp_metadata.py +106 -10
  126. lmcache/integration/vllm/mp_lookup_state.py +420 -0
  127. lmcache/integration/vllm/utils.py +5 -2
  128. lmcache/integration/vllm/vllm_multi_process_adapter.py +213 -276
  129. lmcache/lmcache_native.pyi +1 -0
  130. lmcache/observability.py +6 -4
  131. lmcache/sdk/context.py +10 -5
  132. lmcache/sdk/qringbuffer.py +3 -3
  133. lmcache/sdk/wrapper/contiguous.py +4 -3
  134. lmcache/security/__init__.py +2 -0
  135. lmcache/security/api_key.py +207 -0
  136. lmcache/security/http.py +99 -0
  137. lmcache/storage_backend/serde/cachegen_basics.py +6 -6
  138. lmcache/tools/cache_simulator/lru_cache.py +12 -4
  139. lmcache/tools/cache_simulator/trace_analyze.py +830 -0
  140. lmcache/usage_telemetry/messages.py +1 -2
  141. lmcache/usage_telemetry/metric_specs.py +6 -5
  142. lmcache/usage_telemetry/mp_continuous.py +12 -1
  143. lmcache/utils.py +93 -1
  144. lmcache/v1/compute/blend/utils.py +2 -2
  145. lmcache/v1/distributed/api.py +72 -0
  146. lmcache/v1/distributed/config.py +120 -30
  147. lmcache/v1/distributed/eviction_policy/noop.py +2 -2
  148. lmcache/v1/distributed/internal_api.py +25 -9
  149. lmcache/v1/distributed/l1_manager.py +57 -14
  150. lmcache/v1/distributed/l2_adapters/aerospike_l2_adapter.py +2 -0
  151. lmcache/v1/distributed/l2_adapters/base.py +30 -1
  152. lmcache/v1/distributed/l2_adapters/bigtable_l2_adapter.py +45 -20
  153. lmcache/v1/distributed/l2_adapters/config.py +22 -12
  154. lmcache/v1/{storage_backend → distributed/l2_adapters}/dax/core.py +17 -16
  155. lmcache/v1/distributed/l2_adapters/dax_l2_adapter.py +167 -38
  156. lmcache/v1/distributed/l2_adapters/factory.py +19 -7
  157. lmcache/v1/distributed/l2_adapters/fault_inject_l2_adapter.py +11 -1
  158. lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +14 -1
  159. lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +70 -1
  160. lmcache/v1/distributed/l2_adapters/hf3fs_l2_adapter.py +2 -0
  161. lmcache/v1/distributed/l2_adapters/hfbucket_client.py +193 -0
  162. lmcache/v1/distributed/l2_adapters/hfbucket_l2_adapter.py +7 -2
  163. lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +5 -0
  164. lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +4 -1
  165. lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +8 -1
  166. lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +4 -1
  167. lmcache/v1/distributed/l2_adapters/nixl_native_l2_adapter.py +21 -0
  168. lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +13 -4
  169. lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +30 -24
  170. lmcache/v1/distributed/l2_adapters/p2p_l2_adapter.py +11 -1
  171. lmcache/v1/{storage_backend → distributed/l2_adapters}/raw_block/__init__.py +2 -2
  172. lmcache/v1/{storage_backend → distributed/l2_adapters}/raw_block/core.py +11 -4
  173. lmcache/v1/distributed/l2_adapters/raw_block_l2_adapter.py +18 -4
  174. lmcache/v1/distributed/l2_adapters/reconfiguration.py +11 -6
  175. lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +4 -1
  176. lmcache/v1/distributed/l2_adapters/s3_l2_adapter.py +12 -1
  177. lmcache/v1/distributed/l2_adapters/sagemaker_hyperpod_l2_adapter.py +11 -1
  178. lmcache/v1/distributed/l2_adapters/serde_wrapper.py +13 -1
  179. lmcache/v1/{storage_backend → distributed/l2_adapters}/valkey/__init__.py +1 -1
  180. lmcache/v1/distributed/l2_adapters/valkey_l2_adapter.py +12 -2
  181. lmcache/v1/distributed/memory_manager/devdax_l1_memory_manager.py +36 -23
  182. lmcache/v1/distributed/memory_manager/l1_memory_manager.py +10 -2
  183. lmcache/v1/distributed/storage_controllers/prefetch_controller.py +141 -36
  184. lmcache/v1/distributed/storage_controllers/store_controller.py +32 -1
  185. lmcache/v1/distributed/storage_controllers/utils.py +44 -3
  186. lmcache/v1/distributed/storage_manager.py +129 -62
  187. lmcache/v1/distributed/transfer_channel/impl/mooncake_te_impl.py +15 -7
  188. lmcache/v1/gpu_connector/__init__.py +1 -261
  189. lmcache/v1/gpu_connector/kv_format/detectors/vllm.py +19 -0
  190. lmcache/v1/gpu_connector/kv_format/specs/base.py +77 -6
  191. lmcache/v1/gpu_connector/kv_format/specs/nl_x_nb_bsv_bss.py +1 -0
  192. lmcache/v1/gpu_connector/kv_format/specs/nl_x_nb_two_nh_one_bs_hs.py +66 -0
  193. lmcache/v1/gpu_connector/utils.py +3 -46
  194. lmcache/v1/kv_layer_groups.py +24 -1
  195. lmcache/v1/memory_allocators/devdax_memory_allocator.py +99 -42
  196. lmcache/v1/memory_allocators/lazy_memory_allocator.py +28 -2
  197. lmcache/v1/memory_management.py +1 -1
  198. lmcache/v1/memory_orchestrator/__init__.py +5 -0
  199. lmcache/v1/memory_orchestrator/__main__.py +8 -0
  200. lmcache/v1/memory_orchestrator/_codec.py +231 -0
  201. lmcache/v1/memory_orchestrator/_proto_gen/.gitignore +5 -0
  202. lmcache/v1/memory_orchestrator/_proto_gen/__init__.py +13 -0
  203. lmcache/v1/memory_orchestrator/_proto_gen/memory_orchestrator_pb2.pyi +221 -0
  204. lmcache/v1/memory_orchestrator/api.py +237 -0
  205. lmcache/v1/memory_orchestrator/client.py +410 -0
  206. lmcache/v1/memory_orchestrator/protos/memory_orchestrator.proto +145 -0
  207. lmcache/v1/memory_orchestrator/server.py +514 -0
  208. lmcache/v1/memory_orchestrator/service.py +248 -0
  209. lmcache/v1/memory_orchestrator/state.py +652 -0
  210. lmcache/v1/mp_coordinator/api.py +9 -0
  211. lmcache/v1/mp_coordinator/app.py +97 -44
  212. lmcache/v1/mp_coordinator/background_loops.py +148 -0
  213. lmcache/v1/mp_coordinator/cache_events.py +51 -4
  214. lmcache/v1/mp_coordinator/config.py +5 -4
  215. lmcache/v1/mp_coordinator/controllers/base.py +42 -5
  216. lmcache/v1/mp_coordinator/controllers/eviction_controller.py +9 -10
  217. lmcache/v1/mp_coordinator/controllers/eviction_http_api.py +117 -6
  218. lmcache/v1/mp_coordinator/http_apis/cache_api.py +3 -116
  219. lmcache/v1/mp_coordinator/http_apis/config_api.py +26 -0
  220. lmcache/v1/mp_coordinator/http_apis/dependencies.py +43 -1
  221. lmcache/v1/mp_coordinator/http_apis/instances_api.py +7 -1
  222. lmcache/v1/mp_coordinator/ingest/event_broadcaster.py +52 -7
  223. lmcache/v1/mp_coordinator/ingest/event_gate.py +208 -47
  224. lmcache/v1/mp_coordinator/ingest/kafka_event_source.py +76 -1
  225. lmcache/v1/mp_coordinator/metric_sources.py +56 -0
  226. lmcache/v1/mp_coordinator/observability.py +399 -64
  227. lmcache/v1/mp_coordinator/registrar.py +34 -1
  228. lmcache/v1/mp_coordinator/schemas.py +14 -0
  229. lmcache/v1/mp_coordinator/views/instance_registry.py +25 -0
  230. lmcache/v1/mp_coordinator/views/key_directory.py +39 -2
  231. lmcache/v1/mp_observability/README.md +1 -1
  232. lmcache/v1/mp_observability/config.py +32 -3
  233. lmcache/v1/mp_observability/otel_init.py +12 -0
  234. lmcache/v1/mp_observability/subscribers/metrics/lookup.py +30 -1
  235. lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +61 -2
  236. lmcache/v1/mp_observability/trace/codecs.py +36 -0
  237. lmcache/v1/mp_observability/trace/format.py +4 -1
  238. lmcache/v1/multiprocess/cache_control/key_resolver.py +2 -2
  239. lmcache/v1/multiprocess/config.py +63 -29
  240. lmcache/v1/multiprocess/custom_types.py +103 -11
  241. lmcache/v1/multiprocess/engine_context.py +130 -2
  242. lmcache/v1/multiprocess/engine_module.py +210 -2
  243. lmcache/v1/multiprocess/ext_server_module.py +116 -229
  244. lmcache/v1/multiprocess/futures.py +55 -4
  245. lmcache/v1/multiprocess/http_apis/common/__init__.py +1 -0
  246. lmcache/v1/{internal_api_server → multiprocess/http_apis}/common/env_api.py +12 -1
  247. lmcache/v1/multiprocess/http_apis/common_api.py +14 -16
  248. lmcache/v1/multiprocess/http_apis/config_api.py +0 -9
  249. lmcache/v1/multiprocess/http_apis/info_api.py +17 -7
  250. lmcache/v1/multiprocess/http_apis/l1_reconfigure_api.py +30 -8
  251. lmcache/v1/multiprocess/http_apis/reconfigure_api.py +25 -0
  252. lmcache/v1/multiprocess/http_apis/reconfigure_schemas.py +30 -5
  253. lmcache/v1/multiprocess/http_auth.py +86 -0
  254. lmcache/v1/multiprocess/http_server.py +13 -0
  255. lmcache/v1/multiprocess/kv_load_policy.py +145 -0
  256. lmcache/v1/multiprocess/modules/blend/lookup.py +15 -9
  257. lmcache/v1/multiprocess/modules/blend/module.py +64 -8
  258. lmcache/v1/multiprocess/modules/blend/store.py +6 -3
  259. lmcache/v1/multiprocess/modules/engine_driven_transfer.py +167 -13
  260. lmcache/v1/multiprocess/modules/kv_events.py +29 -2
  261. lmcache/v1/multiprocess/modules/lmcache_driven_transfer.py +50 -14
  262. lmcache/v1/multiprocess/modules/lookup.py +246 -77
  263. lmcache/v1/multiprocess/modules/management.py +50 -9
  264. lmcache/v1/multiprocess/modules/p2p_controller.py +81 -6
  265. lmcache/v1/multiprocess/modules/server_transfer.py +306 -115
  266. lmcache/v1/multiprocess/object_group_transfer.py +5 -4
  267. lmcache/v1/multiprocess/plugin_modules/__init__.py +18 -0
  268. lmcache/v1/multiprocess/{modules/experimental → plugin_modules}/qstore.py +51 -4
  269. lmcache/v1/multiprocess/request_handler.py +2 -2
  270. lmcache/v1/multiprocess/server.py +110 -211
  271. lmcache/v1/multiprocess/session.py +23 -15
  272. lmcache/v1/multiprocess/token_codec.py +90 -0
  273. lmcache/v1/multiprocess/token_hasher.py +96 -9
  274. lmcache/v1/multiprocess/transfer_context/__init__.py +2 -2
  275. lmcache/v1/multiprocess/transfer_context/async_engine_driven.py +128 -3
  276. lmcache/v1/multiprocess/transfer_context/base.py +157 -212
  277. lmcache/v1/multiprocess/transfer_context/inline.py +155 -0
  278. lmcache/v1/multiprocess/transfer_context/shm.py +87 -1
  279. lmcache/v1/multiprocess/transfer_context/tensor_wire.py +316 -0
  280. lmcache/v1/multiprocess/transfer_context/worker_transfer.py +595 -29
  281. lmcache/v1/multiprocess/transport/auth.py +50 -0
  282. lmcache/v1/multiprocess/transport/base.py +18 -2
  283. lmcache/v1/multiprocess/transport/grpc_impl/_proto_gen/_generate.py +64 -25
  284. lmcache/v1/multiprocess/transport/grpc_impl/_proto_gen/common_pb2.pyi +10 -10
  285. lmcache/v1/multiprocess/transport/grpc_impl/aio_server.py +265 -0
  286. lmcache/v1/multiprocess/transport/grpc_impl/client.py +9 -0
  287. lmcache/v1/multiprocess/transport/grpc_impl/codecs/common.py +2 -2
  288. lmcache/v1/multiprocess/transport/grpc_impl/native_server.py +417 -0
  289. lmcache/v1/multiprocess/transport/grpc_impl/proto_codec.py +68 -23
  290. lmcache/v1/multiprocess/transport/grpc_impl/protos/common.proto +3 -2
  291. lmcache/v1/multiprocess/transport/grpc_impl/protos/engine_driven_service.proto +22 -0
  292. lmcache/v1/multiprocess/transport/grpc_impl/protos/extension_service.proto +31 -0
  293. lmcache/v1/multiprocess/transport/grpc_impl/protos/{observability_service.proto → kv_event_service.proto} +2 -18
  294. lmcache/v1/multiprocess/transport/grpc_impl/protos/lookup_service.proto +7 -0
  295. lmcache/v1/multiprocess/transport/grpc_impl/protos/management_service.proto +78 -0
  296. lmcache/v1/multiprocess/transport/grpc_impl/server.py +48 -192
  297. lmcache/v1/multiprocess/transport/grpc_impl/server_base.py +126 -0
  298. lmcache/v1/multiprocess/transport/grpc_impl/server_builder.py +49 -0
  299. lmcache/v1/multiprocess/transport/grpc_impl/server_handlers.py +127 -0
  300. lmcache/v1/multiprocess/transport/server_factory.py +3 -17
  301. lmcache/v1/multiprocess/transport/zmq_impl/mq.py +66 -54
  302. lmcache/v1/multiprocess/transport/zmq_impl/server.py +4 -12
  303. lmcache/v1/periodic_thread.py +45 -35
  304. lmcache/v1/platform/base/cache_context.py +43 -0
  305. lmcache/v1/platform/base/device_ops.py +0 -6
  306. lmcache/v1/platform/devices/cuda/cache_context.py +25 -1
  307. lmcache/v1/platform/devices/cuda/ipc_wrapper.py +26 -76
  308. lmcache/v1/platform/devices/cuda/timeline_semaphore_event_ipc.py +5 -0
  309. lmcache/v1/platform/devices/cuda/utils.py +45 -0
  310. lmcache/v1/platform/devices/mlu/__init__.py +76 -0
  311. lmcache/v1/platform/devices/mlu/device_ops.py +277 -0
  312. lmcache/v1/platform/devices/musa/cache_context.py +9 -4
  313. lmcache/v1/platform/devices/musa/device_ops.py +35 -33
  314. lmcache/v1/platform/devices/musa/format_capabilities.py +93 -0
  315. lmcache/v1/platform/devices/musa/native_kv_transfer.py +9 -25
  316. lmcache/v1/platform/devices/neuron/__init__.py +152 -11
  317. lmcache/v1/platform/devices/neuron/device_ops.py +145 -0
  318. lmcache/v1/platform/devices/neuron/kv_ops.py +135 -0
  319. lmcache/v1/platform/devices/rbln/device_ops.py +73 -55
  320. lmcache/v1/platform/devices/rbln/kv_layout.py +56 -18
  321. lmcache/v1/platform/devices/rbln/kv_ops.py +145 -46
  322. lmcache/v1/platform/devices/supa/__init__.py +101 -0
  323. lmcache/v1/platform/devices/supa/device_ops.py +21 -0
  324. lmcache/v1/platform/native_block_transfer.py +159 -0
  325. lmcache/v1/platform/torch_ops/_kv_format.py +37 -1
  326. lmcache/v1/platform/torch_ops/mem_kernels.py +0 -158
  327. lmcache/v1/platform/torch_ops/mp_mem_kernels.py +24 -1
  328. lmcache/v1/protocol.py +3 -291
  329. lmcache/v1/rpc_utils.py +4 -212
  330. lmcache/v1/storage_backend/__init__.py +1 -1
  331. lmcache/v1/storage_backend/batched_message_sender.py +4 -4
  332. lmcache/v1/storage_backend/connector/__init__.py +11 -10
  333. lmcache/v1/storage_backend/connector/base_connector.py +5 -2
  334. lmcache/v1/storage_backend/connector/bigtable_connector.py +4 -2
  335. lmcache/v1/storage_backend/connector/eic_connector.py +1 -1
  336. lmcache/v1/storage_backend/connector/external_adapter.py +2 -2
  337. lmcache/v1/storage_backend/connector/fs_connector.py +1 -1
  338. lmcache/v1/storage_backend/connector/hf3fs_adapter.py +37 -23
  339. lmcache/v1/storage_backend/connector/hf3fs_connector.py +1 -1
  340. lmcache/v1/storage_backend/connector/hfbucket_connector.py +9 -199
  341. lmcache/v1/storage_backend/connector/infinistore_connector.py +2 -3
  342. lmcache/v1/storage_backend/connector/mooncakestore_connector.py +42 -32
  343. lmcache/v1/storage_backend/connector/redis_connector.py +1 -1
  344. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +1 -1
  345. lmcache/v1/storage_backend/connector/valkey_connector.py +6 -6
  346. lmcache/v1/storage_backend/local_cpu_backend.py +9 -4
  347. lmcache/v1/storage_backend/local_disk_backend.py +2 -2
  348. lmcache/v1/storage_backend/nixl_storage_backend.py +24 -17
  349. lmcache/v1/storage_backend/p2p_backend.py +16 -16
  350. lmcache/v1/storage_backend/pd_backend.py +3 -3
  351. lmcache/v1/storage_backend/pd_backend_async.py +9 -3
  352. lmcache/v1/storage_backend/plugins/dax_backend.py +9 -1
  353. lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +5 -5
  354. lmcache/v1/storage_backend/storage_manager.py +27 -25
  355. lmcache/v1/utils/subclass_discovery.py +55 -24
  356. {lmcache_cli-0.5.7.dev0.dist-info → lmcache_cli-0.5.7rc3.dev0.dist-info}/METADATA +4 -2
  357. {lmcache_cli-0.5.7.dev0.dist-info → lmcache_cli-0.5.7rc3.dev0.dist-info}/RECORD +414 -366
  358. {lmcache_cli-0.5.7.dev0.dist-info → lmcache_cli-0.5.7rc3.dev0.dist-info}/WHEEL +1 -1
  359. lmcache/integration/sglang/multi_process_adapter.py +0 -705
  360. lmcache/integration/sglang/sglang_adapter.py +0 -421
  361. lmcache/integration/sglang/utils.py +0 -31
  362. lmcache/v1/cache_controller/__init__.py +0 -9
  363. lmcache/v1/cache_controller/controllers/__init__.py +0 -11
  364. lmcache/v1/internal_api_server/vllm/version_api.py +0 -25
  365. lmcache/v1/lookup_client/__init__.py +0 -25
  366. lmcache/v1/multiprocess/modules/experimental/__init__.py +0 -10
  367. lmcache/v1/multiprocess/transfer_context/pickle.py +0 -93
  368. lmcache/v1/multiprocess/transport/grpc_impl/protos/controller_service.proto +0 -52
  369. lmcache/v1/multiprocess/transport/grpc_impl/protos/debug_service.proto +0 -17
  370. lmcache/v1/offload_server/__init__.py +0 -17
  371. lmcache/v1/server/__init__.py +0 -2
  372. lmcache/v1/server/__main__.py +0 -174
  373. lmcache/v1/server/storage_backend/__init__.py +0 -21
  374. lmcache/v1/server/storage_backend/abstract_backend.py +0 -80
  375. lmcache/v1/server/storage_backend/local_backend.py +0 -75
  376. lmcache/v1/server/utils.py +0 -21
  377. lmcache/v1/storage_backend/connector/lm_adapter.py +0 -31
  378. lmcache/v1/storage_backend/connector/lm_connector.py +0 -176
  379. /lmcache/{tools/controller_benchmark → inprocess/integration/tensorrt_llm}/__init__.py +0 -0
  380. /lmcache/{integration → inprocess/integration}/tensorrt_llm/utils.py +0 -0
  381. /lmcache/{v1/health_monitor → inprocess/standalone}/__init__.py +0 -0
  382. /lmcache/{v1/health_monitor/checks → inprocess/tools}/__init__.py +0 -0
  383. /lmcache/{v1/internal_api_server/common → inprocess/tools/controller_benchmark}/__init__.py +0 -0
  384. /lmcache/{tools → inprocess/tools}/controller_benchmark/config.py +0 -0
  385. /lmcache/{tools → inprocess/tools}/controller_benchmark/constants.py +0 -0
  386. /lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/__init__.py +0 -0
  387. /lmcache/{tools → inprocess/tools}/controller_benchmark/handlers/base.py +0 -0
  388. /lmcache/{v1/internal_api_server/controller → inprocess/v1}/__init__.py +0 -0
  389. /lmcache/{v1 → inprocess/v1}/api_server/__init__.py +0 -0
  390. /lmcache/{v1 → inprocess/v1}/cache_controller/config.py +0 -0
  391. /lmcache/{v1 → inprocess/v1}/cache_controller/frontend/static/css/style.css +0 -0
  392. /lmcache/{v1 → inprocess/v1}/cache_controller/frontend/static/img/logo.png +0 -0
  393. /lmcache/{v1 → inprocess/v1}/cache_controller/frontend/static/index.html +0 -0
  394. /lmcache/{v1 → inprocess/v1}/cache_controller/frontend/static/js/controller_app.js +0 -0
  395. /lmcache/{v1 → inprocess/v1}/cache_controller/locks.py +0 -0
  396. /lmcache/{v1 → inprocess/v1}/cache_controller/observability.py +0 -0
  397. /lmcache/{v1 → inprocess/v1}/cache_interface.py +0 -0
  398. /lmcache/{v1 → inprocess/v1}/event_manager.py +0 -0
  399. /lmcache/{v1/internal_api_server/vllm → inprocess/v1/health_monitor}/__init__.py +0 -0
  400. /lmcache/{v1/standalone → inprocess/v1/health_monitor/checks}/__init__.py +0 -0
  401. /lmcache/{v1 → inprocess/v1}/health_monitor/constants.py +0 -0
  402. /lmcache/{v1 → inprocess/v1}/internal_api_server/__init__.py +0 -0
  403. /lmcache/{v1 → inprocess/v1}/internal_api_server/controller/key_stats_api.py +0 -0
  404. /lmcache/{v1 → inprocess/v1}/internal_api_server/controller/worker_info_api.py +0 -0
  405. /lmcache/{v1 → inprocess/v1}/internal_api_server/utils.py +0 -0
  406. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/backend_api.py +0 -0
  407. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/bypass_api.py +0 -0
  408. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/conf_api.py +0 -0
  409. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/freeze_api.py +0 -0
  410. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/hot_cache_api.py +0 -0
  411. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/inference_api.py +0 -0
  412. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/load_fs_chunks_api.py +0 -0
  413. /lmcache/{v1 → inprocess/v1}/internal_api_server/vllm/lookup_api.py +0 -0
  414. /lmcache/{v1 → inprocess/v1}/lookup_client/abstract_client.py +0 -0
  415. /lmcache/{v1 → inprocess/v1}/lookup_client/async_lookup_message.py +0 -0
  416. /lmcache/{v1 → inprocess/v1}/offload_server/abstract_server.py +0 -0
  417. /lmcache/{v1 → inprocess/v1}/offload_server/message.py +0 -0
  418. /lmcache/{v1 → inprocess/v1}/pin_monitor.py +0 -0
  419. /lmcache/{v1 → inprocess/v1}/rpc/transport.py +0 -0
  420. /lmcache/{v1 → inprocess/v1}/token_database.py +0 -0
  421. /lmcache/{v1 → inprocess/v1}/transfer_channel/transfer_utils.py +0 -0
  422. /lmcache/v1/{storage_backend/connector → distributed/l2_adapters}/bigtable_config.py +0 -0
  423. /lmcache/v1/{storage_backend/connector → distributed/l2_adapters}/bigtable_sharder.py +0 -0
  424. /lmcache/v1/{storage_backend → distributed/l2_adapters}/dax/__init__.py +0 -0
  425. /lmcache/v1/{storage_backend → distributed/l2_adapters}/raw_block/key_codec.py +0 -0
  426. /lmcache/v1/{storage_backend → distributed/l2_adapters}/valkey/worker_pool.py +0 -0
  427. /lmcache/v1/{internal_api_server → multiprocess/http_apis}/common/loglevel_api.py +0 -0
  428. /lmcache/v1/{internal_api_server → multiprocess/http_apis}/common/metrics_api.py +0 -0
  429. /lmcache/v1/{internal_api_server → multiprocess/http_apis}/common/periodic_thread_api.py +0 -0
  430. /lmcache/v1/{internal_api_server → multiprocess/http_apis}/common/run_script_api.py +0 -0
  431. /lmcache/v1/{internal_api_server → multiprocess/http_apis}/common/thread_api.py +0 -0
  432. {lmcache_cli-0.5.7.dev0.dist-info → lmcache_cli-0.5.7rc3.dev0.dist-info}/entry_points.txt +0 -0
  433. {lmcache_cli-0.5.7.dev0.dist-info → lmcache_cli-0.5.7rc3.dev0.dist-info}/licenses/LICENSE +0 -0
  434. {lmcache_cli-0.5.7.dev0.dist-info → lmcache_cli-0.5.7rc3.dev0.dist-info}/top_level.txt +0 -0
lmcache/_version.py CHANGED
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.5.7.dev0'
22
- __version_tuple__ = version_tuple = (0, 5, 7, 'dev0')
21
+ __version__ = version = '0.5.7rc3.dev0'
22
+ __version_tuple__ = version_tuple = (0, 5, 7, 'rc3', 'dev0')
23
23
 
24
- __commit_id__ = commit_id = 'g3e11e1081'
24
+ __commit_id__ = commit_id = 'gbf85b41fa'
@@ -107,6 +107,7 @@ def add_engine_arguments(parser: argparse.ArgumentParser) -> None:
107
107
  "--workload",
108
108
  default=None,
109
109
  choices=[
110
+ "kv-tier-pressure",
110
111
  "long-doc-permutator",
111
112
  "long-doc-qa",
112
113
  "multi-round-chat",
@@ -234,6 +235,83 @@ def add_engine_arguments(parser: argparse.ArgumentParser) -> None:
234
235
  "--ignore-eos for a reproducible decode phase.",
235
236
  )
236
237
 
238
+ # --- KV-tier-pressure workload args ---
239
+ ktp_group = parser.add_argument_group("kv-tier-pressure workload options")
240
+ ktp_group.add_argument(
241
+ "--ktp-overflow-factor",
242
+ type=float,
243
+ default=2.0,
244
+ help="Working set as a multiple of the LMCache server's L1 capacity "
245
+ "(default: 2.0). Used only when --ktp-pool-size is omitted: the pool "
246
+ "is then sized from the capacity reported by --lmcache-url, so you "
247
+ "do not have to convert GB to tokens to documents yourself. Above "
248
+ "1.0 forces eviction to the storage tier.",
249
+ )
250
+ ktp_group.add_argument(
251
+ "--ktp-pool-size",
252
+ type=int,
253
+ default=None,
254
+ help="Total documents in the corpus. Derived from the server's L1 "
255
+ "capacity and --ktp-overflow-factor when omitted. "
256
+ "This sets the working "
257
+ "set -- pool_size x --ktp-context-length tokens -- independently of "
258
+ "how large one prompt is, and only what exceeds L1 can reach the "
259
+ "storage tier. To overflow a cache of V GB by a factor F: "
260
+ "ceil(F * V * tokens_per_gb_kvcache / context_length).",
261
+ )
262
+ ktp_group.add_argument(
263
+ "--ktp-docs-per-request",
264
+ type=int,
265
+ default=16,
266
+ help="Documents sampled into each request (default: 16). Bounded by "
267
+ "the engine's context limit, not by the pool size.",
268
+ )
269
+ ktp_group.add_argument(
270
+ "--ktp-context-length",
271
+ type=int,
272
+ default=2560,
273
+ help="Exact token length of each document (default: 2560, which is "
274
+ "10 whole 256-token chunks). Requires a loadable tokenizer; pass "
275
+ "--model when the engine reports a name that is not a HuggingFace "
276
+ "repo ID or local path.",
277
+ )
278
+ ktp_group.add_argument(
279
+ "--ktp-system-prompt-length",
280
+ type=int,
281
+ default=256,
282
+ help="Exact token length of the shared system prompt (default: 256). "
283
+ "Use 0 for no system prompt.",
284
+ )
285
+ ktp_group.add_argument(
286
+ "--ktp-num-requests",
287
+ type=int,
288
+ default=200,
289
+ help="Number of measured requests (default: 200). The warm-up sweep "
290
+ "is separate and sized automatically from the pool.",
291
+ )
292
+ ktp_group.add_argument(
293
+ "--ktp-access-skew",
294
+ type=float,
295
+ default=0.0,
296
+ help="Zipf exponent for document popularity (default: 0.0 = uniform). "
297
+ "Larger values concentrate reads on a hot subset, raising the L1 hit "
298
+ "rate and lowering the L2 share.",
299
+ )
300
+ ktp_group.add_argument(
301
+ "--ktp-num-inflight-requests",
302
+ type=int,
303
+ default=8,
304
+ help="Max concurrent in-flight requests (default: 8).",
305
+ )
306
+ ktp_group.add_argument(
307
+ "--ktp-max-output-length",
308
+ type=int,
309
+ default=1,
310
+ help="Max tokens to generate per request (default: 1). The default "
311
+ "isolates prefill, which is the phase the cache tier affects; larger "
312
+ "values add decode time that dilutes the measurement.",
313
+ )
314
+
237
315
  # --- Long-doc-qa workload args ---
238
316
  group = parser.add_argument_group("long-doc-qa workload options")
239
317
  group.add_argument(
@@ -472,6 +550,20 @@ def _resolve_args(args: argparse.Namespace) -> argparse.Namespace:
472
550
  cli_val = getattr(args, attr, None)
473
551
  if cli_val is not None:
474
552
  setattr(resolved, attr, cli_val)
553
+ # A config file can omit a workload-specific required value -- it is
554
+ # only written when the flag was supplied. Catch it here rather than
555
+ # letting None reach the workload and surface as a TypeError.
556
+ missing = [
557
+ flag
558
+ for attr, flag in _REQUIRED_WORKLOAD_ARGS.get(resolved.workload, ())
559
+ if getattr(resolved, attr, None) is None
560
+ ]
561
+ if missing:
562
+ raise SystemExit(
563
+ f"Config {config_path} is missing required arguments for the "
564
+ f"{resolved.workload!r} workload: " + ", ".join(missing) + ". "
565
+ "Add them to the config, or pass them on the command line."
566
+ )
475
567
  return resolved
476
568
 
477
569
  # Case 2: --no-interactive or --export-config — error if missing
@@ -177,6 +177,69 @@ def _find_model_meta(
177
177
  )
178
178
 
179
179
 
180
+ def server_runs_blend_module(lmcache_url: str) -> bool:
181
+ """Report whether the LMCache server loaded the blend module.
182
+
183
+ Detected by the presence of ``active_cb_lookups`` in ``/status``, which
184
+ only :mod:`lmcache.v1.multiprocess.modules.blend` emits. Two fields that
185
+ look like they would answer this do not: ``engine_type`` is
186
+ ``self.__class__.__name__`` and reads ``MPCacheServer`` for both engines,
187
+ and nothing in the codebase writes ``cb_gpu_context_meta``. Tracked
188
+ upstream in #5589; switch to the reported engine once that lands.
189
+
190
+ This is a necessary but not sufficient condition for blended reuse -- it
191
+ says the server can blend, not that vLLM is driving it through
192
+ ``CBKVConnector``. Confirm the end-to-end path by checking which lookup
193
+ counter family moves once traffic has run.
194
+
195
+ Args:
196
+ lmcache_url: URL of the LMCache HTTP server.
197
+
198
+ Returns:
199
+ True when the server reports the blend module.
200
+
201
+ Raises:
202
+ RuntimeError: If the server is unreachable.
203
+ """
204
+ return "active_cb_lookups" in _fetch_lmcache_status(lmcache_url)
205
+
206
+
207
+ def resolve_l1_capacity_gb(lmcache_url: str) -> float:
208
+ """Query the LMCache server for the configured host-memory capacity.
209
+
210
+ Reads ``storage_manager.l1_manager.memory_total_bytes`` from ``/status``.
211
+ Workloads that size themselves against the cache need the real capacity,
212
+ not a figure the user retyped; a mistyped value silently produces a
213
+ working set that never exceeds L1, and a run that does no L2 I/O at all.
214
+
215
+ Args:
216
+ lmcache_url: URL of the LMCache HTTP server.
217
+
218
+ Returns:
219
+ Configured L1 capacity in GB.
220
+
221
+ Raises:
222
+ RuntimeError: If the server is unreachable, or ``/status`` does not
223
+ report an L1 capacity.
224
+ """
225
+ data = _fetch_lmcache_status(lmcache_url)
226
+ total = (
227
+ data.get("storage_manager", {}).get("l1_manager", {}).get("memory_total_bytes")
228
+ )
229
+ if total is None:
230
+ # Older servers report it directly on storage_manager.
231
+ total = data.get("storage_manager", {}).get("memory_total_bytes")
232
+ if not isinstance(total, (int, float)) or total <= 0:
233
+ raise RuntimeError(
234
+ f"LMCache at {lmcache_url} did not report an L1 capacity "
235
+ f"(storage_manager.l1_manager.memory_total_bytes). Pass an "
236
+ f"explicit pool size instead."
237
+ )
238
+ capacity_gb = float(total) / (1024**3)
239
+ logger.debug("Resolved L1 capacity %.2f GB from %s", capacity_gb, lmcache_url)
240
+ return capacity_gb
241
+
242
+
180
243
  def resolve_tokens_per_gb(lmcache_url: str, model_name: str) -> int:
181
244
  """Query the LMCache server and compute tokens per GB of KV cache.
182
245
 
@@ -112,6 +112,11 @@ ALL_ITEMS: list[ConfigItem] = [
112
112
  default=None,
113
113
  required=True,
114
114
  choices=[
115
+ (
116
+ "kv-tier-pressure",
117
+ "Retrieval over a corpus too large to cache, exercising the "
118
+ "storage tier",
119
+ ),
115
120
  (
116
121
  "long-doc-permutator",
117
122
  "Query the same set of long documents with different orders",
@@ -234,6 +239,103 @@ ALL_ITEMS: list[ConfigItem] = [
234
239
  default=False,
235
240
  phase=PHASE_GENERAL,
236
241
  ),
242
+ # ── Phase 3: kv-tier-pressure ────────────────────────────────────
243
+ ConfigItem(
244
+ key="ktp_pool_size",
245
+ display_name="Document pool size",
246
+ description=(
247
+ "Total documents in the corpus. Sets the working set "
248
+ "(pool size x document length), and therefore whether the "
249
+ "storage tier is reached. Leave empty to size it from the "
250
+ "LMCache server's L1 capacity and the overflow factor."
251
+ ),
252
+ input_type="int",
253
+ default=None,
254
+ condition=_workload_is("kv-tier-pressure"),
255
+ phase=PHASE_WORKLOAD,
256
+ ),
257
+ ConfigItem(
258
+ key="ktp_overflow_factor",
259
+ display_name="Overflow factor",
260
+ description=(
261
+ "Working set as a multiple of the server's L1 capacity. Used "
262
+ "only when no pool size is given. Above 1.0 forces eviction to "
263
+ "the storage tier."
264
+ ),
265
+ input_type="float",
266
+ default=2.0,
267
+ condition=_workload_is("kv-tier-pressure"),
268
+ phase=PHASE_WORKLOAD,
269
+ ),
270
+ ConfigItem(
271
+ key="ktp_docs_per_request",
272
+ display_name="Documents per request",
273
+ description="Documents sampled into each request from the pool.",
274
+ input_type="int",
275
+ default=16,
276
+ condition=_workload_is("kv-tier-pressure"),
277
+ phase=PHASE_WORKLOAD,
278
+ ),
279
+ ConfigItem(
280
+ key="ktp_context_length",
281
+ display_name="Document length (tokens)",
282
+ description="Token length of each document in the pool.",
283
+ input_type="int",
284
+ default=2560,
285
+ condition=_workload_is("kv-tier-pressure"),
286
+ phase=PHASE_WORKLOAD,
287
+ ),
288
+ ConfigItem(
289
+ key="ktp_system_prompt_length",
290
+ display_name="System prompt length (tokens)",
291
+ description="Token length of the shared system prompt. Use 0 for none.",
292
+ input_type="int",
293
+ default=256,
294
+ condition=_workload_is("kv-tier-pressure"),
295
+ phase=PHASE_WORKLOAD,
296
+ ),
297
+ ConfigItem(
298
+ key="ktp_num_requests",
299
+ display_name="Measured requests",
300
+ description=(
301
+ "Requests to measure. The warm-up sweep is separate and sized "
302
+ "automatically from the pool."
303
+ ),
304
+ input_type="int",
305
+ default=200,
306
+ condition=_workload_is("kv-tier-pressure"),
307
+ phase=PHASE_WORKLOAD,
308
+ ),
309
+ ConfigItem(
310
+ key="ktp_access_skew",
311
+ display_name="Access skew",
312
+ description=(
313
+ "Zipf exponent for document popularity. 0 samples uniformly; "
314
+ "larger values concentrate reads on a hot subset."
315
+ ),
316
+ input_type="float",
317
+ default=0.0,
318
+ condition=_workload_is("kv-tier-pressure"),
319
+ phase=PHASE_WORKLOAD,
320
+ ),
321
+ ConfigItem(
322
+ key="ktp_num_inflight_requests",
323
+ display_name="Max inflight requests",
324
+ description="Maximum concurrent in-flight requests.",
325
+ input_type="int",
326
+ default=8,
327
+ condition=_workload_is("kv-tier-pressure"),
328
+ phase=PHASE_WORKLOAD,
329
+ ),
330
+ ConfigItem(
331
+ key="ktp_max_output_length",
332
+ display_name="Max output length (tokens)",
333
+ description="Max tokens per request. Use 1 to measure prefill alone.",
334
+ input_type="int",
335
+ default=1,
336
+ condition=_workload_is("kv-tier-pressure"),
337
+ phase=PHASE_WORKLOAD,
338
+ ),
237
339
  # ── Phase 3: long-doc-permutator ─────────────────────────────────
238
340
  ConfigItem(
239
341
  key="ldp_num_contexts",
@@ -219,10 +219,12 @@ def _same_number(
219
219
  def answer_match(prediction: str, references: list[str]) -> bool:
220
220
  """Whether *prediction* states one of the gold *references*.
221
221
 
222
- A reference matches when, after :func:`normalize_answer`, it equals the
223
- prediction or appears in it as a whole-word phrase ("Paris" in "the
224
- capital is Paris"). A reference containing numbers also matches when
225
- every one of its numbers appears in the prediction, up to 0.5% rounding.
222
+ A reference without numeric figures matches when, after
223
+ :func:`normalize_answer`, it equals the prediction or appears in it as
224
+ a whole-word phrase ("Paris" in "the capital is Paris"). References with
225
+ numeric figures require every figure to appear in the prediction, up
226
+ to 0.5% rounding. They cannot match by a punctuation-stripped phrase:
227
+ removing a decimal point would equate different values ("1.5", "15").
226
228
  When either side names a scale ("million", "billion", "5B", …), a rescale
227
229
  by a power of 1000 is also accepted, so "$11588.00" (in millions) matches
228
230
  "$11,588 million" and "$11.588 billion", but not "$3,033 million"; and a
@@ -265,9 +267,9 @@ def answer_match(prediction: str, references: list[str]) -> bool:
265
267
  gold = _phrase(reference)
266
268
  if abstains and not (_NEGATIVE.search(gold) or is_abstention(reference)):
267
269
  continue
268
- if gold and (gold == normalized or f" {gold} " in padded):
269
- return True
270
270
  gold_numbers = _numbers(reference)
271
+ if not gold_numbers and gold and (gold == normalized or f" {gold} " in padded):
272
+ return True
271
273
  scales = (
272
274
  _UNIT_SCALES
273
275
  if _SCALE_WORD.search(prediction) or _SCALE_WORD.search(reference)
@@ -36,9 +36,18 @@ def _normalize_url(engine_url: str) -> str:
36
36
 
37
37
 
38
38
  def _extract_content(chunk: object, completions_mode: bool) -> str:
39
- """Return text content from a streaming chunk, or ``""`` if none.
39
+ """Return non-empty text from a completion or chat streaming chunk.
40
40
 
41
- Ported from Tensormesh-Benchmark ``streaming_utils.py``.
41
+ Chat chunks fall back to ``reasoning_content`` and then ``reasoning``
42
+ when ``content`` is empty. Ported from Tensormesh-Benchmark
43
+ ``streaming_utils.py``.
44
+
45
+ Args:
46
+ chunk: An OpenAI-compatible streaming response chunk.
47
+ completions_mode: Whether the chunk uses the legacy completions API.
48
+
49
+ Returns:
50
+ The chunk's text, or an empty string when it contains no text.
42
51
  """
43
52
  choices = getattr(chunk, "choices", None)
44
53
  if not choices:
@@ -55,12 +64,12 @@ def _extract_content(chunk: object, completions_mode: bool) -> str:
55
64
  if delta is None:
56
65
  return ""
57
66
  content = getattr(delta, "content", None)
58
- if content is not None:
67
+ if content:
59
68
  return content
60
69
  # Fallback for reasoning models
61
70
  for attr in ("reasoning_content", "reasoning"):
62
71
  fallback = getattr(delta, attr, None)
63
- if fallback is not None:
72
+ if fallback:
64
73
  return fallback
65
74
  return ""
66
75
 
@@ -138,10 +147,20 @@ class RequestSender:
138
147
  ) -> RequestResult:
139
148
  """Send a single streaming request and return the result.
140
149
 
141
- Streams the response via SSE, measures TTFT, decode speed, and
142
- total latency. Extracts token counts from server usage reports.
150
+ Streams the response via SSE, measures TTFT from the first non-empty
151
+ content or reasoning text, decode speed, and total latency. Extracts
152
+ token counts from server usage reports.
143
153
  After collecting the result, invokes all registered
144
154
  ``on_finished`` callbacks.
155
+
156
+ Args:
157
+ request_id: Identifier included in the result and callbacks.
158
+ messages: Chat messages, or the first message's content as the
159
+ prompt in completions mode.
160
+ max_tokens: Maximum number of output tokens requested.
161
+
162
+ Returns:
163
+ Token counts, timings, and success status for this request.
145
164
  """
146
165
  submit_time = time.time()
147
166
  first_token_time = 0.0
@@ -243,6 +243,7 @@ class StatsCollector:
243
243
  "config": asdict(config),
244
244
  "results": asdict(final),
245
245
  }
246
+ Path(path).parent.mkdir(parents=True, exist_ok=True)
246
247
  with open(path, "w") as f:
247
248
  json.dump(output, f, indent=2)
248
249
  logger.debug("Exported JSON summary to: %s", path)
@@ -12,13 +12,21 @@ import argparse
12
12
  import os
13
13
 
14
14
  # First Party
15
- from lmcache.cli.commands.bench.engine_bench.config import EngineBenchConfig
15
+ from lmcache.cli.commands.bench.engine_bench.config import (
16
+ EngineBenchConfig,
17
+ resolve_l1_capacity_gb,
18
+ server_runs_blend_module,
19
+ )
16
20
  from lmcache.cli.commands.bench.engine_bench.progress import ProgressMonitor
17
21
  from lmcache.cli.commands.bench.engine_bench.request_sender import (
18
22
  RequestSender,
19
23
  )
20
24
  from lmcache.cli.commands.bench.engine_bench.stats import StatsCollector
21
25
  from lmcache.cli.commands.bench.engine_bench.workloads.base import BaseWorkload
26
+ from lmcache.cli.commands.bench.engine_bench.workloads.kv_tier_pressure import (
27
+ KVTierPressureConfig,
28
+ KVTierPressureWorkload,
29
+ )
22
30
  from lmcache.cli.commands.bench.engine_bench.workloads.long_doc_permutator import (
23
31
  LongDocPermutatorConfig,
24
32
  LongDocPermutatorWorkload,
@@ -45,12 +53,15 @@ from lmcache.cli.commands.bench.engine_bench.workloads.random_prefill import (
45
53
  RandomPrefillConfig,
46
54
  RandomPrefillWorkload,
47
55
  )
56
+ from lmcache.logging import init_logger
48
57
 
49
58
  __all__ = [
50
59
  "DEFAULT_DOC_ALIGN_TOKENS",
51
60
  "BaseWorkload",
52
61
  "LongDocPermutatorConfig",
53
62
  "LongDocPermutatorWorkload",
63
+ "KVTierPressureConfig",
64
+ "KVTierPressureWorkload",
54
65
  "LongDocQAConfig",
55
66
  "LongDocQAWorkload",
56
67
  "MultiRoundChatConfig",
@@ -66,7 +77,10 @@ __all__ = [
66
77
  "validate_max_output_length_supported",
67
78
  ]
68
79
 
80
+ logger = init_logger(__name__)
81
+
69
82
  _WORKLOAD_NAMES = (
83
+ "kv-tier-pressure",
70
84
  "long-doc-permutator",
71
85
  "long-doc-qa",
72
86
  "multi-round-chat",
@@ -77,19 +91,19 @@ _WORKLOAD_NAMES = (
77
91
 
78
92
  # Workloads that expose a user-configurable max output length, each via its own
79
93
  # flag (``--ldqa-max-output-length`` / ``--mrc-output-length`` /
80
- # ``--ldp-max-output-length``).
94
+ # ``--ldp-max-output-length`` / ``--ktp-max-output-length``).
81
95
  _WORKLOADS_WITH_MAX_OUTPUT_LENGTH: frozenset[str] = frozenset(
82
- {"long-doc-permutator", "long-doc-qa", "multi-round-chat"}
96
+ {"long-doc-permutator", "long-doc-qa", "kv-tier-pressure", "multi-round-chat"}
83
97
  )
84
98
 
85
99
 
86
100
  def validate_max_output_length_supported(workload: str) -> None:
87
101
  """Validate that a max output length can be specified for ``workload``.
88
102
 
89
- Only workloads with a max-output-length parameter (``long-doc-permutator``,
90
- ``long-doc-qa``, ``multi-round-chat``) support setting it; every other
91
- workload fixes its generation length internally, so requesting one is
92
- rejected.
103
+ Only workloads with a max-output-length parameter
104
+ (``long-doc-permutator``, ``long-doc-qa``, ``kv-tier-pressure``,
105
+ ``multi-round-chat``) support setting it; every other workload fixes its
106
+ generation length internally, so requesting one is rejected.
93
107
 
94
108
  Args:
95
109
  workload: The selected workload name (``EngineBenchConfig.workload``).
@@ -151,6 +165,66 @@ def create_workload(
151
165
  model_name=config.model,
152
166
  )
153
167
 
168
+ if config.workload == "kv-tier-pressure":
169
+ # Query the server for its real L1 capacity so the pool can be sized
170
+ # against the cache rather than against a figure the user retyped.
171
+ # Only needed when no explicit pool size was given.
172
+ lmcache_url = getattr(args, "lmcache_url", None) or ""
173
+ l1_capacity_gb = 0.0
174
+ if not args.ktp_pool_size and lmcache_url:
175
+ try:
176
+ l1_capacity_gb = resolve_l1_capacity_gb(lmcache_url)
177
+ except RuntimeError as exc:
178
+ logger.warning("Could not read L1 capacity: %s", exc)
179
+ # A cheap early signal only. The server reporting the blend module
180
+ # does not mean vLLM is driving it through CBKVConnector, and the
181
+ # fields that look authoritative are not: engine_type is the server
182
+ # class name (MPCacheServer for both engines) and nothing writes
183
+ # cb_gpu_context_meta. The real check is which lookup counter family
184
+ # moves during warm-up, which the workload does itself once traffic
185
+ # has run -- so warn here, do not refuse.
186
+ if lmcache_url and args.ktp_docs_per_request > 1:
187
+ try:
188
+ runs_blend = server_runs_blend_module(lmcache_url)
189
+ except RuntimeError as exc:
190
+ logger.warning("Could not read the server status: %s", exc)
191
+ else:
192
+ if not runs_blend:
193
+ logger.warning(
194
+ "The LMCache server at %s does not report the blend "
195
+ "module, and --ktp-docs-per-request is %d. Each "
196
+ "request concatenates a random subset of the pool in "
197
+ "random order, which a prefix-chained cache cannot "
198
+ "reuse. The run will stop after warm-up if blended "
199
+ "reuse is not confirmed; pass "
200
+ "--ktp-docs-per-request 1 for the non-blend path.",
201
+ lmcache_url,
202
+ args.ktp_docs_per_request,
203
+ )
204
+ ktp_workload_config = KVTierPressureConfig.resolve(
205
+ pool_size=args.ktp_pool_size or 0,
206
+ l1_capacity_gb=l1_capacity_gb,
207
+ tokens_per_gb_kvcache=config.tokens_per_gb_kvcache,
208
+ overflow_factor=args.ktp_overflow_factor,
209
+ docs_per_request=args.ktp_docs_per_request,
210
+ context_length=args.ktp_context_length,
211
+ system_prompt_length=args.ktp_system_prompt_length,
212
+ num_requests=args.ktp_num_requests,
213
+ access_skew=args.ktp_access_skew,
214
+ vocab_size=8000,
215
+ num_inflight_requests=args.ktp_num_inflight_requests,
216
+ max_output_length=args.ktp_max_output_length,
217
+ )
218
+ return KVTierPressureWorkload(
219
+ config=ktp_workload_config,
220
+ request_sender=request_sender,
221
+ stats_collector=stats_collector,
222
+ progress_monitor=progress_monitor,
223
+ seed=config.seed,
224
+ model_name=config.model,
225
+ lmcache_url=lmcache_url,
226
+ )
227
+
154
228
  if config.workload == "long-doc-qa":
155
229
  ld_workload_config = LongDocQAConfig.resolve(
156
230
  kv_cache_volume_gb=config.kv_cache_volume_gb,