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.
- lmcache/__init__.py +84 -0
- lmcache/_version.py +24 -0
- lmcache/cli/__init__.py +1 -0
- lmcache/cli/commands/__init__.py +34 -0
- lmcache/cli/commands/base.py +157 -0
- lmcache/cli/commands/bench/__init__.py +557 -0
- lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
- lmcache/cli/commands/bench/engine_bench/config.py +245 -0
- lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
- lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
- lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
- lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
- lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
- lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
- lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
- lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
- lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
- lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
- lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
- lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
- lmcache/cli/commands/describe.py +310 -0
- lmcache/cli/commands/kvcache.py +133 -0
- lmcache/cli/commands/mock.py +75 -0
- lmcache/cli/commands/ping.py +113 -0
- lmcache/cli/commands/query/__init__.py +155 -0
- lmcache/cli/commands/query/prompt.py +134 -0
- lmcache/cli/commands/query/request.py +357 -0
- lmcache/cli/commands/server.py +99 -0
- lmcache/cli/commands/tool/__init__.py +63 -0
- lmcache/cli/commands/tool/cache_simulator.py +113 -0
- lmcache/cli/commands/trace/__init__.py +505 -0
- lmcache/cli/commands/trace/dispatch.py +249 -0
- lmcache/cli/commands/trace/driver.py +372 -0
- lmcache/cli/commands/trace/stats.py +289 -0
- lmcache/cli/documents/lmcache.txt +11 -0
- lmcache/cli/main.py +42 -0
- lmcache/cli/metrics/__init__.py +29 -0
- lmcache/cli/metrics/formatter.py +171 -0
- lmcache/cli/metrics/handler.py +94 -0
- lmcache/cli/metrics/metrics.py +161 -0
- lmcache/cli/metrics/section.py +77 -0
- lmcache/connections.py +173 -0
- lmcache/integration/__init__.py +2 -0
- lmcache/integration/base_service_factory.py +165 -0
- lmcache/integration/request_telemetry/__init__.py +1 -0
- lmcache/integration/request_telemetry/base.py +51 -0
- lmcache/integration/request_telemetry/factory.py +113 -0
- lmcache/integration/request_telemetry/fastapi.py +109 -0
- lmcache/integration/request_telemetry/noop.py +35 -0
- lmcache/integration/sglang/__init__.py +2 -0
- lmcache/integration/sglang/sglang_adapter.py +326 -0
- lmcache/integration/sglang/utils.py +39 -0
- lmcache/integration/vllm/__init__.py +1 -0
- lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
- lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
- lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
- lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
- lmcache/integration/vllm/utils.py +433 -0
- lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
- lmcache/integration/vllm/vllm_service_factory.py +339 -0
- lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
- lmcache/logging.py +107 -0
- lmcache/native_storage_ops.pyi +230 -0
- lmcache/non_cuda_equivalents.py +1424 -0
- lmcache/observability.py +1958 -0
- lmcache/storage_backend/serde/__init__.py +1 -0
- lmcache/storage_backend/serde/cachegen_basics.py +210 -0
- lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
- lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
- lmcache/storage_backend/serde/serde.py +75 -0
- lmcache/tools/__init__.py +1 -0
- lmcache/tools/cache_simulator/README.md +392 -0
- lmcache/tools/cache_simulator/__init__.py +1 -0
- lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
- lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
- lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
- lmcache/tools/cache_simulator/lru_cache.py +124 -0
- lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
- lmcache/tools/cache_simulator/simulator.py +795 -0
- lmcache/tools/controller_benchmark/README.md +161 -0
- lmcache/tools/controller_benchmark/__init__.py +1 -0
- lmcache/tools/controller_benchmark/__main__.py +331 -0
- lmcache/tools/controller_benchmark/benchmark.py +660 -0
- lmcache/tools/controller_benchmark/config.py +44 -0
- lmcache/tools/controller_benchmark/constants.py +10 -0
- lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
- lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
- lmcache/tools/controller_benchmark/handlers/base.py +47 -0
- lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
- lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
- lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
- lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
- lmcache/tools/controller_benchmark/handlers/register.py +56 -0
- lmcache/tools/mp_status_viewer/__init__.py +1 -0
- lmcache/tools/mp_status_viewer/__main__.py +95 -0
- lmcache/usage_context.py +417 -0
- lmcache/utils.py +665 -0
- lmcache/v1/__init__.py +2 -0
- lmcache/v1/api_server/__init__.py +2 -0
- lmcache/v1/api_server/__main__.py +537 -0
- lmcache/v1/basic_check.py +112 -0
- lmcache/v1/cache_controller/__init__.py +9 -0
- lmcache/v1/cache_controller/commands/__init__.py +15 -0
- lmcache/v1/cache_controller/commands/base.py +35 -0
- lmcache/v1/cache_controller/commands/full_sync.py +49 -0
- lmcache/v1/cache_controller/config.py +176 -0
- lmcache/v1/cache_controller/controller_manager.py +535 -0
- lmcache/v1/cache_controller/controllers/__init__.py +11 -0
- lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
- lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
- lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
- lmcache/v1/cache_controller/executor.py +463 -0
- lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
- lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
- lmcache/v1/cache_controller/frontend/static/index.html +234 -0
- lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
- lmcache/v1/cache_controller/full_sync_sender.py +475 -0
- lmcache/v1/cache_controller/locks.py +149 -0
- lmcache/v1/cache_controller/message.py +828 -0
- lmcache/v1/cache_controller/observability.py +208 -0
- lmcache/v1/cache_controller/utils.py +679 -0
- lmcache/v1/cache_controller/worker.py +665 -0
- lmcache/v1/cache_engine.py +2058 -0
- lmcache/v1/cache_interface.py +19 -0
- lmcache/v1/check/__init__.py +74 -0
- lmcache/v1/check/check_mode_gen.py +86 -0
- lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
- lmcache/v1/check/check_mode_test_remote.py +155 -0
- lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
- lmcache/v1/check/utils.py +571 -0
- lmcache/v1/compute/__init__.py +2 -0
- lmcache/v1/compute/attention/__init__.py +0 -0
- lmcache/v1/compute/attention/abstract.py +39 -0
- lmcache/v1/compute/attention/flash_attn.py +129 -0
- lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
- lmcache/v1/compute/attention/metadata.py +85 -0
- lmcache/v1/compute/attention/utils.py +14 -0
- lmcache/v1/compute/blend/__init__.py +7 -0
- lmcache/v1/compute/blend/blender.py +168 -0
- lmcache/v1/compute/blend/metadata.py +34 -0
- lmcache/v1/compute/blend/utils.py +63 -0
- lmcache/v1/compute/models/__init__.py +0 -0
- lmcache/v1/compute/models/base.py +141 -0
- lmcache/v1/compute/models/llama.py +9 -0
- lmcache/v1/compute/models/qwen3.py +24 -0
- lmcache/v1/compute/models/utils.py +68 -0
- lmcache/v1/compute/positional_encoding.py +199 -0
- lmcache/v1/config.py +848 -0
- lmcache/v1/config_base.py +848 -0
- lmcache/v1/distributed/api.py +248 -0
- lmcache/v1/distributed/config.py +321 -0
- lmcache/v1/distributed/error.py +64 -0
- lmcache/v1/distributed/eviction.py +192 -0
- lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
- lmcache/v1/distributed/eviction_policy/factory.py +27 -0
- lmcache/v1/distributed/eviction_policy/lru.py +244 -0
- lmcache/v1/distributed/eviction_policy/noop.py +50 -0
- lmcache/v1/distributed/internal_api.py +170 -0
- lmcache/v1/distributed/l1_manager.py +835 -0
- lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
- lmcache/v1/distributed/l2_adapters/base.py +360 -0
- lmcache/v1/distributed/l2_adapters/config.py +385 -0
- lmcache/v1/distributed/l2_adapters/factory.py +205 -0
- lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
- lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
- lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
- lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
- lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
- lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
- lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
- lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
- lmcache/v1/distributed/memory_manager.py +179 -0
- lmcache/v1/distributed/storage_controller.py +39 -0
- lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
- lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
- lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
- lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
- lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
- lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
- lmcache/v1/distributed/storage_manager.py +532 -0
- lmcache/v1/event_manager.py +145 -0
- lmcache/v1/exceptions/__init__.py +16 -0
- lmcache/v1/gpu_connector/__init__.py +126 -0
- lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
- lmcache/v1/gpu_connector/gpu_ops.py +85 -0
- lmcache/v1/gpu_connector/hpu_connector.py +326 -0
- lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
- lmcache/v1/gpu_connector/utils.py +890 -0
- lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
- lmcache/v1/health_monitor/__init__.py +1 -0
- lmcache/v1/health_monitor/base.py +587 -0
- lmcache/v1/health_monitor/checks/__init__.py +1 -0
- lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
- lmcache/v1/health_monitor/constants.py +36 -0
- lmcache/v1/internal_api_server/__init__.py +0 -0
- lmcache/v1/internal_api_server/api_registry.py +59 -0
- lmcache/v1/internal_api_server/api_server.py +120 -0
- lmcache/v1/internal_api_server/common/__init__.py +1 -0
- lmcache/v1/internal_api_server/common/env_api.py +22 -0
- lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
- lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
- lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
- lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
- lmcache/v1/internal_api_server/common/thread_api.py +63 -0
- lmcache/v1/internal_api_server/controller/__init__.py +1 -0
- lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
- lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
- lmcache/v1/internal_api_server/utils.py +43 -0
- lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
- lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
- lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
- lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
- lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
- lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
- lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
- lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
- lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
- lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
- lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
- lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
- lmcache/v1/kv_layer_groups.py +267 -0
- lmcache/v1/lazy_memory_allocator.py +284 -0
- lmcache/v1/lookup_client/__init__.py +25 -0
- lmcache/v1/lookup_client/abstract_client.py +77 -0
- lmcache/v1/lookup_client/async_lookup_message.py +50 -0
- lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
- lmcache/v1/lookup_client/factory.py +251 -0
- lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
- lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
- lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
- lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
- lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
- lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
- lmcache/v1/lookup_client/record_strategies/base.py +327 -0
- lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
- lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
- lmcache/v1/manager.py +539 -0
- lmcache/v1/memory_management.py +2619 -0
- lmcache/v1/metadata.py +114 -0
- lmcache/v1/mp_observability/AGENTS.override.md +21 -0
- lmcache/v1/mp_observability/README.md +204 -0
- lmcache/v1/mp_observability/config.py +340 -0
- lmcache/v1/mp_observability/event.py +100 -0
- lmcache/v1/mp_observability/event_bus.py +313 -0
- lmcache/v1/mp_observability/otel_init.py +145 -0
- lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
- lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
- lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
- lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
- lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
- lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
- lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
- lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
- lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
- lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
- lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
- lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
- lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
- lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
- lmcache/v1/mp_observability/trace/__init__.py +50 -0
- lmcache/v1/mp_observability/trace/codecs.py +255 -0
- lmcache/v1/mp_observability/trace/decorator.py +147 -0
- lmcache/v1/mp_observability/trace/format.py +132 -0
- lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
- lmcache/v1/mp_observability/trace/reader.py +167 -0
- lmcache/v1/mp_observability/trace/recorder.py +300 -0
- lmcache/v1/multiprocess/__init__.py +0 -0
- lmcache/v1/multiprocess/affinity_pool.py +102 -0
- lmcache/v1/multiprocess/blend_server_v2.py +891 -0
- lmcache/v1/multiprocess/config.py +253 -0
- lmcache/v1/multiprocess/custom_types.py +281 -0
- lmcache/v1/multiprocess/futures.py +194 -0
- lmcache/v1/multiprocess/gpu_context.py +511 -0
- lmcache/v1/multiprocess/http_server.py +235 -0
- lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
- lmcache/v1/multiprocess/mq.py +732 -0
- lmcache/v1/multiprocess/protocol.py +86 -0
- lmcache/v1/multiprocess/protocols/README.md +213 -0
- lmcache/v1/multiprocess/protocols/__init__.py +127 -0
- lmcache/v1/multiprocess/protocols/base.py +89 -0
- lmcache/v1/multiprocess/protocols/blend.py +109 -0
- lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
- lmcache/v1/multiprocess/protocols/controller.py +53 -0
- lmcache/v1/multiprocess/protocols/debug.py +34 -0
- lmcache/v1/multiprocess/protocols/engine.py +146 -0
- lmcache/v1/multiprocess/protocols/observability.py +39 -0
- lmcache/v1/multiprocess/server.py +1134 -0
- lmcache/v1/multiprocess/session.py +190 -0
- lmcache/v1/multiprocess/token_hasher.py +441 -0
- lmcache/v1/offload_server/__init__.py +17 -0
- lmcache/v1/offload_server/abstract_server.py +37 -0
- lmcache/v1/offload_server/message.py +30 -0
- lmcache/v1/offload_server/zmq_server.py +122 -0
- lmcache/v1/periodic_thread.py +579 -0
- lmcache/v1/pin_monitor.py +246 -0
- lmcache/v1/plugin/__init__.py +0 -0
- lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
- lmcache/v1/protocol.py +317 -0
- lmcache/v1/rpc/__init__.py +17 -0
- lmcache/v1/rpc/transport.py +105 -0
- lmcache/v1/rpc/zmq_transport.py +213 -0
- lmcache/v1/rpc_utils.py +165 -0
- lmcache/v1/server/__init__.py +2 -0
- lmcache/v1/server/__main__.py +170 -0
- lmcache/v1/server/storage_backend/__init__.py +21 -0
- lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
- lmcache/v1/server/storage_backend/local_backend.py +75 -0
- lmcache/v1/server/utils.py +21 -0
- lmcache/v1/standalone/__init__.py +1 -0
- lmcache/v1/standalone/__main__.py +583 -0
- lmcache/v1/standalone/manager.py +80 -0
- lmcache/v1/standalone/standalone_service_factory.py +86 -0
- lmcache/v1/storage_backend/__init__.py +313 -0
- lmcache/v1/storage_backend/abstract_backend.py +445 -0
- lmcache/v1/storage_backend/audit_backend.py +233 -0
- lmcache/v1/storage_backend/batched_message_sender.py +222 -0
- lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
- lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
- lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
- lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
- lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
- lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
- lmcache/v1/storage_backend/connector/__init__.py +443 -0
- lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
- lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
- lmcache/v1/storage_backend/connector/base_connector.py +379 -0
- lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
- lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
- lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
- lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
- lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
- lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
- lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
- lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
- lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
- lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
- lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
- lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
- lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
- lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
- lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
- lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
- lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
- lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
- lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
- lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
- lmcache/v1/storage_backend/gds_backend.py +1199 -0
- lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
- lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
- lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
- lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
- lmcache/v1/storage_backend/local_disk_backend.py +656 -0
- lmcache/v1/storage_backend/maru_backend.py +734 -0
- lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
- lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
- lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
- lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
- lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
- lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
- lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
- lmcache/v1/storage_backend/p2p_backend.py +788 -0
- lmcache/v1/storage_backend/path_sharder.py +117 -0
- lmcache/v1/storage_backend/pd_backend.py +646 -0
- lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
- lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
- lmcache/v1/storage_backend/remote_backend.py +624 -0
- lmcache/v1/storage_backend/resp_client.py +227 -0
- lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
- lmcache/v1/storage_backend/storage_manager.py +1352 -0
- lmcache/v1/system_detection.py +110 -0
- lmcache/v1/token_database.py +551 -0
- lmcache/v1/transfer_channel/__init__.py +83 -0
- lmcache/v1/transfer_channel/abstract.py +285 -0
- lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
- lmcache/v1/transfer_channel/nixl_channel.py +639 -0
- lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
- lmcache/v1/transfer_channel/transfer_utils.py +63 -0
- lmcache/v1/utils/__init__.py +1 -0
- lmcache/v1/utils/bloom_filter.py +109 -0
- lmcache/v1/utils/cache_utils.py +125 -0
- lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
- lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
- lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
- lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
- lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
- lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""
|
|
3
|
+
Overview
|
|
4
|
+
--------
|
|
5
|
+
This server enables KV cache reuse across requests that share token
|
|
6
|
+
sub-sequences at *arbitrary positions*, not only at a common prefix.
|
|
7
|
+
|
|
8
|
+
Workflow (example: chunk_size = 3)
|
|
9
|
+
-----------------------------------
|
|
10
|
+
1. cb_store_pre_computed([1,2,3,4,5,6])
|
|
11
|
+
Tokens are split into full chunks ([1,2,3] and [4,5,6]). Each chunk
|
|
12
|
+
is stored in the underlying storage under its normal rolling prefix
|
|
13
|
+
hash, and the chunk fingerprints are registered in
|
|
14
|
+
BlendTokenRangeMatcher for fast sub-sequence lookup. Because normal
|
|
15
|
+
hashes are used, these chunks are also accessible via the standard
|
|
16
|
+
lookup/retrieve path.
|
|
17
|
+
|
|
18
|
+
2. cb_lookup_pre_computed([x,y,z, a,b,c, 4,5,6, m,n,p])
|
|
19
|
+
BlendTokenRangeMatcher slides a rolling polynomial hash over the new
|
|
20
|
+
request's tokens and detects that the window at positions [6, 9)
|
|
21
|
+
matches the stored chunk [4,5,6]. A prefetch task is submitted for
|
|
22
|
+
that chunk using its stored hash as the storage key. Only chunks
|
|
23
|
+
confirmed present in storage are returned as CBMatchResult objects
|
|
24
|
+
(with cur_st/cur_ed pointing to their location in the new request).
|
|
25
|
+
|
|
26
|
+
3. cb_retrieve_pre_computed(...)
|
|
27
|
+
The (prefetched) KV cache for each matched chunk is copied (CPU→GPU)
|
|
28
|
+
into the correct slot of the new request's KV cache buffer (at
|
|
29
|
+
cur_st + offset), so the LLM can skip recomputing those tokens.
|
|
30
|
+
|
|
31
|
+
4. cb_store_final([x,y,z, a,b,c, 4,5,6, m,n,p])
|
|
32
|
+
After inference completes on the new request, all its chunks are
|
|
33
|
+
stored under normal prefix hashes. Future requests sharing
|
|
34
|
+
any prefix of the new request will get standard prefix-cache hits.
|
|
35
|
+
Future requests sharing any prefix of the first request will also
|
|
36
|
+
get hits because cb_store_pre_computed already stored those chunks
|
|
37
|
+
under normal hashes.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Standard
|
|
41
|
+
import time
|
|
42
|
+
|
|
43
|
+
# Third Party
|
|
44
|
+
import numpy as np
|
|
45
|
+
import torch
|
|
46
|
+
import zmq
|
|
47
|
+
|
|
48
|
+
# First Party
|
|
49
|
+
from lmcache.logging import init_logger
|
|
50
|
+
from lmcache.v1.distributed.api import (
|
|
51
|
+
MemoryLayoutDesc,
|
|
52
|
+
ObjectKey,
|
|
53
|
+
ipc_key_to_object_keys,
|
|
54
|
+
)
|
|
55
|
+
from lmcache.v1.distributed.config import (
|
|
56
|
+
StorageManagerConfig,
|
|
57
|
+
parse_args_to_config,
|
|
58
|
+
)
|
|
59
|
+
from lmcache.v1.distributed.storage_manager import PrefetchHandle
|
|
60
|
+
from lmcache.v1.gpu_connector.gpu_ops import (
|
|
61
|
+
lmcache_memcpy_async_d2h,
|
|
62
|
+
lmcache_memcpy_async_h2d,
|
|
63
|
+
)
|
|
64
|
+
from lmcache.v1.mp_observability.config import (
|
|
65
|
+
ObservabilityConfig,
|
|
66
|
+
init_observability,
|
|
67
|
+
parse_args_to_observability_config,
|
|
68
|
+
)
|
|
69
|
+
from lmcache.v1.mp_observability.trace import maybe_initialize_trace_recorder
|
|
70
|
+
from lmcache.v1.multiprocess.config import (
|
|
71
|
+
MPServerConfig,
|
|
72
|
+
parse_args_to_mp_server_config,
|
|
73
|
+
)
|
|
74
|
+
from lmcache.v1.multiprocess.custom_types import (
|
|
75
|
+
CBMatchResult,
|
|
76
|
+
IPCCacheEngineKey,
|
|
77
|
+
KVCache,
|
|
78
|
+
)
|
|
79
|
+
from lmcache.v1.multiprocess.gpu_context import (
|
|
80
|
+
PlainGPUCacheContext,
|
|
81
|
+
)
|
|
82
|
+
from lmcache.v1.multiprocess.mq import MessageQueueServer
|
|
83
|
+
from lmcache.v1.multiprocess.protocol import (
|
|
84
|
+
RequestType,
|
|
85
|
+
get_handler_type,
|
|
86
|
+
get_payload_classes,
|
|
87
|
+
)
|
|
88
|
+
from lmcache.v1.multiprocess.server import MPCacheEngine, parse_args
|
|
89
|
+
from lmcache.v1.multiprocess.token_hasher import (
|
|
90
|
+
chunk_hash_windows_numba,
|
|
91
|
+
rolling_hash_windows_numba,
|
|
92
|
+
unique_hits_direct_id_numba,
|
|
93
|
+
update_table_id_numba,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
logger = init_logger(__name__)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class BlendTokenRangeMatcher:
|
|
100
|
+
# TODO(Jiayi): Needs thread-safety for this class.
|
|
101
|
+
"""Fast token-range matcher using polynomial rolling/chunk hashes and a
|
|
102
|
+
direct-address lookup table.
|
|
103
|
+
|
|
104
|
+
Table layout: poly_chunk_hash (u64) → compact_chunk_id (i64, sequential 0…N-1).
|
|
105
|
+
|
|
106
|
+
Because compact IDs are bounded by _TABLE_SIZE, unique_hits_direct_id_numba
|
|
107
|
+
can use a fixed `seen` array of _TABLE_SIZE bytes (~1 MB) rather than one
|
|
108
|
+
sized by an arbitrary max hash — no memory explosion.
|
|
109
|
+
|
|
110
|
+
Auxiliary storage:
|
|
111
|
+
_chunk_token_hash[i] : token_hash for chunk i (None if evicted)
|
|
112
|
+
_token_hash_to_start : token_hash → start position in seq
|
|
113
|
+
_compact_id_to_slot[i] : table slot for compact_id i
|
|
114
|
+
_token_hash_to_compact_id : token_hash → compact_chunk_id
|
|
115
|
+
|
|
116
|
+
Methods:
|
|
117
|
+
on_new_token_hashes – register a sequence; builds fingerprints
|
|
118
|
+
and writes compact IDs.
|
|
119
|
+
match_sub_sequence – sliding-window probe → compact IDs →
|
|
120
|
+
token_hash → start. Skips evicted entries.
|
|
121
|
+
remove_chunks – lazily evict stale entries. Clears the
|
|
122
|
+
table slot and auxiliary maps.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
_TABLE_BITS: int = 20 # 2^20 ≈ 1 M entries
|
|
126
|
+
_TABLE_SIZE: int = 1 << _TABLE_BITS
|
|
127
|
+
_BASE: np.uint64 = np.uint64(0x9E3779B97F4A7C15) # Fibonacci-hashing constant
|
|
128
|
+
|
|
129
|
+
def __init__(self, chunk_size: int = 256):
|
|
130
|
+
self.chunk_size = chunk_size
|
|
131
|
+
# poly_chunk_hash → compact_chunk_id; -1 = empty
|
|
132
|
+
self._table_id = np.full(self._TABLE_SIZE, -1, dtype=np.int64)
|
|
133
|
+
self._mask = np.uint64(self._TABLE_SIZE - 1)
|
|
134
|
+
# compact_chunk_id → caller-supplied token_hash (full bytes)
|
|
135
|
+
self._chunk_token_hash: list[bytes | None] = []
|
|
136
|
+
# token_hash → start position in its registered sequence
|
|
137
|
+
self._token_hash_to_start: dict[bytes, int] = {}
|
|
138
|
+
# compact_chunk_id → table slot index (for reverse lookup during eviction)
|
|
139
|
+
self._compact_id_to_slot = np.full(self._TABLE_SIZE, -1, dtype=np.int64)
|
|
140
|
+
# token_hash → compact_chunk_id (for eviction lookup)
|
|
141
|
+
self._token_hash_to_compact_id: dict[bytes, int] = {}
|
|
142
|
+
|
|
143
|
+
def on_new_token_hashes(
|
|
144
|
+
self,
|
|
145
|
+
token_ids: list[int],
|
|
146
|
+
token_hashes: list[bytes],
|
|
147
|
+
):
|
|
148
|
+
"""Register a new token sequence and index its non-overlapping chunks.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
token_ids: Raw token IDs for the full sequence (num_tokens elements).
|
|
152
|
+
Used to compute polynomial chunk fingerprints that match
|
|
153
|
+
the rolling hashes computed in match_sub_sequence.
|
|
154
|
+
token_hashes: Per-chunk bytes hashes supplied by the caller
|
|
155
|
+
(one per complete chunk of chunk_size tokens).
|
|
156
|
+
Stored as the storage key returned in CBMatchResult.hash.
|
|
157
|
+
"""
|
|
158
|
+
arr = np.array(token_ids, dtype=np.uint64)
|
|
159
|
+
# Polynomial fingerprints for non-overlapping chunks, built from raw token IDs
|
|
160
|
+
# so they match the sliding-window rolling hashes in match_sub_sequence
|
|
161
|
+
chunk_hashes = chunk_hash_windows_numba(arr, self.chunk_size, self._BASE)
|
|
162
|
+
n = int(chunk_hashes.shape[0])
|
|
163
|
+
if n == 0:
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
# Compact sequential IDs: bounded by _TABLE_SIZE, safe for seen-array sizing
|
|
167
|
+
base_id = len(self._chunk_token_hash)
|
|
168
|
+
compact_ids = np.arange(base_id, base_id + n, dtype=np.int64)
|
|
169
|
+
|
|
170
|
+
# Write table: poly_chunk_hash → compact_chunk_id
|
|
171
|
+
update_table_id_numba(chunk_hashes, self._table_id, compact_ids)
|
|
172
|
+
|
|
173
|
+
# Persist compact_id → token_hash, token_hash → start, and reverse maps
|
|
174
|
+
for i in range(n):
|
|
175
|
+
th = token_hashes[i]
|
|
176
|
+
cid = int(compact_ids[i])
|
|
177
|
+
slot = int(chunk_hashes[i]) & int(self._mask)
|
|
178
|
+
self._chunk_token_hash.append(th)
|
|
179
|
+
self._token_hash_to_start[th] = i * self.chunk_size
|
|
180
|
+
self._compact_id_to_slot[cid] = slot
|
|
181
|
+
self._token_hash_to_compact_id[th] = cid
|
|
182
|
+
|
|
183
|
+
def match_sub_sequence(
|
|
184
|
+
self,
|
|
185
|
+
token_ids: list[int],
|
|
186
|
+
) -> list[CBMatchResult]:
|
|
187
|
+
"""Find stored chunks whose fingerprints appear anywhere in token_ids.
|
|
188
|
+
|
|
189
|
+
Uses a sliding-window rolling hash so matches need not be aligned to
|
|
190
|
+
chunk_size boundaries in the query. Entries previously evicted via
|
|
191
|
+
remove_chunks (token_hash set to None) are silently skipped.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
token_ids: Query token sequence to probe (raw token IDs as uint64).
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
One CBMatchResult per unique stored chunk that was hit.
|
|
198
|
+
old_st/old_ed : positions in the originally registered sequence
|
|
199
|
+
cur_st/cur_ed : positions in the query (token_ids) where
|
|
200
|
+
the match was found
|
|
201
|
+
hash : token_hash bytes (from registration) for cache key lookup
|
|
202
|
+
"""
|
|
203
|
+
if not self._chunk_token_hash or len(token_ids) < self.chunk_size:
|
|
204
|
+
return []
|
|
205
|
+
|
|
206
|
+
arr = np.array(token_ids, dtype=np.uint64)
|
|
207
|
+
|
|
208
|
+
# Sliding-window polynomial hashes over the query
|
|
209
|
+
rolling = rolling_hash_windows_numba(arr, self.chunk_size, self._BASE)
|
|
210
|
+
|
|
211
|
+
# Probe table; seen array is _TABLE_SIZE bytes (~1 MB), fixed and safe
|
|
212
|
+
hit_ids = unique_hits_direct_id_numba(
|
|
213
|
+
rolling, self._table_id, self._mask, self._TABLE_SIZE
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if hit_ids.shape[0] == 0:
|
|
217
|
+
return []
|
|
218
|
+
|
|
219
|
+
# For each hit compact_id, find the first query position where it matched
|
|
220
|
+
hit_id_set = set(int(cid) for cid in hit_ids)
|
|
221
|
+
cid_to_query_pos: dict[int, int] = {}
|
|
222
|
+
for q_pos in range(rolling.shape[0]):
|
|
223
|
+
idx = int(rolling[q_pos]) & int(self._mask)
|
|
224
|
+
cid = int(self._table_id[idx])
|
|
225
|
+
if cid in hit_id_set and cid not in cid_to_query_pos:
|
|
226
|
+
cid_to_query_pos[cid] = q_pos
|
|
227
|
+
if len(cid_to_query_pos) == len(hit_id_set):
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
results: list[CBMatchResult] = []
|
|
231
|
+
for cid in hit_ids:
|
|
232
|
+
cid_int = int(cid)
|
|
233
|
+
th = self._chunk_token_hash[cid_int]
|
|
234
|
+
if th is None:
|
|
235
|
+
continue
|
|
236
|
+
old_st = self._token_hash_to_start.get(th)
|
|
237
|
+
cur_st = cid_to_query_pos.get(cid_int)
|
|
238
|
+
if old_st is None or cur_st is None:
|
|
239
|
+
continue
|
|
240
|
+
results.append(
|
|
241
|
+
CBMatchResult(
|
|
242
|
+
old_st=old_st,
|
|
243
|
+
old_ed=old_st + self.chunk_size,
|
|
244
|
+
cur_st=cur_st,
|
|
245
|
+
cur_ed=cur_st + self.chunk_size,
|
|
246
|
+
hash=th,
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
return results
|
|
250
|
+
|
|
251
|
+
def remove_chunks(self, token_hashes: list[bytes]) -> None:
|
|
252
|
+
"""Evict stale entries whose backing data is no longer in storage.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
token_hashes: Token hashes of chunks to remove from the table.
|
|
256
|
+
"""
|
|
257
|
+
for th in token_hashes:
|
|
258
|
+
cid = self._token_hash_to_compact_id.get(th)
|
|
259
|
+
if cid is None:
|
|
260
|
+
continue
|
|
261
|
+
# Clear the table slot
|
|
262
|
+
slot = int(self._compact_id_to_slot[cid])
|
|
263
|
+
if slot < 0:
|
|
264
|
+
logger.warning(
|
|
265
|
+
"compact_id %d has no valid table slot; "
|
|
266
|
+
"entry may have been evicted twice",
|
|
267
|
+
cid,
|
|
268
|
+
)
|
|
269
|
+
continue
|
|
270
|
+
self._table_id[slot] = -1
|
|
271
|
+
self._compact_id_to_slot[cid] = -1
|
|
272
|
+
# Clean up auxiliary maps
|
|
273
|
+
self._chunk_token_hash[cid] = None
|
|
274
|
+
self._token_hash_to_start.pop(th, None)
|
|
275
|
+
del self._token_hash_to_compact_id[th]
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# Main class and main functions
|
|
279
|
+
class BlendEngineV2(MPCacheEngine):
|
|
280
|
+
def __init__(
|
|
281
|
+
self,
|
|
282
|
+
storage_manager_config: StorageManagerConfig,
|
|
283
|
+
chunk_size: int = 256,
|
|
284
|
+
hash_algorithm: str = "blake3",
|
|
285
|
+
):
|
|
286
|
+
super().__init__(
|
|
287
|
+
storage_manager_config, chunk_size, hash_algorithm=hash_algorithm
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
self._cb_gpu_contexts: dict[int, PlainGPUCacheContext] = {}
|
|
291
|
+
|
|
292
|
+
# CB GPU ID -> (model name, world size) as metadata
|
|
293
|
+
# NOTE: This is mainly for determining the layout desc during prefetch
|
|
294
|
+
self._cb_gpu_context_meta: dict[int, tuple[str, int]] = {}
|
|
295
|
+
|
|
296
|
+
# Fast local matcher: indexes pre-computed chunk hashes for sub-sequence lookup
|
|
297
|
+
self._token_range_matcher = BlendTokenRangeMatcher(chunk_size)
|
|
298
|
+
|
|
299
|
+
def cb_register_kv_cache(
|
|
300
|
+
self,
|
|
301
|
+
instance_id: int,
|
|
302
|
+
kv_caches: KVCache,
|
|
303
|
+
model_name: str,
|
|
304
|
+
world_size: int,
|
|
305
|
+
) -> None:
|
|
306
|
+
"""
|
|
307
|
+
Register the KV cache buffer from the blend engine
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
instance_id: Unique identifier for the blend engine instance
|
|
311
|
+
kv_caches: KVCache object containing the GPU buffer pointers
|
|
312
|
+
model_name: The name of the model associated with this KV cache.
|
|
313
|
+
world_size: The world size associated with this KV cache.
|
|
314
|
+
"""
|
|
315
|
+
gpu_context = PlainGPUCacheContext(kv_caches, self.chunk_size)
|
|
316
|
+
self._cb_gpu_contexts[instance_id] = gpu_context
|
|
317
|
+
self._cb_gpu_context_meta[instance_id] = (model_name, world_size)
|
|
318
|
+
logger.info(
|
|
319
|
+
"Registered CB KV cache for instance_id %d with %d layers",
|
|
320
|
+
instance_id,
|
|
321
|
+
gpu_context.num_layers,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def cb_unregister_kv_cache(self, instance_id: int) -> None:
|
|
325
|
+
"""
|
|
326
|
+
Unregister the KV cache buffer for the given instance_id
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
instance_id: Unique identifier for the blend engine instance to unregister
|
|
330
|
+
"""
|
|
331
|
+
if instance_id in self._cb_gpu_contexts:
|
|
332
|
+
del self._cb_gpu_contexts[instance_id]
|
|
333
|
+
del self._cb_gpu_context_meta[instance_id]
|
|
334
|
+
logger.info("Unregistered CB KV cache for instance_id %d", instance_id)
|
|
335
|
+
else:
|
|
336
|
+
logger.warning(
|
|
337
|
+
"Attempted to unregister non-existent CB KV cache for instance_id %d",
|
|
338
|
+
instance_id,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
def cb_lookup_pre_computed(self, key: IPCCacheEngineKey) -> list[CBMatchResult]:
|
|
342
|
+
"""
|
|
343
|
+
Lookup the pre-computed chunks in the underlying storage.
|
|
344
|
+
|
|
345
|
+
Uses BlendTokenRangeMatcher for a fast local pre-filter, then submits
|
|
346
|
+
prefetch tasks for matched chunks using their stored hashes directly.
|
|
347
|
+
Chunks that the fingerprint table matched but are no longer present in
|
|
348
|
+
storage are lazily evicted from the matcher via remove_chunks.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
key: IPCCacheEngineKey containing the token ids to lookup
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
List of CBMatchResult for chunks that were actually found in storage,
|
|
355
|
+
ready to be passed to cb_retrieve_pre_computed.
|
|
356
|
+
"""
|
|
357
|
+
# Fast local pre-filter: find which stored chunks appear in this query
|
|
358
|
+
cb_match_result = self._token_range_matcher.match_sub_sequence(
|
|
359
|
+
list(key.token_ids)
|
|
360
|
+
)
|
|
361
|
+
if not cb_match_result:
|
|
362
|
+
return []
|
|
363
|
+
|
|
364
|
+
# Sort by query position and group consecutive matched chunks
|
|
365
|
+
cb_match_result.sort(key=lambda r: r.cur_st)
|
|
366
|
+
groups: list[list[CBMatchResult]] = []
|
|
367
|
+
for result in cb_match_result:
|
|
368
|
+
if groups and groups[-1][-1].cur_ed == result.cur_st:
|
|
369
|
+
groups[-1].append(result)
|
|
370
|
+
else:
|
|
371
|
+
groups.append([result])
|
|
372
|
+
|
|
373
|
+
prefetch_handles: list[PrefetchHandle] = []
|
|
374
|
+
found_cb_match_result: list[CBMatchResult] = []
|
|
375
|
+
model_name, world_size = key.model_name, key.world_size
|
|
376
|
+
|
|
377
|
+
# Find the cb gpu context and calculate the layout desc
|
|
378
|
+
layout_desc: MemoryLayoutDesc | None = None
|
|
379
|
+
for gpu_id, (m_name, w_size) in self._cb_gpu_context_meta.items():
|
|
380
|
+
if m_name == model_name and w_size == world_size:
|
|
381
|
+
cb_ctx = self._cb_gpu_contexts[gpu_id]
|
|
382
|
+
layout_desc = MemoryLayoutDesc(
|
|
383
|
+
shapes=[cb_ctx.get_kv_buffer_shape(self.chunk_size)],
|
|
384
|
+
dtypes=[cb_ctx.dtype],
|
|
385
|
+
)
|
|
386
|
+
break
|
|
387
|
+
|
|
388
|
+
if layout_desc is None:
|
|
389
|
+
logger.error(
|
|
390
|
+
"No CB GPU context found for model %s with world size %d "
|
|
391
|
+
"during cb_lookup_pre_computed!",
|
|
392
|
+
model_name,
|
|
393
|
+
world_size,
|
|
394
|
+
)
|
|
395
|
+
return []
|
|
396
|
+
|
|
397
|
+
# Submit prefetch for each group using CBMatchResult.hash directly
|
|
398
|
+
for group in groups:
|
|
399
|
+
chunk_hashes = [r.hash for r in group]
|
|
400
|
+
obj_keys = ipc_key_to_object_keys(key, chunk_hashes)
|
|
401
|
+
handle = self.storage_manager.submit_prefetch_task(
|
|
402
|
+
obj_keys,
|
|
403
|
+
layout_desc,
|
|
404
|
+
external_request_id=key.request_id,
|
|
405
|
+
)
|
|
406
|
+
prefetch_handles.append(handle)
|
|
407
|
+
|
|
408
|
+
logger.debug(
|
|
409
|
+
"DEBUG: Submitted prefetch for %d chunks starting at %d",
|
|
410
|
+
len(group),
|
|
411
|
+
group[0].cur_st,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# TODO(Jiayi): We need to follow how lookup is handled in server.py
|
|
415
|
+
# to optimize performance.
|
|
416
|
+
# Collect only the CBMatchResults for chunks actually found in storage
|
|
417
|
+
stale_hashes: list[bytes] = []
|
|
418
|
+
for handle, group in zip(prefetch_handles, groups, strict=False):
|
|
419
|
+
found_count = None
|
|
420
|
+
while True:
|
|
421
|
+
found_count = self.storage_manager.query_prefetch_status(handle)
|
|
422
|
+
if found_count is not None:
|
|
423
|
+
break
|
|
424
|
+
|
|
425
|
+
# Standard
|
|
426
|
+
import time
|
|
427
|
+
|
|
428
|
+
time.sleep(0.001)
|
|
429
|
+
|
|
430
|
+
# Real found count after dedup the TP
|
|
431
|
+
found_count = found_count // world_size
|
|
432
|
+
|
|
433
|
+
start = group[0].cur_st
|
|
434
|
+
end = group[-1].cur_ed
|
|
435
|
+
if found_count > 0:
|
|
436
|
+
found_cb_match_result.extend(group[:found_count])
|
|
437
|
+
# Chunks after found_count in the group are stale
|
|
438
|
+
stale_hashes.extend(r.hash for r in group[found_count:])
|
|
439
|
+
logger.debug(
|
|
440
|
+
"Found %d pre-computed chunks for range (%d, %d)",
|
|
441
|
+
found_count,
|
|
442
|
+
start,
|
|
443
|
+
end,
|
|
444
|
+
)
|
|
445
|
+
else:
|
|
446
|
+
stale_hashes.extend(r.hash for r in group)
|
|
447
|
+
logger.debug(
|
|
448
|
+
"No pre-computed chunks found for range (%d, %d)",
|
|
449
|
+
start,
|
|
450
|
+
end,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
# Evict stale entries from the fingerprint table
|
|
454
|
+
if stale_hashes:
|
|
455
|
+
self._token_range_matcher.remove_chunks(stale_hashes)
|
|
456
|
+
logger.debug(
|
|
457
|
+
"Evicted %d stale chunks from fingerprint table",
|
|
458
|
+
len(stale_hashes),
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
return found_cb_match_result
|
|
462
|
+
|
|
463
|
+
def _cb_store_gpu_copy(
|
|
464
|
+
self,
|
|
465
|
+
obj_keys: list[ObjectKey],
|
|
466
|
+
gpu_context: PlainGPUCacheContext,
|
|
467
|
+
offset: int,
|
|
468
|
+
event_ipc_handle: bytes,
|
|
469
|
+
) -> tuple[torch.cuda.Event, dict]:
|
|
470
|
+
"""
|
|
471
|
+
Helper function to perform GPU-to-CPU copy operations for storing chunks.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
obj_keys: List of object keys to store.
|
|
475
|
+
gpu_context: GPU context for the blend engine instance.
|
|
476
|
+
offset: The starting offset in the CB KV cache buffer.
|
|
477
|
+
event_ipc_handle: The IPC handle for the CUDA event that signals the
|
|
478
|
+
completion of LLM inference.
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
A tuple of (event, reserved_dict) where event is the CUDA event and
|
|
482
|
+
reserved_dict is the dictionary of reserved memory objects.
|
|
483
|
+
"""
|
|
484
|
+
with (
|
|
485
|
+
torch.cuda.device(gpu_context.device),
|
|
486
|
+
torch.cuda.stream(gpu_context.stream),
|
|
487
|
+
):
|
|
488
|
+
event = torch.cuda.Event(interprocess=True)
|
|
489
|
+
|
|
490
|
+
# Wait for vLLM event to finish
|
|
491
|
+
vllm_event = torch.cuda.Event.from_ipc_handle(
|
|
492
|
+
gpu_context.device, event_ipc_handle
|
|
493
|
+
)
|
|
494
|
+
vllm_event.wait(stream=gpu_context.stream)
|
|
495
|
+
|
|
496
|
+
# Prepare for the copy
|
|
497
|
+
num_tokens = self.chunk_size
|
|
498
|
+
cpu_shape = gpu_context.get_kv_buffer_shape(num_tokens)
|
|
499
|
+
layout_desc = MemoryLayoutDesc(
|
|
500
|
+
shapes=[cpu_shape], dtypes=[gpu_context.dtype]
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
reserved_dict = self.storage_manager.reserve_write(
|
|
504
|
+
obj_keys, layout_desc, "new"
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
for idx, obj_key in enumerate(obj_keys):
|
|
508
|
+
if obj_key in reserved_dict:
|
|
509
|
+
memory_obj = reserved_dict[obj_key]
|
|
510
|
+
else:
|
|
511
|
+
continue
|
|
512
|
+
|
|
513
|
+
offset_start = idx * self.chunk_size + offset
|
|
514
|
+
offset_end = offset_start + self.chunk_size
|
|
515
|
+
|
|
516
|
+
# Copy from GPU to CPU
|
|
517
|
+
tmp_buffer = gpu_context.get_tmp_gpu_buffer(offset_end - offset_start)
|
|
518
|
+
gpu_kv_slice = gpu_context.slice_kv_cache_on_tokens(
|
|
519
|
+
offset_start, offset_end
|
|
520
|
+
)
|
|
521
|
+
with self.lock:
|
|
522
|
+
tmp_buffer.copy_(gpu_kv_slice, non_blocking=True)
|
|
523
|
+
lmcache_memcpy_async_d2h(tmp_buffer, memory_obj)
|
|
524
|
+
|
|
525
|
+
event.record()
|
|
526
|
+
|
|
527
|
+
# Call finish_write after the copy is done
|
|
528
|
+
gpu_context.cupy_stream.launch_host_func(
|
|
529
|
+
self.storage_manager.finish_write,
|
|
530
|
+
list(reserved_dict.keys()),
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
return event, reserved_dict
|
|
534
|
+
|
|
535
|
+
def cb_store_pre_computed(
|
|
536
|
+
self,
|
|
537
|
+
key: IPCCacheEngineKey,
|
|
538
|
+
offset: int,
|
|
539
|
+
instance_id: int,
|
|
540
|
+
event_ipc_handle: bytes,
|
|
541
|
+
) -> tuple[bytes, bool]:
|
|
542
|
+
"""
|
|
543
|
+
Store the pre-computed chunks in the underlying storage for later retrieval.
|
|
544
|
+
|
|
545
|
+
Args:
|
|
546
|
+
key: IPCCacheEngineKey containing the token ids for which the pre-computed
|
|
547
|
+
chunks are stored.
|
|
548
|
+
offset: The starting offset in the CB KV cache buffer where the
|
|
549
|
+
pre-computed
|
|
550
|
+
instance_id: The instance_id of the blend engine instance to store the
|
|
551
|
+
pre-computed chunks for.
|
|
552
|
+
event_ipc_handle: The IPC handle for the CUDA event that signals the
|
|
553
|
+
completion of LLM inference.
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
IPC handle bytes for the event that signals the completion of storing the
|
|
557
|
+
pre-computed chunks, and a boolean flag indicating if the store is
|
|
558
|
+
successful.
|
|
559
|
+
|
|
560
|
+
Note:
|
|
561
|
+
The input tokens should not have any separator in it. It should just be
|
|
562
|
+
one "paragraph".
|
|
563
|
+
This function will discard the last partial chunk and only store the full
|
|
564
|
+
chunks
|
|
565
|
+
"""
|
|
566
|
+
# Compute normal prefix hashes so these chunks are accessible both via
|
|
567
|
+
# the CB lookup path and via the standard lookup/retrieve path.
|
|
568
|
+
chunk_hashes = self.token_hasher.compute_chunk_hashes(list(key.token_ids))
|
|
569
|
+
# convert to object key
|
|
570
|
+
obj_keys = ipc_key_to_object_keys(key, chunk_hashes)
|
|
571
|
+
|
|
572
|
+
assert instance_id in self._cb_gpu_contexts, (
|
|
573
|
+
f"Instance ID {instance_id} not registered for CB KV cache"
|
|
574
|
+
)
|
|
575
|
+
gpu_context = self._cb_gpu_contexts[instance_id]
|
|
576
|
+
|
|
577
|
+
event, reserved_dict = self._cb_store_gpu_copy(
|
|
578
|
+
obj_keys, gpu_context, offset, event_ipc_handle
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
# Register chunk hashes with the local matcher for fast sub-sequence lookup
|
|
582
|
+
token_hashes = list(chunk_hashes)
|
|
583
|
+
|
|
584
|
+
# NOTE(Jiayi): We only register the token hashes for worker_id 0 or None to
|
|
585
|
+
# avoid duplicate registration across workers.
|
|
586
|
+
if key.worker_id in [0, None]:
|
|
587
|
+
self._token_range_matcher.on_new_token_hashes(
|
|
588
|
+
list(key.token_ids), token_hashes
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
logger.info(
|
|
592
|
+
"Stored pre-computed doc with %d tokens, num stored chunks: %d",
|
|
593
|
+
key.end - key.start,
|
|
594
|
+
len(reserved_dict),
|
|
595
|
+
)
|
|
596
|
+
return event.ipc_handle(), True
|
|
597
|
+
|
|
598
|
+
def cb_retrieve_pre_computed(
|
|
599
|
+
self,
|
|
600
|
+
key: IPCCacheEngineKey,
|
|
601
|
+
cb_match_result: list[CBMatchResult],
|
|
602
|
+
offset: int,
|
|
603
|
+
instance_id: int,
|
|
604
|
+
event_ipc_handle: bytes,
|
|
605
|
+
) -> tuple[bytes, bool]:
|
|
606
|
+
"""
|
|
607
|
+
Retrieve the pre-computed chunks from the underlying storage and copy them to
|
|
608
|
+
the CB KV cache buffer.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
key: IPCCacheEngineKey containing the token ids for which the pre-computed
|
|
612
|
+
chunks are retrieved.
|
|
613
|
+
cb_match_result: List of CBMatchResult returned by cb_lookup_pre_computed,
|
|
614
|
+
containing the per-chunk hashes and query positions.
|
|
615
|
+
offset: The starting offset in the CB KV cache buffer to copy the retrieved
|
|
616
|
+
chunks to.
|
|
617
|
+
instance_id: The instance_id of the blend engine instance to retrieve the
|
|
618
|
+
pre-computed chunks for.
|
|
619
|
+
event_ipc_handle: The IPC handle for the CUDA event that signals the
|
|
620
|
+
completion of LLM inference.
|
|
621
|
+
|
|
622
|
+
Returns:
|
|
623
|
+
IPC handle bytes for the event that signals the completion of retrieving the
|
|
624
|
+
pre-computed chunks, and a boolean flag indicating if the retrieval is
|
|
625
|
+
successful.
|
|
626
|
+
|
|
627
|
+
Note:
|
|
628
|
+
We must call `cb_lookup_pre_computed` first before calling this function
|
|
629
|
+
"""
|
|
630
|
+
assert instance_id in self._cb_gpu_contexts, (
|
|
631
|
+
f"Instance ID {instance_id} not registered for CB KV cache"
|
|
632
|
+
)
|
|
633
|
+
gpu_context = self._cb_gpu_contexts[instance_id]
|
|
634
|
+
|
|
635
|
+
# One obj_key per match_result, in cur_st order
|
|
636
|
+
cb_match_result = sorted(cb_match_result, key=lambda r: r.cur_st)
|
|
637
|
+
chunk_hashes = [r.hash for r in cb_match_result]
|
|
638
|
+
all_obj_keys = ipc_key_to_object_keys(key, chunk_hashes)
|
|
639
|
+
|
|
640
|
+
logger.debug("DEBUG object keys to retrieve: %s", all_obj_keys)
|
|
641
|
+
|
|
642
|
+
with (
|
|
643
|
+
torch.cuda.device(gpu_context.device),
|
|
644
|
+
torch.cuda.stream(gpu_context.stream),
|
|
645
|
+
):
|
|
646
|
+
event = torch.cuda.Event(interprocess=True)
|
|
647
|
+
|
|
648
|
+
try:
|
|
649
|
+
with self.storage_manager.read_prefetched_results(
|
|
650
|
+
all_obj_keys
|
|
651
|
+
) as memory_objs:
|
|
652
|
+
if memory_objs is None:
|
|
653
|
+
logger.error("Some keys not found during CB retrieve!")
|
|
654
|
+
return event.ipc_handle(), False
|
|
655
|
+
|
|
656
|
+
for r, memory_obj in zip(
|
|
657
|
+
cb_match_result, memory_objs, strict=False
|
|
658
|
+
):
|
|
659
|
+
gpu_st = r.cur_st + offset
|
|
660
|
+
gpu_ed = gpu_st + self.chunk_size
|
|
661
|
+
tmp_buffer = gpu_context.get_tmp_gpu_buffer(self.chunk_size)
|
|
662
|
+
target_buffer = gpu_context.slice_kv_cache_on_tokens(
|
|
663
|
+
gpu_st, gpu_ed
|
|
664
|
+
)
|
|
665
|
+
with self.lock:
|
|
666
|
+
lmcache_memcpy_async_h2d(memory_obj, tmp_buffer)
|
|
667
|
+
target_buffer.copy_(tmp_buffer, non_blocking=True)
|
|
668
|
+
|
|
669
|
+
except Exception:
|
|
670
|
+
logger.exception("Error during retrieving prefetched results")
|
|
671
|
+
return event.ipc_handle(), False
|
|
672
|
+
|
|
673
|
+
finally:
|
|
674
|
+
event.record()
|
|
675
|
+
# TODO: here we simply "unlock" all the keys, which may cause
|
|
676
|
+
# double-unlock if error happens during read_prefetched_results.
|
|
677
|
+
# We should consider not unlocking objects in read_prefetched_results
|
|
678
|
+
# if error happens.
|
|
679
|
+
gpu_context.cupy_stream.launch_host_func(
|
|
680
|
+
self.storage_manager.finish_read_prefetched, all_obj_keys
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
logger.info(
|
|
684
|
+
"Retrieved pre-computed for %d match results to GPU offset starting at %d",
|
|
685
|
+
len(cb_match_result),
|
|
686
|
+
offset,
|
|
687
|
+
)
|
|
688
|
+
return event.ipc_handle(), True
|
|
689
|
+
|
|
690
|
+
def cb_store_final(
|
|
691
|
+
self,
|
|
692
|
+
key: IPCCacheEngineKey,
|
|
693
|
+
offset: int,
|
|
694
|
+
instance_id: int,
|
|
695
|
+
event_ipc_handle: bytes,
|
|
696
|
+
) -> tuple[bytes, bool]:
|
|
697
|
+
"""
|
|
698
|
+
Store the final chunks in the underlying storage after processing. The stored
|
|
699
|
+
chunk should be accessible for normal mode LLMs.
|
|
700
|
+
|
|
701
|
+
Args:
|
|
702
|
+
key: IPCCacheEngineKey containing the token ids for which the final chunks
|
|
703
|
+
are stored.
|
|
704
|
+
offset: The starting offset in the CB KV cache buffer where the final
|
|
705
|
+
chunks are stored.
|
|
706
|
+
instance_id: The instance_id of the blend engine instance to store the final
|
|
707
|
+
chunks for.
|
|
708
|
+
event_ipc_handle: The IPC handle for the CUDA event that signals the
|
|
709
|
+
completion of LLM inference.
|
|
710
|
+
|
|
711
|
+
Returns:
|
|
712
|
+
IPC handle bytes for the event that signals the completion of storing the
|
|
713
|
+
final chunks, and a boolean flag indicating if the store is successful.
|
|
714
|
+
"""
|
|
715
|
+
# Compute normal hash for the keys
|
|
716
|
+
chunk_hashes = self.token_hasher.compute_chunk_hashes(list(key.token_ids))
|
|
717
|
+
|
|
718
|
+
# convert to object key
|
|
719
|
+
obj_keys = ipc_key_to_object_keys(key, chunk_hashes)
|
|
720
|
+
|
|
721
|
+
# Get GPU context
|
|
722
|
+
assert instance_id in self._cb_gpu_contexts, (
|
|
723
|
+
f"Instance ID {instance_id} not registered for CB KV cache"
|
|
724
|
+
)
|
|
725
|
+
gpu_context = self._cb_gpu_contexts[instance_id]
|
|
726
|
+
|
|
727
|
+
event, reserved_dict = self._cb_store_gpu_copy(
|
|
728
|
+
obj_keys, gpu_context, offset, event_ipc_handle
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
logger.info(
|
|
732
|
+
"Stored final doc with %d tokens, num stored chunks: %d",
|
|
733
|
+
key.end - key.start,
|
|
734
|
+
len(reserved_dict),
|
|
735
|
+
)
|
|
736
|
+
return event.ipc_handle(), True
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def add_handler_helper(
|
|
740
|
+
server: MessageQueueServer, request_type: RequestType, handler_function
|
|
741
|
+
):
|
|
742
|
+
payload_classes = get_payload_classes(request_type)
|
|
743
|
+
handler_type = get_handler_type(request_type)
|
|
744
|
+
server.add_handler(
|
|
745
|
+
request_type,
|
|
746
|
+
payload_classes,
|
|
747
|
+
handler_type,
|
|
748
|
+
handler_function,
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def run_cache_server(
|
|
753
|
+
mp_config: MPServerConfig,
|
|
754
|
+
storage_manager_config: StorageManagerConfig,
|
|
755
|
+
obs_config: ObservabilityConfig,
|
|
756
|
+
return_engine: bool = False,
|
|
757
|
+
):
|
|
758
|
+
"""
|
|
759
|
+
Run the LMCache cache server with ZMQ message queue.
|
|
760
|
+
|
|
761
|
+
Args:
|
|
762
|
+
mp_config: Configuration for the ZMQ multiprocess server
|
|
763
|
+
storage_manager_config: Configuration for the storage manager
|
|
764
|
+
obs_config: Configuration for the observability stack
|
|
765
|
+
return_engine: If True, return (server, engine) after starting;
|
|
766
|
+
if False, run blocking loop to keep server alive
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
If return_engine is True: tuple of (MessageQueueServer, BlendEngineV2)
|
|
770
|
+
If return_engine is False: None (blocks until interrupted)
|
|
771
|
+
"""
|
|
772
|
+
event_bus = init_observability(obs_config)
|
|
773
|
+
|
|
774
|
+
# Wire up the trace recorder (no-op when --trace-level is unset).
|
|
775
|
+
maybe_initialize_trace_recorder(event_bus, obs_config, storage_manager_config)
|
|
776
|
+
|
|
777
|
+
# Initialize the engine (loggers self-register with the global controller)
|
|
778
|
+
engine = BlendEngineV2(
|
|
779
|
+
storage_manager_config=storage_manager_config,
|
|
780
|
+
chunk_size=mp_config.chunk_size,
|
|
781
|
+
hash_algorithm=mp_config.hash_algorithm,
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
# Initialize the message queue server
|
|
785
|
+
context = zmq.Context.instance()
|
|
786
|
+
server = MessageQueueServer(
|
|
787
|
+
bind_url=f"tcp://{mp_config.host}:{mp_config.port}",
|
|
788
|
+
context=context,
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
# Add handlers for original server
|
|
792
|
+
add_handler_helper(server, RequestType.REGISTER_KV_CACHE, engine.register_kv_cache)
|
|
793
|
+
add_handler_helper(
|
|
794
|
+
server, RequestType.UNREGISTER_KV_CACHE, engine.unregister_kv_cache
|
|
795
|
+
)
|
|
796
|
+
add_handler_helper(server, RequestType.STORE, engine.store)
|
|
797
|
+
add_handler_helper(server, RequestType.LOOKUP, engine.lookup)
|
|
798
|
+
add_handler_helper(
|
|
799
|
+
server, RequestType.QUERY_PREFETCH_STATUS, engine.query_prefetch_status
|
|
800
|
+
)
|
|
801
|
+
add_handler_helper(server, RequestType.FREE_LOOKUP_LOCKS, engine.free_lookup_locks)
|
|
802
|
+
add_handler_helper(server, RequestType.RETRIEVE, engine.retrieve)
|
|
803
|
+
add_handler_helper(server, RequestType.CLEAR, engine.clear)
|
|
804
|
+
add_handler_helper(server, RequestType.GET_CHUNK_SIZE, engine.get_chunk_size)
|
|
805
|
+
add_handler_helper(server, RequestType.END_SESSION, engine.end_session)
|
|
806
|
+
add_handler_helper(server, RequestType.NOOP, engine.debug)
|
|
807
|
+
add_handler_helper(
|
|
808
|
+
server,
|
|
809
|
+
RequestType.REPORT_BLOCK_ALLOCATION,
|
|
810
|
+
engine.report_block_allocations,
|
|
811
|
+
)
|
|
812
|
+
# Add handler for blend operations
|
|
813
|
+
add_handler_helper(
|
|
814
|
+
server, RequestType.CB_REGISTER_KV_CACHE, engine.cb_register_kv_cache
|
|
815
|
+
)
|
|
816
|
+
add_handler_helper(
|
|
817
|
+
server, RequestType.CB_UNREGISTER_KV_CACHE, engine.cb_unregister_kv_cache
|
|
818
|
+
)
|
|
819
|
+
add_handler_helper(
|
|
820
|
+
server, RequestType.CB_LOOKUP_PRE_COMPUTED_V2, engine.cb_lookup_pre_computed
|
|
821
|
+
)
|
|
822
|
+
add_handler_helper(
|
|
823
|
+
server, RequestType.CB_STORE_PRE_COMPUTED, engine.cb_store_pre_computed
|
|
824
|
+
)
|
|
825
|
+
add_handler_helper(
|
|
826
|
+
server, RequestType.CB_RETRIEVE_PRE_COMPUTED_V2, engine.cb_retrieve_pre_computed
|
|
827
|
+
)
|
|
828
|
+
add_handler_helper(server, RequestType.CB_STORE_FINAL, engine.cb_store_final)
|
|
829
|
+
add_handler_helper(server, RequestType.PING, engine.ping)
|
|
830
|
+
|
|
831
|
+
# Assign thread pools
|
|
832
|
+
server.add_affinity_thread_pool(
|
|
833
|
+
[
|
|
834
|
+
RequestType.STORE,
|
|
835
|
+
RequestType.RETRIEVE,
|
|
836
|
+
RequestType.CB_STORE_PRE_COMPUTED,
|
|
837
|
+
RequestType.CB_RETRIEVE_PRE_COMPUTED_V2,
|
|
838
|
+
RequestType.CB_STORE_FINAL,
|
|
839
|
+
],
|
|
840
|
+
max_workers=mp_config.max_gpu_workers,
|
|
841
|
+
)
|
|
842
|
+
server.add_normal_thread_pool(
|
|
843
|
+
[
|
|
844
|
+
RequestType.LOOKUP,
|
|
845
|
+
RequestType.QUERY_PREFETCH_STATUS,
|
|
846
|
+
RequestType.FREE_LOOKUP_LOCKS,
|
|
847
|
+
RequestType.END_SESSION,
|
|
848
|
+
RequestType.CLEAR,
|
|
849
|
+
RequestType.CB_LOOKUP_PRE_COMPUTED_V2,
|
|
850
|
+
RequestType.PING,
|
|
851
|
+
RequestType.REPORT_BLOCK_ALLOCATION,
|
|
852
|
+
],
|
|
853
|
+
max_workers=mp_config.max_cpu_workers,
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
logger.info(
|
|
857
|
+
"LMCache ZMQ cache server is running on tcp://%s:%d",
|
|
858
|
+
mp_config.host,
|
|
859
|
+
mp_config.port,
|
|
860
|
+
)
|
|
861
|
+
# Start the ZMQ server
|
|
862
|
+
torch.cuda.init()
|
|
863
|
+
server.start()
|
|
864
|
+
|
|
865
|
+
logger.info("LMCache cache blend v2 server is running...")
|
|
866
|
+
|
|
867
|
+
# Return server and engine if requested (for HTTP server integration)
|
|
868
|
+
if return_engine:
|
|
869
|
+
return server, engine
|
|
870
|
+
|
|
871
|
+
# Dummy loop to keep the server running
|
|
872
|
+
try:
|
|
873
|
+
while True:
|
|
874
|
+
time.sleep(1)
|
|
875
|
+
except KeyboardInterrupt:
|
|
876
|
+
logger.info("Shutting down server...")
|
|
877
|
+
event_bus.stop()
|
|
878
|
+
server.close()
|
|
879
|
+
engine.close()
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
if __name__ == "__main__":
|
|
883
|
+
args = parse_args()
|
|
884
|
+
mp_config = parse_args_to_mp_server_config(args)
|
|
885
|
+
storage_manager_config = parse_args_to_config(args)
|
|
886
|
+
obs_config = parse_args_to_observability_config(args)
|
|
887
|
+
run_cache_server(
|
|
888
|
+
mp_config=mp_config,
|
|
889
|
+
storage_manager_config=storage_manager_config,
|
|
890
|
+
obs_config=obs_config,
|
|
891
|
+
)
|