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,505 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""``lmcache trace`` — inspect and replay storage-level trace files.
|
|
4
|
+
|
|
5
|
+
Subcommands:
|
|
6
|
+
|
|
7
|
+
* ``info FILE`` — print a summary (header metadata + per-qualname
|
|
8
|
+
record counts).
|
|
9
|
+
* ``replay FILE ...`` — reissue every recorded call against a fresh
|
|
10
|
+
StorageManager, honoring the recorded inter-call timings. Takes
|
|
11
|
+
the standard storage-manager CLI flags (see
|
|
12
|
+
:func:`lmcache.v1.distributed.config.add_storage_manager_args`),
|
|
13
|
+
plus per-record output (``--verbose`` / ``--jsonl-out``),
|
|
14
|
+
aggregated CSV/JSON summary export (``--output-dir`` / ``--no-csv``
|
|
15
|
+
/ ``--json``), and a terminal metrics table (suppressible with
|
|
16
|
+
``-q``).
|
|
17
|
+
|
|
18
|
+
Trace *capture* is not a ``trace`` subcommand — recording is bound to
|
|
19
|
+
the live process via ``lmcache server --trace-level storage
|
|
20
|
+
[--trace-output ...]``. Surfacing a CLI stub here would only
|
|
21
|
+
duplicate that flag while leaving the user wondering why it cannot
|
|
22
|
+
start a recorder against an already-running server.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# Future
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
# Standard
|
|
29
|
+
from collections import Counter
|
|
30
|
+
from typing import Callable
|
|
31
|
+
import argparse
|
|
32
|
+
import json
|
|
33
|
+
import os
|
|
34
|
+
import sys
|
|
35
|
+
|
|
36
|
+
# First Party
|
|
37
|
+
from lmcache.cli.commands.base import BaseCommand
|
|
38
|
+
from lmcache.cli.metrics import Metrics, StreamHandler, get_formatter
|
|
39
|
+
from lmcache.logging import init_logger
|
|
40
|
+
|
|
41
|
+
logger = init_logger(__name__)
|
|
42
|
+
|
|
43
|
+
# ``lmcache trace`` drives a real StorageManager and decodes a binary
|
|
44
|
+
# trace file — both pulled from the full LMCache runtime
|
|
45
|
+
# (``lmcache.v1.*``, torch kernels, native ops). Users who installed
|
|
46
|
+
# the thin ``lmcache-cli`` shell lack those modules, so importing them
|
|
47
|
+
# unconditionally at the top of this file would kill the *entire*
|
|
48
|
+
# ``lmcache`` CLI with an opaque ImportError the first time
|
|
49
|
+
# ``lmcache/cli/commands/__init__.py`` loads the command registry.
|
|
50
|
+
#
|
|
51
|
+
# Wrap the heavy imports and remember the error so each subcommand
|
|
52
|
+
# handler can bail out with an actionable install hint. ``record`` is
|
|
53
|
+
# a stub that needs no runtime, so it keeps working on a CLI-only
|
|
54
|
+
# install.
|
|
55
|
+
_IMPORT_ERROR: ImportError | None = None
|
|
56
|
+
try:
|
|
57
|
+
# First Party
|
|
58
|
+
from lmcache.cli.commands.trace.driver import (
|
|
59
|
+
ReplayResult,
|
|
60
|
+
StorageReplayDriver,
|
|
61
|
+
)
|
|
62
|
+
from lmcache.cli.commands.trace.stats import ReplayStatsCollector
|
|
63
|
+
from lmcache.v1.distributed.config import (
|
|
64
|
+
StorageManagerConfig,
|
|
65
|
+
add_storage_manager_args,
|
|
66
|
+
parse_args_to_config,
|
|
67
|
+
)
|
|
68
|
+
from lmcache.v1.mp_observability.config import (
|
|
69
|
+
add_observability_args,
|
|
70
|
+
parse_args_to_observability_config,
|
|
71
|
+
)
|
|
72
|
+
from lmcache.v1.mp_observability.trace.reader import TraceReader
|
|
73
|
+
except ImportError as _exc:
|
|
74
|
+
_IMPORT_ERROR = _exc
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _require_full_install() -> None:
|
|
78
|
+
"""Exit with an install hint if the full LMCache runtime is missing.
|
|
79
|
+
|
|
80
|
+
``lmcache trace info`` and ``lmcache trace replay`` both need
|
|
81
|
+
``lmcache.v1.*`` (StorageManager, trace codecs, TraceReader).
|
|
82
|
+
When those imports failed at module load — almost always because
|
|
83
|
+
the user installed ``lmcache-cli`` instead of the full package —
|
|
84
|
+
this helper prints the shortest actionable message to stderr and
|
|
85
|
+
exits with status ``2`` so scripts can detect the install gap
|
|
86
|
+
programmatically.
|
|
87
|
+
|
|
88
|
+
Writes directly to :data:`sys.stderr` rather than going through
|
|
89
|
+
:mod:`logging` so the message reaches the user even when the
|
|
90
|
+
lmcache logger has been suppressed or its handlers redirected.
|
|
91
|
+
|
|
92
|
+
No-op when imports succeeded, so it is safe to call
|
|
93
|
+
unconditionally at the top of every trace handler.
|
|
94
|
+
"""
|
|
95
|
+
if _IMPORT_ERROR is None:
|
|
96
|
+
return
|
|
97
|
+
print(
|
|
98
|
+
"ERROR: `lmcache trace` needs the full LMCache package "
|
|
99
|
+
"(StorageManager, trace codecs, etc.), but only the `lmcache-cli` "
|
|
100
|
+
"shell appears to be installed.\n"
|
|
101
|
+
" Install the full package with `pip install lmcache` and try "
|
|
102
|
+
"again.\n"
|
|
103
|
+
f" Original import error: {_IMPORT_ERROR}",
|
|
104
|
+
file=sys.stderr,
|
|
105
|
+
)
|
|
106
|
+
sys.exit(2)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class TraceCommand(BaseCommand):
|
|
110
|
+
"""Subcommand group for trace inspection and replay."""
|
|
111
|
+
|
|
112
|
+
def name(self) -> str:
|
|
113
|
+
return "trace"
|
|
114
|
+
|
|
115
|
+
def help(self) -> str:
|
|
116
|
+
return "Inspect and replay LMCache storage-level trace files."
|
|
117
|
+
|
|
118
|
+
def add_arguments(self, _parser: argparse.ArgumentParser) -> None:
|
|
119
|
+
# Empty; all args live under the per-subcommand parsers added
|
|
120
|
+
# in :meth:`register`.
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
def register(self, subparsers: argparse._SubParsersAction) -> None:
|
|
124
|
+
"""Register ``trace`` with the root parser.
|
|
125
|
+
|
|
126
|
+
Overrides :meth:`BaseCommand.register` because ``trace`` has
|
|
127
|
+
its own nested subparsers (``info`` and ``replay``). The
|
|
128
|
+
base-class ``--format``/``--output``/``--quiet`` flags do not
|
|
129
|
+
apply uniformly across the subcommands — ``replay`` has its
|
|
130
|
+
own ``--jsonl-out`` output channel — so they are added only
|
|
131
|
+
to ``info``.
|
|
132
|
+
"""
|
|
133
|
+
parser = subparsers.add_parser(
|
|
134
|
+
self.name(),
|
|
135
|
+
help=self.help(),
|
|
136
|
+
description=self.help(),
|
|
137
|
+
)
|
|
138
|
+
inner = parser.add_subparsers(
|
|
139
|
+
dest="trace_target",
|
|
140
|
+
required=True,
|
|
141
|
+
metavar="{info,replay}",
|
|
142
|
+
)
|
|
143
|
+
self._register_info(inner)
|
|
144
|
+
self._register_replay(inner)
|
|
145
|
+
|
|
146
|
+
def execute(self, args: argparse.Namespace) -> None:
|
|
147
|
+
"""Dispatch the parsed subcommand."""
|
|
148
|
+
handlers: dict[str, Callable[[argparse.Namespace], None]] = {
|
|
149
|
+
"info": self._run_info,
|
|
150
|
+
"replay": self._run_replay,
|
|
151
|
+
}
|
|
152
|
+
handler = handlers.get(args.trace_target)
|
|
153
|
+
if handler is None:
|
|
154
|
+
# ``required=True`` on the subparser makes this unreachable
|
|
155
|
+
# in practice; branch is kept for defensive logging.
|
|
156
|
+
print(f"Unknown trace target: {args.trace_target}", file=sys.stderr)
|
|
157
|
+
sys.exit(1)
|
|
158
|
+
handler(args)
|
|
159
|
+
|
|
160
|
+
# ------------------------------------------------------------------
|
|
161
|
+
# ``info``
|
|
162
|
+
# ------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
def _register_info(self, subparsers: argparse._SubParsersAction) -> None:
|
|
165
|
+
parser = subparsers.add_parser(
|
|
166
|
+
"info",
|
|
167
|
+
help="Print a summary of a trace file.",
|
|
168
|
+
)
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"trace_path",
|
|
171
|
+
metavar="FILE",
|
|
172
|
+
help="Path to a .lct trace file.",
|
|
173
|
+
)
|
|
174
|
+
parser.set_defaults(func=self.execute)
|
|
175
|
+
|
|
176
|
+
def _run_info(self, args: argparse.Namespace) -> None:
|
|
177
|
+
"""Read a trace file and print a one-screen summary."""
|
|
178
|
+
_require_full_install()
|
|
179
|
+
with TraceReader(args.trace_path) as r:
|
|
180
|
+
header = r.header
|
|
181
|
+
counts: Counter[str] = Counter()
|
|
182
|
+
max_mono = 0.0
|
|
183
|
+
for record in r.records():
|
|
184
|
+
counts[record.qualname] += 1
|
|
185
|
+
if record.t_mono > max_mono:
|
|
186
|
+
max_mono = record.t_mono
|
|
187
|
+
|
|
188
|
+
print(f"Trace file: {args.trace_path}")
|
|
189
|
+
print(f" level: {header.level}")
|
|
190
|
+
print(f" format_version: {header.format_version}")
|
|
191
|
+
print(f" trace_schema_version: {header.trace_schema_version}")
|
|
192
|
+
print(f" duration: {max_mono:.3f}s")
|
|
193
|
+
print(f" sm_config_digest: {header.sm_config_digest or '(none)'}")
|
|
194
|
+
print(f" total_records: {sum(counts.values())}")
|
|
195
|
+
if counts:
|
|
196
|
+
print(" ops:")
|
|
197
|
+
for qn in sorted(counts):
|
|
198
|
+
print(f" {qn}: {counts[qn]}")
|
|
199
|
+
else:
|
|
200
|
+
print(" ops: (none)")
|
|
201
|
+
|
|
202
|
+
# ------------------------------------------------------------------
|
|
203
|
+
# ``replay``
|
|
204
|
+
# ------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
def _register_replay(self, subparsers: argparse._SubParsersAction) -> None:
|
|
207
|
+
parser = subparsers.add_parser(
|
|
208
|
+
"replay",
|
|
209
|
+
help="Replay a trace file against a fresh StorageManager.",
|
|
210
|
+
description=(
|
|
211
|
+
"Replay a trace file against a fresh StorageManager. "
|
|
212
|
+
"Accepts the standard storage-manager config flags "
|
|
213
|
+
"(--l1-size-gb, --eviction-policy, --l2-…); see "
|
|
214
|
+
"'lmcache server --help' for the full list."
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
parser.add_argument(
|
|
218
|
+
"trace_path",
|
|
219
|
+
metavar="FILE",
|
|
220
|
+
help="Path to a .lct trace file.",
|
|
221
|
+
)
|
|
222
|
+
parser.add_argument(
|
|
223
|
+
"--verbose",
|
|
224
|
+
action="store_true",
|
|
225
|
+
default=False,
|
|
226
|
+
help="Print one line per replayed record.",
|
|
227
|
+
)
|
|
228
|
+
parser.add_argument(
|
|
229
|
+
"--jsonl-out",
|
|
230
|
+
default=None,
|
|
231
|
+
metavar="PATH",
|
|
232
|
+
help=(
|
|
233
|
+
"Write one JSON object per replayed record to PATH "
|
|
234
|
+
"(qualname, latency_ms, failed). Useful for post-hoc "
|
|
235
|
+
"analysis."
|
|
236
|
+
),
|
|
237
|
+
)
|
|
238
|
+
parser.add_argument(
|
|
239
|
+
"--output-dir",
|
|
240
|
+
default=".",
|
|
241
|
+
help=(
|
|
242
|
+
"Directory for aggregated CSV/JSON summary output "
|
|
243
|
+
"(default: current directory)."
|
|
244
|
+
),
|
|
245
|
+
)
|
|
246
|
+
parser.add_argument(
|
|
247
|
+
"--no-csv",
|
|
248
|
+
action="store_true",
|
|
249
|
+
help="Skip the aggregated CSV summary export.",
|
|
250
|
+
)
|
|
251
|
+
parser.add_argument(
|
|
252
|
+
"--json",
|
|
253
|
+
action="store_true",
|
|
254
|
+
help="Also export an aggregated JSON summary.",
|
|
255
|
+
)
|
|
256
|
+
parser.add_argument(
|
|
257
|
+
"-q",
|
|
258
|
+
"--quiet",
|
|
259
|
+
action="store_true",
|
|
260
|
+
help="Suppress the terminal metrics table (files are still written).",
|
|
261
|
+
)
|
|
262
|
+
# ``add_storage_manager_args`` and ``add_observability_args``
|
|
263
|
+
# live in the full LMCache runtime (``lmcache.v1.*``) and are
|
|
264
|
+
# unavailable in the CLI-only install. When those imports
|
|
265
|
+
# failed at module load, register ``replay`` with only the
|
|
266
|
+
# CLI-local flags above so ``--help`` still works; the actual
|
|
267
|
+
# execute path bails via :func:`_require_full_install` before
|
|
268
|
+
# it would try to parse the missing-flag namespace.
|
|
269
|
+
#
|
|
270
|
+
# When the full runtime *is* present we share the whole
|
|
271
|
+
# observability surface with ``lmcache server``.
|
|
272
|
+
# ``--trace-level`` / ``--trace-output`` configure *recording*
|
|
273
|
+
# and have no effect during replay; :meth:`_run_replay`
|
|
274
|
+
# overrides them to ``None`` before constructing the
|
|
275
|
+
# observability config rather than duplicating the argparse
|
|
276
|
+
# registration to strip them.
|
|
277
|
+
if _IMPORT_ERROR is None:
|
|
278
|
+
add_storage_manager_args(parser)
|
|
279
|
+
add_observability_args(parser)
|
|
280
|
+
parser.set_defaults(func=self.execute)
|
|
281
|
+
|
|
282
|
+
def _run_replay(self, args: argparse.Namespace) -> None:
|
|
283
|
+
"""Construct a StorageManager from *args* and drive replay.
|
|
284
|
+
|
|
285
|
+
Produces three kinds of output:
|
|
286
|
+
|
|
287
|
+
* Per-record stream: every dispatch is logged at INFO with its
|
|
288
|
+
progress (``[N/total]``), qualname, and latency.
|
|
289
|
+
``--verbose`` additionally mirrors each record to stdout,
|
|
290
|
+
and ``--jsonl-out PATH`` writes one JSON object per record
|
|
291
|
+
to ``PATH`` for post-hoc analysis.
|
|
292
|
+
* Aggregated per-qualname summary: CSV (unless ``--no-csv``)
|
|
293
|
+
and JSON (with ``--json``) written under ``--output-dir``.
|
|
294
|
+
* Terminal metrics table (unless ``--quiet``) using the shared
|
|
295
|
+
:class:`~lmcache.cli.metrics.Metrics` renderer.
|
|
296
|
+
"""
|
|
297
|
+
_require_full_install()
|
|
298
|
+
sm_config: StorageManagerConfig = parse_args_to_config(args)
|
|
299
|
+
|
|
300
|
+
# ``--trace-level`` / ``--trace-output`` belong to the recording
|
|
301
|
+
# surface. They are still registered on the parser (see
|
|
302
|
+
# :meth:`_register_replay`) so the flag set stays in lock-step
|
|
303
|
+
# with ``lmcache server``, but they have no meaning here — any
|
|
304
|
+
# value a caller passes is silently clobbered to ``None`` so
|
|
305
|
+
# the replay-side ``ObservabilityConfig`` never tries to start
|
|
306
|
+
# a recorder.
|
|
307
|
+
args.trace_level = None
|
|
308
|
+
args.trace_output = None
|
|
309
|
+
obs_config = parse_args_to_observability_config(args)
|
|
310
|
+
|
|
311
|
+
# Create output directories *before* replay starts. A replay
|
|
312
|
+
# can run for minutes; surfacing a bad ``--output-dir`` or
|
|
313
|
+
# unwritable ``--jsonl-out`` parent now avoids silently losing
|
|
314
|
+
# the summary/stream after the work has already happened.
|
|
315
|
+
os.makedirs(args.output_dir, exist_ok=True)
|
|
316
|
+
if args.jsonl_out:
|
|
317
|
+
jsonl_parent = os.path.dirname(os.path.abspath(args.jsonl_out))
|
|
318
|
+
if jsonl_parent:
|
|
319
|
+
os.makedirs(jsonl_parent, exist_ok=True)
|
|
320
|
+
|
|
321
|
+
# ANSI: bold + yellow for the banner text, reset at the end.
|
|
322
|
+
# The lmcache log formatter only colors the WARNING prefix;
|
|
323
|
+
# these codes highlight the message body too. Writing them
|
|
324
|
+
# into a file via shell redirection leaves the escape bytes
|
|
325
|
+
# visible but still readable.
|
|
326
|
+
bold = "\033[1;33m"
|
|
327
|
+
reset = "\033[0m"
|
|
328
|
+
bar = "=" * 78
|
|
329
|
+
logger.warning(
|
|
330
|
+
"\n%s%s\n"
|
|
331
|
+
" !! REPLAY ENVIRONMENT MISMATCH MAY CAUSE RETRIEVE MISSES !!\n"
|
|
332
|
+
"%s%s\n"
|
|
333
|
+
" * Replay uses the *replay-side* StorageManager config, which\n"
|
|
334
|
+
" may differ from the config recorded in the trace.\n"
|
|
335
|
+
" * Replay runs on a host whose performance may differ from\n"
|
|
336
|
+
" the recording host.\n"
|
|
337
|
+
" * StorageManager reads/writes are async — an L2 load that\n"
|
|
338
|
+
" had finished at record time may not have finished yet at\n"
|
|
339
|
+
" replay time, so the matching retrieve can miss.\n"
|
|
340
|
+
"\n"
|
|
341
|
+
" Treat retrieve-miss counts as a signal about the replay\n"
|
|
342
|
+
" environment, not as a defect in the trace.\n"
|
|
343
|
+
"%s%s",
|
|
344
|
+
bold,
|
|
345
|
+
bar,
|
|
346
|
+
bar,
|
|
347
|
+
reset,
|
|
348
|
+
bar,
|
|
349
|
+
reset,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Pre-scan to count total records so progress logs can show
|
|
353
|
+
# [N/total]. The reader streams frames, so counting is cheap
|
|
354
|
+
# relative to replay (which actually dispatches StorageManager
|
|
355
|
+
# calls).
|
|
356
|
+
with TraceReader(args.trace_path) as r:
|
|
357
|
+
total_records = sum(1 for _ in r.records())
|
|
358
|
+
logger.info(
|
|
359
|
+
"trace replay: file=%s records=%d",
|
|
360
|
+
args.trace_path,
|
|
361
|
+
total_records,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
jsonl_fh = open(args.jsonl_out, "w") if args.jsonl_out else None
|
|
365
|
+
verbose = args.verbose
|
|
366
|
+
counter = {"n": 0}
|
|
367
|
+
|
|
368
|
+
def _on_record(qualname: str, latency_s: float, failed: bool) -> None:
|
|
369
|
+
counter["n"] += 1
|
|
370
|
+
status = "FAIL" if failed else "OK"
|
|
371
|
+
logger.info(
|
|
372
|
+
"[%d/%d] %s %s (%.3fms)",
|
|
373
|
+
counter["n"],
|
|
374
|
+
total_records,
|
|
375
|
+
status,
|
|
376
|
+
qualname,
|
|
377
|
+
latency_s * 1000.0,
|
|
378
|
+
)
|
|
379
|
+
if verbose:
|
|
380
|
+
print(
|
|
381
|
+
f" [{counter['n']}/{total_records}] "
|
|
382
|
+
f"{status:<4} {latency_s * 1000:8.3f}ms {qualname}"
|
|
383
|
+
)
|
|
384
|
+
if jsonl_fh is not None:
|
|
385
|
+
jsonl_fh.write(
|
|
386
|
+
json.dumps(
|
|
387
|
+
{
|
|
388
|
+
"qualname": qualname,
|
|
389
|
+
"latency_ms": latency_s * 1000.0,
|
|
390
|
+
"failed": failed,
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
+ "\n"
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
try:
|
|
397
|
+
with StorageReplayDriver(
|
|
398
|
+
sm_config, args.trace_path, obs_config=obs_config
|
|
399
|
+
) as driver:
|
|
400
|
+
result = driver.run(on_record=_on_record)
|
|
401
|
+
finally:
|
|
402
|
+
if jsonl_fh is not None:
|
|
403
|
+
jsonl_fh.close()
|
|
404
|
+
|
|
405
|
+
if not args.no_csv:
|
|
406
|
+
csv_path = os.path.join(args.output_dir, "trace_replay_ops.csv")
|
|
407
|
+
result.stats.export_csv(csv_path)
|
|
408
|
+
logger.info("CSV written to %s", csv_path)
|
|
409
|
+
if args.json:
|
|
410
|
+
json_path = os.path.join(args.output_dir, "trace_replay_summary.json")
|
|
411
|
+
result.stats.export_json(json_path)
|
|
412
|
+
logger.info("JSON written to %s", json_path)
|
|
413
|
+
|
|
414
|
+
if not args.quiet:
|
|
415
|
+
self._emit_replay_metrics(result.stats, result)
|
|
416
|
+
|
|
417
|
+
if result.records_failed > 0:
|
|
418
|
+
sys.exit(1)
|
|
419
|
+
|
|
420
|
+
@staticmethod
|
|
421
|
+
def _emit_replay_metrics(
|
|
422
|
+
stats: ReplayStatsCollector,
|
|
423
|
+
result: ReplayResult,
|
|
424
|
+
) -> None:
|
|
425
|
+
"""Print the replay summary using the shared :class:`Metrics` renderer.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
stats: The stats collector populated during replay.
|
|
429
|
+
result: The full :class:`ReplayResult` — used for the
|
|
430
|
+
replayed/skipped/failed totals and digest comparison.
|
|
431
|
+
"""
|
|
432
|
+
metrics = Metrics(title="Trace Replay Result")
|
|
433
|
+
metrics.add_handler(StreamHandler(get_formatter("terminal", width=64)))
|
|
434
|
+
|
|
435
|
+
overall = metrics.add_section("overall", "Overall")
|
|
436
|
+
overall.add("level", "Trace level", result.header_level)
|
|
437
|
+
overall.add("replayed", "Records replayed", result.records_replayed)
|
|
438
|
+
overall.add("skipped", "Records skipped", result.records_skipped)
|
|
439
|
+
overall.add("failed", "Records failed", result.records_failed)
|
|
440
|
+
overall.add(
|
|
441
|
+
"duration",
|
|
442
|
+
"Replay duration (s)",
|
|
443
|
+
round(stats.total_duration_s(), 3),
|
|
444
|
+
)
|
|
445
|
+
header_digest = result.header_digest
|
|
446
|
+
replay_digest = result.replay_config_digest
|
|
447
|
+
if header_digest and replay_digest and header_digest != replay_digest:
|
|
448
|
+
overall.add(
|
|
449
|
+
"digest",
|
|
450
|
+
"Config digest",
|
|
451
|
+
f"MISMATCH (rec={header_digest[:8]}, run={replay_digest[:8]})",
|
|
452
|
+
)
|
|
453
|
+
elif header_digest:
|
|
454
|
+
overall.add("digest", "Config digest", f"match ({header_digest[:8]})")
|
|
455
|
+
|
|
456
|
+
summary = stats.summary()
|
|
457
|
+
if summary:
|
|
458
|
+
ops_section = metrics.add_section("ops", "Per-Op Latency (ms)")
|
|
459
|
+
for qn in sorted(summary):
|
|
460
|
+
s = summary[qn]
|
|
461
|
+
short = _short_op_name(qn)
|
|
462
|
+
ops_section.add(f"{short}_count", f"{short} count", s.count)
|
|
463
|
+
ops_section.add(
|
|
464
|
+
f"{short}_mean",
|
|
465
|
+
f"{short} mean",
|
|
466
|
+
round(s.mean_ms, 3),
|
|
467
|
+
)
|
|
468
|
+
ops_section.add(
|
|
469
|
+
f"{short}_p50",
|
|
470
|
+
f"{short} p50",
|
|
471
|
+
round(s.p50_ms, 3),
|
|
472
|
+
)
|
|
473
|
+
ops_section.add(
|
|
474
|
+
f"{short}_p99",
|
|
475
|
+
f"{short} p99",
|
|
476
|
+
round(s.p99_ms, 3),
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
metrics.emit()
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _short_op_name(qualname: str) -> str:
|
|
483
|
+
"""Return a compact, human-readable label for a traced qualname.
|
|
484
|
+
|
|
485
|
+
Plain methods collapse to the method name: the table has limited
|
|
486
|
+
column width and the fully-qualified path is verbose.
|
|
487
|
+
|
|
488
|
+
Context-manager handlers (``__enter__`` / ``__exit__``) instead
|
|
489
|
+
collapse to ``<owning_method>.enter`` / ``<owning_method>.exit``,
|
|
490
|
+
so the reader can tell *which* context manager the pair belongs
|
|
491
|
+
to — the bare ``__enter__`` / ``__exit__`` label is useless when
|
|
492
|
+
multiple context-manager-returning methods are traced.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
qualname: Dotted qualname recorded by the tracer, e.g.
|
|
496
|
+
``lmcache.v1.distributed.storage_manager.StorageManager.read_prefetched_results.__enter__``.
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
A short label suitable as a metrics row prefix.
|
|
500
|
+
"""
|
|
501
|
+
parts = qualname.split(".")
|
|
502
|
+
last = parts[-1]
|
|
503
|
+
if last in ("__enter__", "__exit__") and len(parts) >= 2:
|
|
504
|
+
return f"{parts[-2]}.{last.strip('_')}"
|
|
505
|
+
return last
|