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,357 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""HTTP request for VLLM Serving Engine."""
|
|
3
|
+
|
|
4
|
+
# Standard
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
import time
|
|
9
|
+
import urllib.error
|
|
10
|
+
import urllib.request
|
|
11
|
+
|
|
12
|
+
_MAX_ERR = 65536
|
|
13
|
+
MetricValue = tuple[str, Any]
|
|
14
|
+
MetricMap = dict[str, MetricValue]
|
|
15
|
+
_METRIC_NAMES = {
|
|
16
|
+
"prompt_tokens": "Input tokens",
|
|
17
|
+
"output_tokens": "Output tokens",
|
|
18
|
+
"ttft_ms": "TTFT (ms)",
|
|
19
|
+
"tpot_ms_per_token": "TPOT (ms/token)",
|
|
20
|
+
"total_latency_ms": "Total latency (ms)",
|
|
21
|
+
"throughput_tokens_per_s": "Throughput (tokens/s)",
|
|
22
|
+
"model": "Model",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _clip(text: str, limit: int = _MAX_ERR) -> str:
|
|
27
|
+
return (
|
|
28
|
+
text
|
|
29
|
+
if len(text) <= limit
|
|
30
|
+
else text[: max(0, limit - 24)] + "\n...(message truncated)..."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _info(msg: str) -> None:
|
|
35
|
+
print(f"lmcache query: {msg}", file=sys.stderr)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _openai_error(obj: dict[str, Any]) -> Optional[str]:
|
|
39
|
+
err = obj.get("error")
|
|
40
|
+
if err is None:
|
|
41
|
+
return None
|
|
42
|
+
if isinstance(err, str):
|
|
43
|
+
return err.strip() or None
|
|
44
|
+
if not isinstance(err, dict):
|
|
45
|
+
return _clip(str(err))
|
|
46
|
+
for key in ("message", "detail"):
|
|
47
|
+
val = err.get(key)
|
|
48
|
+
if not isinstance(val, str) or not val.strip():
|
|
49
|
+
continue
|
|
50
|
+
typ = err.get("type") or err.get("code")
|
|
51
|
+
if key == "message" and isinstance(typ, str) and typ.strip():
|
|
52
|
+
return f"{typ.strip()}: {val.strip()}"
|
|
53
|
+
return val.strip()
|
|
54
|
+
try:
|
|
55
|
+
return _clip(json.dumps(err, ensure_ascii=False))
|
|
56
|
+
except Exception:
|
|
57
|
+
return _clip(str(err))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _raise_openai_error(obj: dict[str, Any]) -> None:
|
|
61
|
+
msg = _openai_error(obj)
|
|
62
|
+
if msg:
|
|
63
|
+
raise RuntimeError(_clip(msg))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _raise_json_blob_error(blob: str) -> None:
|
|
67
|
+
s = blob.strip()
|
|
68
|
+
if not s.startswith("{"):
|
|
69
|
+
return
|
|
70
|
+
try:
|
|
71
|
+
obj = json.loads(s)
|
|
72
|
+
except json.JSONDecodeError:
|
|
73
|
+
return
|
|
74
|
+
if isinstance(obj, dict):
|
|
75
|
+
_raise_openai_error(obj)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _api_url(base: str, path: str) -> str:
|
|
79
|
+
base = base.strip()
|
|
80
|
+
if "://" not in base:
|
|
81
|
+
base = f"http://{base}"
|
|
82
|
+
base = base.rstrip("/")
|
|
83
|
+
return f"{base if base.endswith('/v1') else base + '/v1'}/{path}"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _read_json(url: str, timeout: float) -> dict[str, Any]:
|
|
87
|
+
try:
|
|
88
|
+
with urllib.request.urlopen(
|
|
89
|
+
urllib.request.Request(url, method="GET"), timeout=max(timeout + 2.0, 5.0)
|
|
90
|
+
) as resp:
|
|
91
|
+
raw = resp.read().decode("utf-8", errors="replace")
|
|
92
|
+
except urllib.error.HTTPError as e:
|
|
93
|
+
body = e.read().decode("utf-8", errors="replace")[:512]
|
|
94
|
+
raise RuntimeError(
|
|
95
|
+
f"GET {url} failed (HTTP {e.code}): {body or 'no body'}"
|
|
96
|
+
) from e
|
|
97
|
+
except urllib.error.URLError as e:
|
|
98
|
+
raise RuntimeError(f"GET {url} failed: {getattr(e, 'reason', e)}") from e
|
|
99
|
+
try:
|
|
100
|
+
obj = json.loads(raw)
|
|
101
|
+
except json.JSONDecodeError as e:
|
|
102
|
+
raise RuntimeError(f"Invalid JSON from GET {url}: {e}") from e
|
|
103
|
+
if not isinstance(obj, dict):
|
|
104
|
+
raise RuntimeError(f"GET {url}: expected a JSON object")
|
|
105
|
+
return obj
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _sse_piece(obj: dict[str, Any], chat: bool) -> str:
|
|
109
|
+
choices = obj.get("choices") or []
|
|
110
|
+
if not choices:
|
|
111
|
+
return ""
|
|
112
|
+
c0 = choices[0]
|
|
113
|
+
return (
|
|
114
|
+
str((c0.get("delta") or {}).get("content") or "")
|
|
115
|
+
if chat
|
|
116
|
+
else str(c0.get("text") or "")
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _trim_misc_buffer(misc: list[str], limit: int = _MAX_ERR) -> None:
|
|
121
|
+
while misc and sum(map(len, misc)) > limit:
|
|
122
|
+
misc.pop(0)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _stream(
|
|
126
|
+
url: str,
|
|
127
|
+
body: dict[str, Any],
|
|
128
|
+
timeout: float,
|
|
129
|
+
*,
|
|
130
|
+
chat: bool,
|
|
131
|
+
max_tokens: int,
|
|
132
|
+
) -> dict[str, Any]:
|
|
133
|
+
"""POST with ``stream: true``; parse SSE; return TTFT/TPOT and token metrics."""
|
|
134
|
+
payload = {
|
|
135
|
+
**body,
|
|
136
|
+
"stream": True,
|
|
137
|
+
"stream_options": {"include_usage": True},
|
|
138
|
+
}
|
|
139
|
+
req = urllib.request.Request(
|
|
140
|
+
url,
|
|
141
|
+
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
|
|
142
|
+
method="POST",
|
|
143
|
+
headers={"Content-Type": "application/json"},
|
|
144
|
+
)
|
|
145
|
+
t0, first_token_t, pieces, usage, misc = time.time(), None, [], None, []
|
|
146
|
+
try:
|
|
147
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
148
|
+
while True:
|
|
149
|
+
raw = resp.readline()
|
|
150
|
+
if not raw:
|
|
151
|
+
break
|
|
152
|
+
line = raw.decode("utf-8", errors="replace").strip()
|
|
153
|
+
if not line:
|
|
154
|
+
continue
|
|
155
|
+
if not line.startswith("data:"):
|
|
156
|
+
misc.append(line)
|
|
157
|
+
_trim_misc_buffer(misc)
|
|
158
|
+
continue
|
|
159
|
+
chunk = line[5:].strip()
|
|
160
|
+
if chunk == "[DONE]":
|
|
161
|
+
break
|
|
162
|
+
try:
|
|
163
|
+
obj = json.loads(chunk)
|
|
164
|
+
except json.JSONDecodeError:
|
|
165
|
+
misc.append(chunk)
|
|
166
|
+
_trim_misc_buffer(misc)
|
|
167
|
+
continue
|
|
168
|
+
if not isinstance(obj, dict):
|
|
169
|
+
continue
|
|
170
|
+
_raise_openai_error(obj)
|
|
171
|
+
piece = _sse_piece(obj, chat)
|
|
172
|
+
if piece:
|
|
173
|
+
first_token_t = first_token_t or time.time()
|
|
174
|
+
pieces.append(piece)
|
|
175
|
+
|
|
176
|
+
u_chunk = obj.get("usage")
|
|
177
|
+
if u_chunk is not None:
|
|
178
|
+
usage = u_chunk
|
|
179
|
+
t1 = time.time()
|
|
180
|
+
except urllib.error.HTTPError as e:
|
|
181
|
+
err_body = e.read().decode("utf-8", errors="replace")
|
|
182
|
+
_raise_json_blob_error(err_body)
|
|
183
|
+
raise RuntimeError(
|
|
184
|
+
_clip(f"POST {url} failed (HTTP {e.code}):\n{_clip(err_body)}")
|
|
185
|
+
) from e
|
|
186
|
+
except urllib.error.URLError as e:
|
|
187
|
+
raise RuntimeError(f"POST {url} failed: {getattr(e, 'reason', e)}") from e
|
|
188
|
+
|
|
189
|
+
misc_text = "\n".join(misc).strip()
|
|
190
|
+
_raise_json_blob_error(misc_text)
|
|
191
|
+
joined = "".join(pieces)
|
|
192
|
+
if not joined and usage is None:
|
|
193
|
+
raise RuntimeError(
|
|
194
|
+
_clip(f"No completion output from engine. Captured response:\n{misc_text}")
|
|
195
|
+
if misc_text
|
|
196
|
+
else "Empty response from engine (no SSE chunks parsed)."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
u = usage or {}
|
|
200
|
+
prompt_tokens = int(u.get("prompt_tokens") or 0)
|
|
201
|
+
num_completion = int(u.get("completion_tokens") or 0)
|
|
202
|
+
# Match V2RequestSender: server count if present, else max_tokens cap.
|
|
203
|
+
num_generated = num_completion if num_completion > 0 else max_tokens
|
|
204
|
+
if first_token_t is None:
|
|
205
|
+
# Use total round-trip as a conservative TTFT approximation.
|
|
206
|
+
ttft_s = t1 - t0
|
|
207
|
+
decode_time = 0.0
|
|
208
|
+
else:
|
|
209
|
+
ttft_s = first_token_t - t0
|
|
210
|
+
decode_time = t1 - first_token_t
|
|
211
|
+
dt = t1 - t0
|
|
212
|
+
decoding_speed = (num_generated / decode_time) if decode_time > 0 else 0.0
|
|
213
|
+
tpot_s = (
|
|
214
|
+
(decode_time / num_generated) if num_generated > 0 and decode_time > 0 else 0.0
|
|
215
|
+
)
|
|
216
|
+
return {
|
|
217
|
+
"prompt_tokens": prompt_tokens,
|
|
218
|
+
"output_tokens": num_generated,
|
|
219
|
+
"ttft_ms": ttft_s * 1000.0,
|
|
220
|
+
"tpot_ms_per_token": tpot_s * 1000.0,
|
|
221
|
+
"total_latency_ms": dt * 1000.0,
|
|
222
|
+
"throughput_tokens_per_s": decoding_speed,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _missing_chat_template(exc: BaseException) -> bool:
|
|
227
|
+
msg = str(exc).lower()
|
|
228
|
+
return any(
|
|
229
|
+
s in msg
|
|
230
|
+
for s in (
|
|
231
|
+
"chat template",
|
|
232
|
+
"chat_template",
|
|
233
|
+
"chattemplate",
|
|
234
|
+
"template resolution",
|
|
235
|
+
"must provide a chat template",
|
|
236
|
+
"default chat template is no longer allowed",
|
|
237
|
+
)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _weak_completions_error(msg: str) -> bool:
|
|
242
|
+
msg = msg.lower()
|
|
243
|
+
return any(
|
|
244
|
+
s in msg
|
|
245
|
+
for s in (
|
|
246
|
+
"empty response from engine",
|
|
247
|
+
"no completion output from engine",
|
|
248
|
+
"no sse chunks parsed",
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class Request:
|
|
254
|
+
"""Build and send one query request against an OpenAI-compatible endpoint."""
|
|
255
|
+
|
|
256
|
+
def __init__(
|
|
257
|
+
self,
|
|
258
|
+
base: str,
|
|
259
|
+
model: Optional[str],
|
|
260
|
+
max_tokens: int,
|
|
261
|
+
timeout: float,
|
|
262
|
+
*,
|
|
263
|
+
completions_only: bool = False,
|
|
264
|
+
chat_first: bool = False,
|
|
265
|
+
) -> None:
|
|
266
|
+
self._base = base
|
|
267
|
+
self._model = model
|
|
268
|
+
self._max_tokens = max_tokens
|
|
269
|
+
self._timeout = timeout
|
|
270
|
+
self._completions_only = completions_only
|
|
271
|
+
self._chat_first = chat_first
|
|
272
|
+
|
|
273
|
+
def build_request(self, prompt: str) -> dict[str, Any]:
|
|
274
|
+
"""Build request payload and metadata for the provided prompt."""
|
|
275
|
+
model = self._model or self._first_model_id()
|
|
276
|
+
return {
|
|
277
|
+
"base": self._base,
|
|
278
|
+
"model": model,
|
|
279
|
+
"prompt": prompt,
|
|
280
|
+
"max_tokens": self._max_tokens,
|
|
281
|
+
"timeout": self._timeout,
|
|
282
|
+
"completions_only": self._completions_only,
|
|
283
|
+
"chat_first": self._chat_first,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
def send_request(self, prompt: str) -> dict[str, Any] | MetricMap:
|
|
287
|
+
"""Send request and return stats."""
|
|
288
|
+
request_data = self.build_request(prompt)
|
|
289
|
+
stats = {
|
|
290
|
+
"model": request_data["model"],
|
|
291
|
+
**self._query_with_fallback(request_data),
|
|
292
|
+
}
|
|
293
|
+
return {key: (_METRIC_NAMES.get(key, key), stats[key]) for key in stats}
|
|
294
|
+
|
|
295
|
+
def _first_model_id(self) -> str:
|
|
296
|
+
"""Return the first model ID from ``GET /v1/models``."""
|
|
297
|
+
obj = _read_json(_api_url(self._base, "models"), self._timeout)
|
|
298
|
+
data = obj.get("data")
|
|
299
|
+
if not isinstance(data, list) or not data:
|
|
300
|
+
raise RuntimeError(
|
|
301
|
+
"GET /v1/models returned no models; pass --model explicitly."
|
|
302
|
+
)
|
|
303
|
+
first = data[0]
|
|
304
|
+
if not isinstance(first, dict) or "id" not in first:
|
|
305
|
+
raise RuntimeError("GET /v1/models: first entry missing 'id'.")
|
|
306
|
+
return str(first["id"])
|
|
307
|
+
|
|
308
|
+
def _query_with_fallback(self, request_data: dict[str, Any]) -> dict[str, Any]:
|
|
309
|
+
"""Send one query and fallback between completions/chat endpoints."""
|
|
310
|
+
if request_data["completions_only"]:
|
|
311
|
+
return self._query(request_data, chat=False)
|
|
312
|
+
try:
|
|
313
|
+
return self._query(request_data, chat=request_data["chat_first"])
|
|
314
|
+
except RuntimeError as first_err:
|
|
315
|
+
if request_data["chat_first"]:
|
|
316
|
+
if not _missing_chat_template(first_err):
|
|
317
|
+
raise
|
|
318
|
+
_info(
|
|
319
|
+
"chat API failed (no chat template); retrying with /v1/completions"
|
|
320
|
+
)
|
|
321
|
+
return self._query(request_data, chat=False)
|
|
322
|
+
_info("/v1/completions failed; retrying with /v1/chat/completions")
|
|
323
|
+
try:
|
|
324
|
+
return self._query(request_data, chat=True)
|
|
325
|
+
except RuntimeError as second_err:
|
|
326
|
+
if _weak_completions_error(str(first_err)) and _missing_chat_template(
|
|
327
|
+
second_err
|
|
328
|
+
):
|
|
329
|
+
_info(
|
|
330
|
+
"base / completion-only models: try `--completions` or "
|
|
331
|
+
"an instruct model with a chat template."
|
|
332
|
+
)
|
|
333
|
+
raise second_err
|
|
334
|
+
raise RuntimeError(f"{first_err}; then {second_err}") from second_err
|
|
335
|
+
|
|
336
|
+
def _query(self, request_data: dict[str, Any], *, chat: bool) -> dict[str, Any]:
|
|
337
|
+
path = "chat/completions" if chat else "completions"
|
|
338
|
+
model = request_data["model"]
|
|
339
|
+
prompt = request_data["prompt"]
|
|
340
|
+
max_tokens = request_data["max_tokens"]
|
|
341
|
+
timeout = request_data["timeout"]
|
|
342
|
+
body = (
|
|
343
|
+
{
|
|
344
|
+
"model": model,
|
|
345
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
346
|
+
"max_tokens": max_tokens,
|
|
347
|
+
}
|
|
348
|
+
if chat
|
|
349
|
+
else {"model": model, "prompt": prompt, "max_tokens": max_tokens}
|
|
350
|
+
)
|
|
351
|
+
return _stream(
|
|
352
|
+
_api_url(request_data["base"], path),
|
|
353
|
+
body,
|
|
354
|
+
timeout,
|
|
355
|
+
chat=chat,
|
|
356
|
+
max_tokens=max_tokens,
|
|
357
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""``lmcache server`` — launch the LMCache server (ZMQ + HTTP)."""
|
|
3
|
+
|
|
4
|
+
# Standard
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
# First Party
|
|
8
|
+
from lmcache.cli.commands.base import BaseCommand
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ServerCommand(BaseCommand):
|
|
12
|
+
"""CLI command that launches the LMCache server (ZMQ + HTTP)."""
|
|
13
|
+
|
|
14
|
+
def name(self) -> str:
|
|
15
|
+
"""Return the subcommand name.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
The string ``"server"``.
|
|
19
|
+
"""
|
|
20
|
+
return "server"
|
|
21
|
+
|
|
22
|
+
def help(self) -> str:
|
|
23
|
+
"""Return short help text.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Help string shown by ``lmcache -h``.
|
|
27
|
+
"""
|
|
28
|
+
return "Launch the LMCache server (ZMQ + HTTP)."
|
|
29
|
+
|
|
30
|
+
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
|
|
31
|
+
"""Add server-specific arguments to the parser.
|
|
32
|
+
|
|
33
|
+
Composes argument groups from the multiprocess, storage manager,
|
|
34
|
+
HTTP frontend, Prometheus, and telemetry config modules.
|
|
35
|
+
Silently skips argument registration when server dependencies
|
|
36
|
+
(e.g. CUDA extensions) are not installed; ``execute`` will then
|
|
37
|
+
print an actionable error.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
parser: The ``ArgumentParser`` for this subcommand.
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
# First Party
|
|
44
|
+
from lmcache.v1.distributed.config import add_storage_manager_args
|
|
45
|
+
from lmcache.v1.mp_observability.config import add_observability_args
|
|
46
|
+
from lmcache.v1.multiprocess.config import (
|
|
47
|
+
add_http_frontend_args,
|
|
48
|
+
add_mp_server_args,
|
|
49
|
+
)
|
|
50
|
+
except ImportError as e:
|
|
51
|
+
print(
|
|
52
|
+
f"Failed to import server dependencies: {e}. "
|
|
53
|
+
"Install the full lmcache package to use 'lmcache server'."
|
|
54
|
+
)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
add_mp_server_args(parser)
|
|
58
|
+
add_storage_manager_args(parser)
|
|
59
|
+
add_http_frontend_args(parser)
|
|
60
|
+
add_observability_args(parser)
|
|
61
|
+
|
|
62
|
+
def execute(self, args: argparse.Namespace) -> None:
|
|
63
|
+
"""Parse CLI arguments into config objects and launch the HTTP server.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
args: Parsed CLI arguments.
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
SystemExit: When server dependencies are not installed.
|
|
70
|
+
"""
|
|
71
|
+
# Standard
|
|
72
|
+
import sys
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
# First Party
|
|
76
|
+
from lmcache.v1.distributed.config import parse_args_to_config
|
|
77
|
+
from lmcache.v1.mp_observability.config import (
|
|
78
|
+
parse_args_to_observability_config,
|
|
79
|
+
)
|
|
80
|
+
from lmcache.v1.multiprocess.config import (
|
|
81
|
+
parse_args_to_http_frontend_config,
|
|
82
|
+
parse_args_to_mp_server_config,
|
|
83
|
+
)
|
|
84
|
+
from lmcache.v1.multiprocess.http_server import run_http_server
|
|
85
|
+
except ImportError:
|
|
86
|
+
print(
|
|
87
|
+
"The 'lmcache server' command requires the full lmcache "
|
|
88
|
+
"installation with CUDA extensions.\n"
|
|
89
|
+
"Install with: pip install lmcache",
|
|
90
|
+
file=sys.stderr,
|
|
91
|
+
)
|
|
92
|
+
sys.exit(1)
|
|
93
|
+
|
|
94
|
+
run_http_server(
|
|
95
|
+
http_config=parse_args_to_http_frontend_config(args),
|
|
96
|
+
mp_config=parse_args_to_mp_server_config(args),
|
|
97
|
+
storage_manager_config=parse_args_to_config(args),
|
|
98
|
+
obs_config=parse_args_to_observability_config(args),
|
|
99
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""``lmcache tool`` command — offline analysis utilities.
|
|
3
|
+
|
|
4
|
+
To add a new tool:
|
|
5
|
+
|
|
6
|
+
1. Create ``lmcache/cli/commands/tool/<your_tool>.py`` with a ``register``
|
|
7
|
+
function that adds a sub-subcommand to the supplied ``subparsers``.
|
|
8
|
+
2. Import it here and call ``your_tool.register(inner)`` inside
|
|
9
|
+
:meth:`ToolCommand.register`.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Standard
|
|
13
|
+
import argparse
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
# First Party
|
|
17
|
+
from lmcache.cli.commands.base import BaseCommand
|
|
18
|
+
from lmcache.cli.commands.tool import cache_simulator
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ToolCommand(BaseCommand):
|
|
22
|
+
"""CLI command for offline analysis tools bundled with LMCache."""
|
|
23
|
+
|
|
24
|
+
def name(self) -> str:
|
|
25
|
+
"""Return the subcommand name."""
|
|
26
|
+
return "tool"
|
|
27
|
+
|
|
28
|
+
def help(self) -> str:
|
|
29
|
+
"""Return short help text shown by ``lmcache -h``."""
|
|
30
|
+
return "Run offline analysis tools."
|
|
31
|
+
|
|
32
|
+
def add_arguments(self, _parser: argparse.ArgumentParser) -> None:
|
|
33
|
+
"""No top-level arguments; all args are registered in register()."""
|
|
34
|
+
|
|
35
|
+
def register(self, subparsers: argparse._SubParsersAction) -> None:
|
|
36
|
+
"""Register ``lmcache tool`` and all tool sub-subcommands.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
subparsers: The subparsers action from the root parser.
|
|
40
|
+
"""
|
|
41
|
+
parser = subparsers.add_parser(
|
|
42
|
+
self.name(),
|
|
43
|
+
help=self.help(),
|
|
44
|
+
description="Run offline analysis tools bundled with LMCache.",
|
|
45
|
+
)
|
|
46
|
+
inner = parser.add_subparsers(
|
|
47
|
+
dest="tool_name",
|
|
48
|
+
required=True,
|
|
49
|
+
metavar="{cache-simulator}",
|
|
50
|
+
)
|
|
51
|
+
cache_simulator.register(inner)
|
|
52
|
+
|
|
53
|
+
def execute(self, args: argparse.Namespace) -> None:
|
|
54
|
+
"""Dispatch is handled per-tool via parser.set_defaults(func=...).
|
|
55
|
+
|
|
56
|
+
This method is never called directly; each tool's register() binds
|
|
57
|
+
its own execute function as the dispatch target.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
args: Parsed CLI arguments.
|
|
61
|
+
"""
|
|
62
|
+
print(f"Unknown tool: {getattr(args, 'tool_name', '?')}", file=sys.stderr)
|
|
63
|
+
sys.exit(1)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""``lmcache tool cache-simulator`` sub-subcommand wiring.
|
|
3
|
+
|
|
4
|
+
Registers the ``simulate``, ``sweep``, and ``gen-dataset`` actions under
|
|
5
|
+
``lmcache tool cache-simulator``. Flag definitions and execution logic
|
|
6
|
+
live entirely in the simulator modules:
|
|
7
|
+
|
|
8
|
+
* :func:`~lmcache.tools.cache_simulator.simulator.add_simulate_arguments`
|
|
9
|
+
* :func:`~lmcache.tools.cache_simulator.simulator.run_simulate`
|
|
10
|
+
* :func:`~lmcache.tools.cache_simulator.plot_hit_rate.add_sweep_arguments`
|
|
11
|
+
* :func:`~lmcache.tools.cache_simulator.plot_hit_rate.run_sweep`
|
|
12
|
+
* :func:`~lmcache.tools.cache_simulator.gen_bench_dataset.add_gen_dataset_arguments`
|
|
13
|
+
* :func:`~lmcache.tools.cache_simulator.gen_bench_dataset.run_gen_dataset`
|
|
14
|
+
|
|
15
|
+
To add a new action, add ``add_*_arguments`` / ``run_*`` functions to the
|
|
16
|
+
appropriate simulator module and wire them up in :func:`register`.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# Standard
|
|
20
|
+
import argparse
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def register(subparsers: argparse._SubParsersAction) -> None:
|
|
24
|
+
"""Register ``cache-simulator`` and its ``simulate``/``sweep`` actions.
|
|
25
|
+
|
|
26
|
+
Imports are lazy so that matplotlib is not loaded at CLI startup.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
subparsers: The subparsers action from the ``lmcache tool`` parser.
|
|
30
|
+
"""
|
|
31
|
+
# Lazy imports — keeps CLI startup fast and makes cache-simulator optional.
|
|
32
|
+
# Missing sortedcontainers/matplotlib (plot extra) skips the subcommand.
|
|
33
|
+
try:
|
|
34
|
+
# First Party
|
|
35
|
+
from lmcache.tools.cache_simulator.gen_bench_dataset import (
|
|
36
|
+
add_gen_dataset_arguments,
|
|
37
|
+
)
|
|
38
|
+
from lmcache.tools.cache_simulator.plot_hit_rate import add_sweep_arguments
|
|
39
|
+
from lmcache.tools.cache_simulator.simulator import add_simulate_arguments
|
|
40
|
+
except ImportError:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
cs_parser = subparsers.add_parser(
|
|
44
|
+
"cache-simulator",
|
|
45
|
+
help="Simulate KV-cache token hit rate from lookup-hash JSONL logs.",
|
|
46
|
+
description=(
|
|
47
|
+
"Replay LMCache lookup-hash JSONL logs through an LRU cache "
|
|
48
|
+
"to measure token hit rate."
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
cs_sub = cs_parser.add_subparsers(
|
|
52
|
+
dest="cs_action",
|
|
53
|
+
required=True,
|
|
54
|
+
metavar="{simulate,sweep,gen-dataset}",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
sim_parser = cs_sub.add_parser(
|
|
58
|
+
"simulate",
|
|
59
|
+
help=(
|
|
60
|
+
"Replay logs at a fixed cache capacity; print a text report "
|
|
61
|
+
"and save a 7-panel statistics PNG."
|
|
62
|
+
),
|
|
63
|
+
)
|
|
64
|
+
add_simulate_arguments(sim_parser)
|
|
65
|
+
sim_parser.set_defaults(func=execute)
|
|
66
|
+
|
|
67
|
+
sweep_parser = cs_sub.add_parser(
|
|
68
|
+
"sweep",
|
|
69
|
+
help=(
|
|
70
|
+
"Sweep across a range of cache capacities and save a "
|
|
71
|
+
"hit-rate vs capacity PNG."
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
add_sweep_arguments(sweep_parser)
|
|
75
|
+
sweep_parser.set_defaults(func=execute)
|
|
76
|
+
|
|
77
|
+
gen_parser = cs_sub.add_parser(
|
|
78
|
+
"gen-dataset",
|
|
79
|
+
help=(
|
|
80
|
+
"Generate a vllm bench serve custom dataset (JSONL) from "
|
|
81
|
+
"lookup-hash JSONL logs, preserving prefix-sharing structure."
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
add_gen_dataset_arguments(gen_parser)
|
|
85
|
+
gen_parser.set_defaults(func=execute)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def execute(args: argparse.Namespace) -> None:
|
|
89
|
+
"""Dispatch to the correct cache-simulator action.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
args: Parsed CLI arguments (includes ``cs_action``).
|
|
93
|
+
"""
|
|
94
|
+
# Standard
|
|
95
|
+
import sys
|
|
96
|
+
|
|
97
|
+
# First Party
|
|
98
|
+
# Lazy imports — keeps CLI startup fast (avoids loading matplotlib)
|
|
99
|
+
from lmcache.tools.cache_simulator.plot_hit_rate import run_sweep
|
|
100
|
+
from lmcache.tools.cache_simulator.simulator import run_simulate
|
|
101
|
+
|
|
102
|
+
if args.cs_action == "simulate":
|
|
103
|
+
run_simulate(args)
|
|
104
|
+
elif args.cs_action == "sweep":
|
|
105
|
+
run_sweep(args)
|
|
106
|
+
elif args.cs_action == "gen-dataset":
|
|
107
|
+
# First Party
|
|
108
|
+
from lmcache.tools.cache_simulator.gen_bench_dataset import run_gen_dataset
|
|
109
|
+
|
|
110
|
+
run_gen_dataset(args)
|
|
111
|
+
else:
|
|
112
|
+
print(f"Unknown cache-simulator action: {args.cs_action}", file=sys.stderr)
|
|
113
|
+
sys.exit(1)
|