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,895 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import Annotated, Any, Callable, List, Optional, Tuple
|
|
4
|
+
import asyncio
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
# Third Party
|
|
10
|
+
from fastapi import APIRouter, Query
|
|
11
|
+
from starlette.requests import Request
|
|
12
|
+
from starlette.responses import PlainTextResponse
|
|
13
|
+
import torch
|
|
14
|
+
|
|
15
|
+
# First Party
|
|
16
|
+
from lmcache.logging import init_logger
|
|
17
|
+
from lmcache.utils import (
|
|
18
|
+
compress_slot_mapping,
|
|
19
|
+
parse_mixed_slot_mapping,
|
|
20
|
+
)
|
|
21
|
+
from lmcache.v1.cache_engine import LMCacheEngine
|
|
22
|
+
|
|
23
|
+
logger = init_logger(__name__)
|
|
24
|
+
|
|
25
|
+
router = APIRouter()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _parse_tokens_from_params(
|
|
29
|
+
tokens_mock: Optional[str],
|
|
30
|
+
) -> Tuple[Optional[List[int]], Optional[dict]]:
|
|
31
|
+
"""Parse tokens from input parameters.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
tokens_mock: Two comma-separated numbers specifying start and end of token range
|
|
35
|
+
- Example: "0,100" generates tokens [0, 1, 2, ..., 99]
|
|
36
|
+
- Example: "50,75" generates tokens [50, 51, 52, ..., 74]
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Tuple of (tokens list, error dict).
|
|
40
|
+
If error dict is not None, tokens will be None.
|
|
41
|
+
"""
|
|
42
|
+
# TODO(baoloongmao): Add support for tokens_input parameter to read tokens from file
|
|
43
|
+
if tokens_mock:
|
|
44
|
+
try:
|
|
45
|
+
parts = tokens_mock.split(",")
|
|
46
|
+
if len(parts) != 2:
|
|
47
|
+
raise ValueError("tokens_mock must contain exactly 2 numbers")
|
|
48
|
+
start, end = int(parts[0].strip()), int(parts[1].strip())
|
|
49
|
+
if start >= end:
|
|
50
|
+
raise ValueError("start must be less than end")
|
|
51
|
+
tokens = list(range(start, end))
|
|
52
|
+
return tokens, None
|
|
53
|
+
except ValueError as e:
|
|
54
|
+
return None, {
|
|
55
|
+
"error": "Invalid tokens_mock format",
|
|
56
|
+
"message": f"tokens_mock must be 'start,end': {str(e)}",
|
|
57
|
+
}
|
|
58
|
+
else:
|
|
59
|
+
return None, {
|
|
60
|
+
"error": "Missing parameters",
|
|
61
|
+
"message": "Must specify either tokens_input or tokens_mock",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _create_error_response(error_info: dict, status_code: int) -> PlainTextResponse:
|
|
66
|
+
"""Create a standardized error response.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
error_info: Dictionary containing error information
|
|
70
|
+
status_code: HTTP status code
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
PlainTextResponse with error information
|
|
74
|
+
"""
|
|
75
|
+
return PlainTextResponse(
|
|
76
|
+
content=json.dumps(error_info, indent=2),
|
|
77
|
+
media_type="application/json",
|
|
78
|
+
status_code=status_code,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _check_lmcache_engine(
|
|
83
|
+
request: Request,
|
|
84
|
+
) -> Tuple[Optional["LMCacheEngine"], Optional[PlainTextResponse]]:
|
|
85
|
+
"""Check if LMCache engine is available.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
request: FastAPI request object
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Tuple of (lmcache_engine, error_response).
|
|
92
|
+
If error_response is not None, engine will be None.
|
|
93
|
+
"""
|
|
94
|
+
lmcache_adapter = request.app.state.lmcache_adapter
|
|
95
|
+
lmcache_engine = getattr(lmcache_adapter, "lmcache_engine", None)
|
|
96
|
+
if not lmcache_engine:
|
|
97
|
+
error_info = {
|
|
98
|
+
"error": "LMCache API is unavailable",
|
|
99
|
+
"message": "LMCache engine not configured.",
|
|
100
|
+
}
|
|
101
|
+
return None, _create_error_response(error_info, 503)
|
|
102
|
+
return lmcache_engine, None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _get_kvcaches_and_device(engine):
|
|
106
|
+
"""Get kvcaches and device from engine's gpu_connector.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
engine: LMCache engine instance
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Tuple of (kvcaches, device).
|
|
113
|
+
kvcaches may be None if not available.
|
|
114
|
+
device defaults to "cpu" if kvcaches not available.
|
|
115
|
+
"""
|
|
116
|
+
kvcaches = None
|
|
117
|
+
device = "cpu" # Default device
|
|
118
|
+
|
|
119
|
+
if engine.gpu_connector:
|
|
120
|
+
kvcaches = engine.gpu_connector.kvcaches
|
|
121
|
+
if kvcaches is not None and len(kvcaches) > 0:
|
|
122
|
+
device = kvcaches[0].device
|
|
123
|
+
logger.debug(f"Using kvcaches device: {device}")
|
|
124
|
+
else:
|
|
125
|
+
logger.warning(
|
|
126
|
+
"gpu_connector.kvcaches is None or empty. "
|
|
127
|
+
"Make sure post_init was called with kvcaches."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return kvcaches, device
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _compute_tensor_checksum(tensor: torch.Tensor) -> str:
|
|
134
|
+
"""Compute MD5 checksum of a tensor."""
|
|
135
|
+
# Move to CPU and convert to bytes for hashing
|
|
136
|
+
# Handle BFloat16 which is not supported by numpy
|
|
137
|
+
if tensor.dtype == torch.bfloat16:
|
|
138
|
+
# Convert bfloat16 to float32 for numpy compatibility
|
|
139
|
+
tensor = tensor.to(torch.float32)
|
|
140
|
+
|
|
141
|
+
tensor_bytes = tensor.detach().cpu().contiguous().numpy().tobytes()
|
|
142
|
+
return hashlib.md5(tensor_bytes).hexdigest()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _slice_by_slot_dim(
|
|
146
|
+
kv_at_slots: torch.Tensor, start_idx: int, end_idx: int
|
|
147
|
+
) -> torch.Tensor:
|
|
148
|
+
"""Slice tensor by slot dimension based on tensor ndim."""
|
|
149
|
+
if kv_at_slots.ndim == 4:
|
|
150
|
+
# MHA: [2, num_slots, num_heads, head_size]
|
|
151
|
+
return kv_at_slots[:, start_idx:end_idx, :, :]
|
|
152
|
+
elif kv_at_slots.ndim == 3:
|
|
153
|
+
# 4D format: [num_slots, num_heads, head_size]
|
|
154
|
+
return kv_at_slots[start_idx:end_idx, :, :]
|
|
155
|
+
else:
|
|
156
|
+
# MLA: [num_slots, head_size]
|
|
157
|
+
return kv_at_slots[start_idx:end_idx, :]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _extract_kv_at_slots(
|
|
161
|
+
kv_tensor: torch.Tensor, slot_tensor: torch.Tensor
|
|
162
|
+
) -> torch.Tensor:
|
|
163
|
+
"""Extract KV data at specified slot positions from kv_tensor.
|
|
164
|
+
|
|
165
|
+
Handles different kv_tensor formats:
|
|
166
|
+
- MHA (5D): [2, num_blocks, block_size, num_heads, head_size]
|
|
167
|
+
- MLA (3D): [num_blocks, block_size, head_size]
|
|
168
|
+
|
|
169
|
+
The slot_mapping is calculated as:
|
|
170
|
+
slot_idx = block_id * block_size + block_offset
|
|
171
|
+
|
|
172
|
+
This means we can reshape the tensor to flatten (num_blocks, block_size)
|
|
173
|
+
into a single slot dimension and index directly.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
kv_tensor: The KV cache tensor for a single layer.
|
|
177
|
+
slot_tensor: Tensor of slot indices to extract.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Tensor with KV data at the specified slots.
|
|
181
|
+
- MHA: shape [2, num_slots, num_heads, head_size]
|
|
182
|
+
- MLA: shape [num_slots, head_size]
|
|
183
|
+
"""
|
|
184
|
+
ndim = kv_tensor.ndim
|
|
185
|
+
|
|
186
|
+
if ndim == 5:
|
|
187
|
+
# MHA format: [2, num_blocks, block_size, num_heads, head_size]
|
|
188
|
+
# Reshape to [2, num_blocks * block_size, num_heads, head_size]
|
|
189
|
+
# then index by slot_tensor on dimension 1
|
|
190
|
+
kv_2d = 2
|
|
191
|
+
num_heads = kv_tensor.shape[3]
|
|
192
|
+
head_size = kv_tensor.shape[4]
|
|
193
|
+
kv_reshaped = kv_tensor.reshape(kv_2d, -1, num_heads, head_size)
|
|
194
|
+
return kv_reshaped[:, slot_tensor, :, :]
|
|
195
|
+
elif ndim == 3:
|
|
196
|
+
# MLA format: [num_blocks, block_size, head_size]
|
|
197
|
+
# Reshape to [num_blocks * block_size, head_size]
|
|
198
|
+
head_size = kv_tensor.shape[2]
|
|
199
|
+
kv_reshaped = kv_tensor.reshape(-1, head_size)
|
|
200
|
+
return kv_reshaped[slot_tensor, :]
|
|
201
|
+
elif ndim == 4:
|
|
202
|
+
# Alternative format: [num_blocks, block_size, num_heads, head_size]
|
|
203
|
+
# (used in some test cases)
|
|
204
|
+
num_heads = kv_tensor.shape[2]
|
|
205
|
+
head_size = kv_tensor.shape[3]
|
|
206
|
+
kv_reshaped = kv_tensor.reshape(-1, num_heads, head_size)
|
|
207
|
+
return kv_reshaped[slot_tensor, :, :]
|
|
208
|
+
else:
|
|
209
|
+
# Fallback: try the original approach
|
|
210
|
+
logger.warning(
|
|
211
|
+
"Unknown kv_tensor ndim=%d, shape=%s. Using fallback indexing.",
|
|
212
|
+
ndim,
|
|
213
|
+
kv_tensor.shape,
|
|
214
|
+
)
|
|
215
|
+
return kv_tensor.view(-1, *kv_tensor.shape[2:])[slot_tensor]
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def compute_kvcache_checksums(
|
|
219
|
+
lmcache_adapter,
|
|
220
|
+
slot_indices: list[int],
|
|
221
|
+
chunk_size: Optional[int] = None,
|
|
222
|
+
layerwise: bool = False,
|
|
223
|
+
) -> Optional[dict[str, Any]]:
|
|
224
|
+
"""Compute MD5 checksums for kvcaches at specified slot positions.
|
|
225
|
+
|
|
226
|
+
This method is used by the kvcache check API to verify that stored
|
|
227
|
+
and retrieved kvcaches are identical.
|
|
228
|
+
|
|
229
|
+
The slot_mapping is calculated in vllm_v1_adapter.py as:
|
|
230
|
+
slot_idx = block_id * block_size + block_offset
|
|
231
|
+
|
|
232
|
+
For vLLM kv_cache formats:
|
|
233
|
+
- MHA (5D): [2, num_blocks, block_size, num_heads, head_size]
|
|
234
|
+
- MLA (3D): [num_blocks, block_size, head_size]
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
lmcache_adapter: The LMCache adapter containing kv_caches.
|
|
238
|
+
slot_indices: List of slot indices to compute checksums for.
|
|
239
|
+
chunk_size: Optional chunk size for computing per-chunk checksums.
|
|
240
|
+
If provided, will compute checksums for each chunk.
|
|
241
|
+
layerwise: If True, output per-layer checksums for each chunk.
|
|
242
|
+
If False (default), output one checksum per chunk (all layers combined).
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Dictionary containing:
|
|
246
|
+
- 'chunk_checksums': (if layerwise=True) dict mapping layer names to
|
|
247
|
+
list of per-chunk checksums
|
|
248
|
+
- 'chunk_checksums': (if layerwise=False) list of checksums, one per chunk
|
|
249
|
+
(each checksum covers all layers for that chunk)
|
|
250
|
+
Returns None if kv_caches is not available.
|
|
251
|
+
"""
|
|
252
|
+
if not lmcache_adapter.kv_caches:
|
|
253
|
+
logger.warning("kv_caches is empty, cannot compute checksums")
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
if chunk_size is None or chunk_size <= 0:
|
|
257
|
+
return {"chunk_checksums": [], "chunk_size": chunk_size, "num_chunks": 0}
|
|
258
|
+
|
|
259
|
+
num_slots = len(slot_indices)
|
|
260
|
+
num_chunks = (num_slots + chunk_size - 1) // chunk_size
|
|
261
|
+
|
|
262
|
+
# Pre-extract all layer data at slot positions
|
|
263
|
+
layer_data_at_slots: dict[str, torch.Tensor] = {}
|
|
264
|
+
for layer_name, kv_tensor in lmcache_adapter.kv_caches.items():
|
|
265
|
+
try:
|
|
266
|
+
slot_tensor = torch.tensor(
|
|
267
|
+
slot_indices, dtype=torch.long, device=kv_tensor.device
|
|
268
|
+
)
|
|
269
|
+
layer_data_at_slots[layer_name] = _extract_kv_at_slots(
|
|
270
|
+
kv_tensor, slot_tensor
|
|
271
|
+
)
|
|
272
|
+
except Exception as e:
|
|
273
|
+
logger.error("Failed to extract data for layer %s: %s", layer_name, str(e))
|
|
274
|
+
layer_data_at_slots[layer_name] = None # type: ignore
|
|
275
|
+
|
|
276
|
+
if layerwise:
|
|
277
|
+
# Output per-layer checksums for each chunk: {layer_name: [checksum1, ...]}
|
|
278
|
+
chunk_checksums: dict[str, list[str]] = {}
|
|
279
|
+
for layer_name, kv_at_slots in layer_data_at_slots.items():
|
|
280
|
+
if kv_at_slots is None:
|
|
281
|
+
chunk_checksums[layer_name] = ["error"] * num_chunks
|
|
282
|
+
continue
|
|
283
|
+
chunk_checksum_list: list[str] = []
|
|
284
|
+
for chunk_idx in range(num_chunks):
|
|
285
|
+
start_idx = chunk_idx * chunk_size
|
|
286
|
+
end_idx = min(start_idx + chunk_size, num_slots)
|
|
287
|
+
chunk_data = _slice_by_slot_dim(kv_at_slots, start_idx, end_idx)
|
|
288
|
+
chunk_checksum_list.append(_compute_tensor_checksum(chunk_data))
|
|
289
|
+
chunk_checksums[layer_name] = chunk_checksum_list
|
|
290
|
+
return {
|
|
291
|
+
"chunk_checksums": chunk_checksums,
|
|
292
|
+
"chunk_size": chunk_size,
|
|
293
|
+
"num_chunks": num_chunks,
|
|
294
|
+
}
|
|
295
|
+
else:
|
|
296
|
+
# Output one checksum per chunk (all layers combined): [checksum1, ...]
|
|
297
|
+
chunk_checksums_list: list[str] = []
|
|
298
|
+
for chunk_idx in range(num_chunks):
|
|
299
|
+
start_idx = chunk_idx * chunk_size
|
|
300
|
+
end_idx = min(start_idx + chunk_size, num_slots)
|
|
301
|
+
md5_hash = hashlib.md5()
|
|
302
|
+
for layer_name in sorted(layer_data_at_slots.keys()):
|
|
303
|
+
kv_at_slots = layer_data_at_slots[layer_name]
|
|
304
|
+
if kv_at_slots is None:
|
|
305
|
+
continue
|
|
306
|
+
chunk_data = _slice_by_slot_dim(kv_at_slots, start_idx, end_idx)
|
|
307
|
+
md5_hash.update(_compute_tensor_checksum(chunk_data).encode())
|
|
308
|
+
chunk_checksums_list.append(md5_hash.hexdigest())
|
|
309
|
+
return {
|
|
310
|
+
"chunk_checksums": chunk_checksums_list,
|
|
311
|
+
"chunk_size": chunk_size,
|
|
312
|
+
"num_chunks": num_chunks,
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@router.delete("/cache/clear")
|
|
317
|
+
async def clear(
|
|
318
|
+
request: Request,
|
|
319
|
+
locations: Annotated[Optional[List[str]], Query()] = None,
|
|
320
|
+
request_configs: Optional[dict] = None,
|
|
321
|
+
):
|
|
322
|
+
"""Clear cached data from the LMCache engine.
|
|
323
|
+
|
|
324
|
+
This endpoint provides a way to clear cached KV (Key-Value) data from the
|
|
325
|
+
LMCache engine. It can clear all cached data or selectively clear data
|
|
326
|
+
from specific storage locations.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
request (Request): The FastAPI request object containing application state.
|
|
330
|
+
locations (Optional[List[str]], optional): List of storage backend locations
|
|
331
|
+
to clear cache from. If None, clears from all available locations.
|
|
332
|
+
Common values include ["LocalCPUBackend", "LocalDiskBackend"].
|
|
333
|
+
Defaults to None.
|
|
334
|
+
request_configs (Optional[dict], optional): Additional configuration
|
|
335
|
+
parameters for the clear operation. Currently unused but reserved
|
|
336
|
+
for future extensions. Defaults to None.
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
PlainTextResponse: A plain text response
|
|
340
|
+
|
|
341
|
+
Example:
|
|
342
|
+
Clear all cached data:
|
|
343
|
+
```bash
|
|
344
|
+
curl -X DELETE "http://localhost:8000/cache/clear"
|
|
345
|
+
# Response: {"status": "success", "num_removed": 10,
|
|
346
|
+
# "locations": null, "request_configs": null}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Clear cache from specific locations:
|
|
350
|
+
```bash
|
|
351
|
+
curl -X DELETE "http://localhost:8000/cache/clear?locations=LocalCPUBackend&locations=LocalDiskBackend"
|
|
352
|
+
# Response: {"status": "success", "num_removed": 5,
|
|
353
|
+
# "locations": ["LocalCPUBackend", "LocalDiskBackend"],
|
|
354
|
+
# "request_configs": null}
|
|
355
|
+
```
|
|
356
|
+
"""
|
|
357
|
+
try:
|
|
358
|
+
lmcache_engine, error_response = _check_lmcache_engine(request)
|
|
359
|
+
if error_response:
|
|
360
|
+
return error_response
|
|
361
|
+
|
|
362
|
+
assert lmcache_engine is not None
|
|
363
|
+
num_removed = lmcache_engine.clear( # type: ignore[attr-defined]
|
|
364
|
+
locations=locations, request_configs=request_configs
|
|
365
|
+
)
|
|
366
|
+
success_info = {
|
|
367
|
+
"status": "success",
|
|
368
|
+
"num_removed": num_removed,
|
|
369
|
+
}
|
|
370
|
+
return PlainTextResponse(
|
|
371
|
+
content=json.dumps(success_info, indent=2),
|
|
372
|
+
media_type="application/json",
|
|
373
|
+
)
|
|
374
|
+
except Exception as e:
|
|
375
|
+
error_info = {"error": "Failed to clear cache", "message": str(e)}
|
|
376
|
+
return _create_error_response(error_info, 500)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _process_tokens_request(
|
|
380
|
+
request: Request,
|
|
381
|
+
tokens_mock: Optional[str],
|
|
382
|
+
) -> Tuple[Optional[object], Optional[List[int]], Optional[PlainTextResponse]]:
|
|
383
|
+
"""Process tokens request and validate parameters.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
request: FastAPI request object
|
|
387
|
+
tokens_mock: Mock token range specification
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
Tuple of (lmcache_engine, tokens, error_response).
|
|
391
|
+
If error_response is not None, the other values will be None.
|
|
392
|
+
"""
|
|
393
|
+
lmcache_engine, error_response = _check_lmcache_engine(request)
|
|
394
|
+
if error_response:
|
|
395
|
+
return None, None, error_response
|
|
396
|
+
|
|
397
|
+
tokens, error_info = _parse_tokens_from_params(tokens_mock)
|
|
398
|
+
if error_info:
|
|
399
|
+
status_code = 400 if error_info["error"] != "File not found" else 404
|
|
400
|
+
return None, None, _create_error_response(error_info, status_code)
|
|
401
|
+
|
|
402
|
+
return lmcache_engine, tokens, None
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def _execute_cache_operation(
|
|
406
|
+
operation_name: str,
|
|
407
|
+
operation_func: Callable,
|
|
408
|
+
lmcache_engine: object,
|
|
409
|
+
tokens: List[int],
|
|
410
|
+
) -> PlainTextResponse:
|
|
411
|
+
"""Execute a cache operation and return standardized response.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
operation_name: Name of the operation for error messages
|
|
415
|
+
operation_func: Function to execute the operation
|
|
416
|
+
lmcache_engine: LMCache engine instance
|
|
417
|
+
tokens: List of token IDs
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
PlainTextResponse with operation result
|
|
421
|
+
"""
|
|
422
|
+
try:
|
|
423
|
+
result = operation_func(lmcache_engine, tokens)
|
|
424
|
+
success_info = {
|
|
425
|
+
"status": "success",
|
|
426
|
+
"num_tokens": len(tokens),
|
|
427
|
+
}
|
|
428
|
+
if result is not None:
|
|
429
|
+
success_info.update(result)
|
|
430
|
+
return PlainTextResponse(
|
|
431
|
+
content=json.dumps(success_info, indent=2),
|
|
432
|
+
media_type="application/json",
|
|
433
|
+
)
|
|
434
|
+
except Exception as e:
|
|
435
|
+
# Log the full traceback for debugging
|
|
436
|
+
tb_str = traceback.format_exc()
|
|
437
|
+
logger.error(f"Failed to {operation_name}: {str(e)}\\n{tb_str}")
|
|
438
|
+
|
|
439
|
+
# Include more detailed error info in response
|
|
440
|
+
error_message = str(e) if str(e) else f"Exception type: {type(e).__name__}"
|
|
441
|
+
error_info = {
|
|
442
|
+
"error": f"Failed to {operation_name}",
|
|
443
|
+
"message": error_message,
|
|
444
|
+
"exception_type": type(e).__name__,
|
|
445
|
+
}
|
|
446
|
+
return _create_error_response(error_info, 500)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@router.post("/cache/store")
|
|
450
|
+
async def store(
|
|
451
|
+
request: Request,
|
|
452
|
+
tokens_mock: Optional[str] = None,
|
|
453
|
+
):
|
|
454
|
+
"""Store KV cache data into the LMCache engine.
|
|
455
|
+
|
|
456
|
+
This endpoint provides a way to store KV cache data by generating mock tokens.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
request (Request): The FastAPI request object containing application state.
|
|
460
|
+
tokens_mock (Optional[str], optional): Two comma-separated numbers specifying
|
|
461
|
+
the start and end of a token range. Example: "0,100" generates tokens
|
|
462
|
+
from 0 to 99. Defaults to None.
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
PlainTextResponse: A plain text response with operation status
|
|
466
|
+
|
|
467
|
+
Example:
|
|
468
|
+
Store with mock tokens:
|
|
469
|
+
```bash
|
|
470
|
+
curl -X POST "http://localhost:8000/cache/store?tokens_mock=0,100"
|
|
471
|
+
# Response: {"status": "success", "num_tokens": 100}
|
|
472
|
+
```
|
|
473
|
+
"""
|
|
474
|
+
lmcache_engine, tokens, error_response = _process_tokens_request(
|
|
475
|
+
request, tokens_mock
|
|
476
|
+
)
|
|
477
|
+
if error_response:
|
|
478
|
+
return error_response
|
|
479
|
+
|
|
480
|
+
assert tokens is not None
|
|
481
|
+
assert lmcache_engine is not None
|
|
482
|
+
|
|
483
|
+
def _store_operation(engine, token_list):
|
|
484
|
+
# Get kvcaches and device using the shared function
|
|
485
|
+
kvcaches, device = _get_kvcaches_and_device(engine)
|
|
486
|
+
|
|
487
|
+
# Create slot mapping for the tokens
|
|
488
|
+
slot_mapping = torch.arange(len(token_list), dtype=torch.long, device=device)
|
|
489
|
+
|
|
490
|
+
logger.debug(
|
|
491
|
+
f"Storing {len(token_list)} tokens with slot_mapping on device {device}"
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
engine.store(
|
|
495
|
+
req_id="cache_api_store",
|
|
496
|
+
tokens=token_list,
|
|
497
|
+
slot_mapping=slot_mapping,
|
|
498
|
+
kvcaches=kvcaches,
|
|
499
|
+
)
|
|
500
|
+
return None
|
|
501
|
+
|
|
502
|
+
return _execute_cache_operation(
|
|
503
|
+
"store cache", _store_operation, lmcache_engine, tokens
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
@router.post("/cache/retrieve")
|
|
508
|
+
async def retrieve(
|
|
509
|
+
request: Request,
|
|
510
|
+
tokens_mock: Optional[str] = None,
|
|
511
|
+
):
|
|
512
|
+
"""Retrieve KV cache data from the LMCache engine.
|
|
513
|
+
|
|
514
|
+
This endpoint provides a way to retrieve KV cache data by generating mock tokens.
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
request (Request): The FastAPI request object containing application state.
|
|
518
|
+
tokens_mock (Optional[str], optional): Two comma-separated numbers specifying
|
|
519
|
+
the start and end of a token range. Example: "0,100" generates tokens
|
|
520
|
+
from 0 to 99. Defaults to None.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
PlainTextResponse: A plain text response with operation status
|
|
524
|
+
|
|
525
|
+
Example:
|
|
526
|
+
Retrieve with mock tokens:
|
|
527
|
+
```bash
|
|
528
|
+
curl -X POST "http://localhost:8000/cache/retrieve?tokens_mock=0,100"
|
|
529
|
+
# Response: {"status": "success", "num_tokens": 100, "num_retrieved": 80}
|
|
530
|
+
```
|
|
531
|
+
"""
|
|
532
|
+
lmcache_engine, tokens, error_response = _process_tokens_request(
|
|
533
|
+
request, tokens_mock
|
|
534
|
+
)
|
|
535
|
+
if error_response:
|
|
536
|
+
return error_response
|
|
537
|
+
|
|
538
|
+
assert tokens is not None
|
|
539
|
+
assert lmcache_engine is not None
|
|
540
|
+
|
|
541
|
+
def _retrieve_operation(engine, token_list):
|
|
542
|
+
# Get kvcaches and device using the shared function
|
|
543
|
+
kvcaches, device = _get_kvcaches_and_device(engine)
|
|
544
|
+
|
|
545
|
+
# Create slot_mapping for retrieve operation
|
|
546
|
+
slot_mapping = torch.arange(len(token_list), dtype=torch.long, device=device)
|
|
547
|
+
|
|
548
|
+
logger.debug(
|
|
549
|
+
"Retrieving %d tokens with slot_mapping on device %s",
|
|
550
|
+
len(token_list),
|
|
551
|
+
device,
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
ret_mask = engine.retrieve(
|
|
555
|
+
req_id="cache_api_retrieve",
|
|
556
|
+
tokens=token_list,
|
|
557
|
+
slot_mapping=slot_mapping,
|
|
558
|
+
kvcaches=kvcaches,
|
|
559
|
+
)
|
|
560
|
+
num_retrieved = int(ret_mask.sum().item())
|
|
561
|
+
return {"num_retrieved": num_retrieved}
|
|
562
|
+
|
|
563
|
+
return _execute_cache_operation(
|
|
564
|
+
"retrieve cache", _retrieve_operation, lmcache_engine, tokens
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
@router.get("/cache/kvcache/check")
|
|
569
|
+
async def kvcache_check(
|
|
570
|
+
request: Request,
|
|
571
|
+
slot_mapping: Optional[str] = None,
|
|
572
|
+
chunk_size: Optional[int] = None,
|
|
573
|
+
layerwise: bool = False,
|
|
574
|
+
):
|
|
575
|
+
"""Compute checksum for kvcaches at specified slot_mapping positions.
|
|
576
|
+
|
|
577
|
+
This endpoint is used to verify that stored and retrieved kvcaches are identical.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
request (Request): The FastAPI request object containing application state.
|
|
581
|
+
slot_mapping (Optional[str], optional): Slot indices in comma-separated format,
|
|
582
|
+
supports single numbers and range expressions.
|
|
583
|
+
Examples: "0,1,2,3", "1,2,3,[9,12],17,19". Defaults to None.
|
|
584
|
+
chunk_size (Optional[int], optional): Chunk size for computing checksums.
|
|
585
|
+
Each chunk contains `chunk_size` slots. Required parameter.
|
|
586
|
+
layerwise (bool, optional): If True, output per-layer checksums for each chunk.
|
|
587
|
+
If False (default), output one checksum per chunk (all layers combined).
|
|
588
|
+
|
|
589
|
+
Returns:
|
|
590
|
+
PlainTextResponse: A JSON response containing checksums.
|
|
591
|
+
|
|
592
|
+
Example:
|
|
593
|
+
```bash
|
|
594
|
+
# layerwise=false (default): one checksum per chunk (all layers combined)
|
|
595
|
+
curl -X GET "http://localhost:8000/cache/kvcache/check?slot_mapping=0,1,2,3&chunk_size=2"
|
|
596
|
+
# Response: {
|
|
597
|
+
# "status": "success",
|
|
598
|
+
# "slot_mapping_ranges": [[0, 3]],
|
|
599
|
+
# "chunk_size": 2,
|
|
600
|
+
# "num_chunks": 2,
|
|
601
|
+
# "chunk_checksums": ["checksum_chunk0", "checksum_chunk1"],
|
|
602
|
+
# "layerwise": false
|
|
603
|
+
# }
|
|
604
|
+
|
|
605
|
+
# layerwise=true: per-layer checksums for each chunk
|
|
606
|
+
curl -X GET "http://localhost:8000/cache/kvcache/check?slot_mapping=0,1,2,3&chunk_size=2&layerwise=true"
|
|
607
|
+
# Response: {
|
|
608
|
+
# "status": "success",
|
|
609
|
+
# "slot_mapping_ranges": [[0, 3]],
|
|
610
|
+
# "chunk_size": 2,
|
|
611
|
+
# "num_chunks": 2,
|
|
612
|
+
# "chunk_checksums": {
|
|
613
|
+
# "layer_0": ["checksum_chunk0", "checksum_chunk1"],
|
|
614
|
+
# "layer_1": ["checksum_chunk0", "checksum_chunk1"],
|
|
615
|
+
# },
|
|
616
|
+
# "layerwise": true
|
|
617
|
+
# }
|
|
618
|
+
```
|
|
619
|
+
"""
|
|
620
|
+
try:
|
|
621
|
+
lmcache_adapter = request.app.state.lmcache_adapter
|
|
622
|
+
if not lmcache_adapter:
|
|
623
|
+
return _create_error_response(
|
|
624
|
+
{
|
|
625
|
+
"error": "LMCache adapter unavailable",
|
|
626
|
+
"message": "LMCache adapter not configured.",
|
|
627
|
+
},
|
|
628
|
+
503,
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
if not slot_mapping:
|
|
632
|
+
return _create_error_response(
|
|
633
|
+
{
|
|
634
|
+
"error": "Missing parameters",
|
|
635
|
+
"message": "slot_mapping parameter is required",
|
|
636
|
+
},
|
|
637
|
+
400,
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
# Parse slot_mapping from mixed format string
|
|
641
|
+
# (supports single numbers and ranges)
|
|
642
|
+
slot_indices, error_info = parse_mixed_slot_mapping(slot_mapping)
|
|
643
|
+
if error_info:
|
|
644
|
+
return _create_error_response(error_info, 400)
|
|
645
|
+
|
|
646
|
+
# slot_indices is guaranteed to be non-None when error_info is None
|
|
647
|
+
assert slot_indices is not None
|
|
648
|
+
|
|
649
|
+
# Validate slot indices are within valid range
|
|
650
|
+
if lmcache_adapter.kv_caches:
|
|
651
|
+
# Get the first kv_tensor to check dimensions
|
|
652
|
+
first_kv_tensor = next(iter(lmcache_adapter.kv_caches.values()))
|
|
653
|
+
# Calculate total slots: num_blocks * block_size
|
|
654
|
+
# For different formats:
|
|
655
|
+
# - MHA (5D): [2, num_blocks, block_size, num_heads, head_size]
|
|
656
|
+
# - MLA (3D): [num_blocks, block_size, head_size]
|
|
657
|
+
# - 4D: [num_blocks, block_size, num_heads, head_size]
|
|
658
|
+
ndim = first_kv_tensor.ndim
|
|
659
|
+
if ndim == 5:
|
|
660
|
+
# MHA: [2, num_blocks, block_size, num_heads, head_size]
|
|
661
|
+
total_slots = first_kv_tensor.shape[1] * first_kv_tensor.shape[2]
|
|
662
|
+
elif ndim == 3:
|
|
663
|
+
# MLA: [num_blocks, block_size, head_size]
|
|
664
|
+
total_slots = first_kv_tensor.shape[0] * first_kv_tensor.shape[1]
|
|
665
|
+
elif ndim == 4:
|
|
666
|
+
# 4D: [num_blocks, block_size, num_heads, head_size]
|
|
667
|
+
total_slots = first_kv_tensor.shape[0] * first_kv_tensor.shape[1]
|
|
668
|
+
else:
|
|
669
|
+
# Fallback
|
|
670
|
+
reshaped = first_kv_tensor.view(-1, *first_kv_tensor.shape[2:])
|
|
671
|
+
total_slots = reshaped.shape[0]
|
|
672
|
+
|
|
673
|
+
# Check each slot index
|
|
674
|
+
invalid_indices = []
|
|
675
|
+
for slot_idx in slot_indices:
|
|
676
|
+
if slot_idx < 0 or slot_idx >= total_slots:
|
|
677
|
+
invalid_indices.append(slot_idx)
|
|
678
|
+
|
|
679
|
+
if invalid_indices:
|
|
680
|
+
return _create_error_response(
|
|
681
|
+
{
|
|
682
|
+
"error": "Invalid slot indices",
|
|
683
|
+
"message": (
|
|
684
|
+
"Slot indices out of bounds: %s. Valid range: 0 to %d"
|
|
685
|
+
)
|
|
686
|
+
% (invalid_indices, total_slots - 1),
|
|
687
|
+
},
|
|
688
|
+
400,
|
|
689
|
+
)
|
|
690
|
+
else:
|
|
691
|
+
return _create_error_response(
|
|
692
|
+
{
|
|
693
|
+
"error": "kv_caches not available",
|
|
694
|
+
"message": "kv_caches is empty or not initialized",
|
|
695
|
+
},
|
|
696
|
+
404,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
# Validate chunk_size if provided
|
|
700
|
+
if chunk_size is not None and chunk_size <= 0:
|
|
701
|
+
return _create_error_response(
|
|
702
|
+
{
|
|
703
|
+
"error": "Invalid chunk_size",
|
|
704
|
+
"message": "chunk_size must be a positive integer",
|
|
705
|
+
},
|
|
706
|
+
400,
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
# Get checksums from the adapter asynchronously to not block the loop
|
|
710
|
+
loop = asyncio.get_running_loop()
|
|
711
|
+
checksums_result = await loop.run_in_executor(
|
|
712
|
+
None, # Uses default ThreadPoolExecutor
|
|
713
|
+
lambda: compute_kvcache_checksums(
|
|
714
|
+
lmcache_adapter, slot_indices, chunk_size, layerwise
|
|
715
|
+
),
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
if checksums_result is None:
|
|
719
|
+
return _create_error_response(
|
|
720
|
+
{
|
|
721
|
+
"error": "Failed to compute checksums",
|
|
722
|
+
"message": "kv_caches not available or empty",
|
|
723
|
+
},
|
|
724
|
+
500,
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
# Compute slot mapping ranges using compress_slot_mapping
|
|
728
|
+
slot_mapping_ranges = compress_slot_mapping(slot_indices)
|
|
729
|
+
|
|
730
|
+
response_data: dict[str, Any] = {
|
|
731
|
+
"status": "success",
|
|
732
|
+
"slot_mapping_ranges": slot_mapping_ranges,
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
# Include chunk checksums
|
|
736
|
+
response_data["chunk_size"] = checksums_result.get("chunk_size")
|
|
737
|
+
response_data["num_chunks"] = checksums_result.get("num_chunks")
|
|
738
|
+
response_data["chunk_checksums"] = checksums_result.get("chunk_checksums")
|
|
739
|
+
response_data["layerwise"] = layerwise
|
|
740
|
+
|
|
741
|
+
return PlainTextResponse(
|
|
742
|
+
content=json.dumps(response_data, indent=2),
|
|
743
|
+
media_type="application/json",
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
except Exception as e:
|
|
747
|
+
logger.error("Failed to compute kvcache checksums: %s", str(e))
|
|
748
|
+
return _create_error_response(
|
|
749
|
+
{"error": "Failed to compute checksums", "message": str(e)},
|
|
750
|
+
500,
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
@router.post("/cache/kvcache/record_slot")
|
|
755
|
+
async def kvcache_record_slot(
|
|
756
|
+
request: Request,
|
|
757
|
+
enabled: Optional[str] = None,
|
|
758
|
+
):
|
|
759
|
+
"""Enable or disable KVCache Check slot_mapping logging.
|
|
760
|
+
|
|
761
|
+
This endpoint controls whether the KVCache Check logs (slot_mapping info)
|
|
762
|
+
are printed when store/retrieve operations are performed.
|
|
763
|
+
|
|
764
|
+
Args:
|
|
765
|
+
request (Request): The FastAPI request object containing application state.
|
|
766
|
+
enabled (Optional[str], optional): "true" to enable logging, "false" to
|
|
767
|
+
disable. Defaults to None.
|
|
768
|
+
|
|
769
|
+
Returns:
|
|
770
|
+
PlainTextResponse: A JSON response containing the current logging status.
|
|
771
|
+
|
|
772
|
+
Example:
|
|
773
|
+
```bash
|
|
774
|
+
# Enable logging
|
|
775
|
+
curl -X POST "http://localhost:8000/cache/kvcache/record_slot?enabled=true"
|
|
776
|
+
|
|
777
|
+
# Disable logging
|
|
778
|
+
curl -X POST "http://localhost:8000/cache/kvcache/record_slot?enabled=false"
|
|
779
|
+
|
|
780
|
+
# Check current status
|
|
781
|
+
curl -X POST "http://localhost:8000/cache/kvcache/record_slot"
|
|
782
|
+
```
|
|
783
|
+
"""
|
|
784
|
+
try:
|
|
785
|
+
lmcache_adapter = request.app.state.lmcache_adapter
|
|
786
|
+
if not lmcache_adapter:
|
|
787
|
+
return _create_error_response(
|
|
788
|
+
{
|
|
789
|
+
"error": "LMCache adapter unavailable",
|
|
790
|
+
"message": "LMCache adapter not configured.",
|
|
791
|
+
},
|
|
792
|
+
503,
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
# Get current status from lmcache_engine
|
|
796
|
+
lmcache_engine = lmcache_adapter.lmcache_engine
|
|
797
|
+
current_status = getattr(lmcache_engine, "kvcache_check_log_enabled", False)
|
|
798
|
+
|
|
799
|
+
# Update status if enabled parameter is provided
|
|
800
|
+
if enabled is not None:
|
|
801
|
+
enabled_lower = enabled.lower()
|
|
802
|
+
if enabled_lower == "true":
|
|
803
|
+
lmcache_engine.kvcache_check_log_enabled = True
|
|
804
|
+
current_status = True
|
|
805
|
+
logger.info("KVCache Check logging enabled")
|
|
806
|
+
elif enabled_lower == "false":
|
|
807
|
+
lmcache_engine.kvcache_check_log_enabled = False
|
|
808
|
+
current_status = False
|
|
809
|
+
logger.info("KVCache Check logging disabled")
|
|
810
|
+
else:
|
|
811
|
+
return _create_error_response(
|
|
812
|
+
{
|
|
813
|
+
"error": "Invalid parameter",
|
|
814
|
+
"message": "enabled must be 'true' or 'false'",
|
|
815
|
+
},
|
|
816
|
+
400,
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
response_data = {
|
|
820
|
+
"status": "success",
|
|
821
|
+
"kvcache_check_log_enabled": current_status,
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return PlainTextResponse(
|
|
825
|
+
content=json.dumps(response_data, indent=2),
|
|
826
|
+
media_type="application/json",
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
except Exception as e:
|
|
830
|
+
logger.error("Failed to set kvcache record slot status: %s", str(e))
|
|
831
|
+
return _create_error_response(
|
|
832
|
+
{"error": "Failed to set record slot status", "message": str(e)},
|
|
833
|
+
500,
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
@router.get("/cache/kvcache/info")
|
|
838
|
+
async def kvcache_info(request: Request):
|
|
839
|
+
"""Get information about the current kvcaches.
|
|
840
|
+
|
|
841
|
+
Returns information about the kvcaches structure including layer names,
|
|
842
|
+
shapes, and device information.
|
|
843
|
+
|
|
844
|
+
Args:
|
|
845
|
+
request (Request): The FastAPI request object containing application state.
|
|
846
|
+
|
|
847
|
+
Returns:
|
|
848
|
+
PlainTextResponse: A JSON response containing kvcache information.
|
|
849
|
+
"""
|
|
850
|
+
try:
|
|
851
|
+
lmcache_adapter = request.app.state.lmcache_adapter
|
|
852
|
+
if not lmcache_adapter:
|
|
853
|
+
return _create_error_response(
|
|
854
|
+
{
|
|
855
|
+
"error": "LMCache adapter unavailable",
|
|
856
|
+
"message": "LMCache adapter not configured.",
|
|
857
|
+
},
|
|
858
|
+
503,
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
kv_caches = getattr(lmcache_adapter, "kvcaches", None)
|
|
862
|
+
if not kv_caches:
|
|
863
|
+
return _create_error_response(
|
|
864
|
+
{
|
|
865
|
+
"error": "kv_caches not available",
|
|
866
|
+
"message": "kv_caches is empty or not initialized",
|
|
867
|
+
},
|
|
868
|
+
404,
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
layers_info: dict = {}
|
|
872
|
+
for layer_name, kv_tensor in kv_caches.items():
|
|
873
|
+
layers_info[layer_name] = {
|
|
874
|
+
"shape": list(kv_tensor.shape),
|
|
875
|
+
"dtype": str(kv_tensor.dtype),
|
|
876
|
+
"device": str(kv_tensor.device),
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
info = {
|
|
880
|
+
"status": "success",
|
|
881
|
+
"num_layers": len(kv_caches),
|
|
882
|
+
"layers": layers_info,
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return PlainTextResponse(
|
|
886
|
+
content=json.dumps(info, indent=2),
|
|
887
|
+
media_type="application/json",
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
except Exception as e:
|
|
891
|
+
logger.error("Failed to get kvcache info: %s", str(e))
|
|
892
|
+
return _create_error_response(
|
|
893
|
+
{"error": "Failed to get kvcache info", "message": str(e)},
|
|
894
|
+
500,
|
|
895
|
+
)
|