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,99 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
# Third Party
|
|
6
|
+
import torch
|
|
7
|
+
|
|
8
|
+
# First Party
|
|
9
|
+
from lmcache.logging import init_logger
|
|
10
|
+
from lmcache.v1.cache_engine import LMCacheEngine
|
|
11
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
12
|
+
from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
|
|
13
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
14
|
+
|
|
15
|
+
logger = init_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LMCacheBypassLookupClient(LookupClientInterface):
|
|
19
|
+
"""
|
|
20
|
+
Bypass lookup client that directly calls LMCacheEngine.lookup()
|
|
21
|
+
instead of using ZMQ communication. This is particularly useful
|
|
22
|
+
for MLA scenarios where only rank 0 needs to perform lookups.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
config: LMCacheEngineConfig,
|
|
28
|
+
metadata: LMCacheMetadata,
|
|
29
|
+
lmcache_engine: LMCacheEngine,
|
|
30
|
+
):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the bypass lookup client.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
config: The LMCacheEngine configuration
|
|
36
|
+
metadata: The LMCacheEngine metadata
|
|
37
|
+
lmcache_engine: The LMCacheEngine instance to use for lookups
|
|
38
|
+
"""
|
|
39
|
+
assert isinstance(config, LMCacheEngineConfig), (
|
|
40
|
+
"LMCache v1 configuration should be passed."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
self.lmcache_engine = lmcache_engine
|
|
44
|
+
self.config = config
|
|
45
|
+
|
|
46
|
+
# Use the token database from the provided LMCacheEngine
|
|
47
|
+
self.token_database = self.lmcache_engine.token_database
|
|
48
|
+
self.enable_blending = self.config.enable_blending
|
|
49
|
+
|
|
50
|
+
logger.info("LMCacheBypassLookupClient initialized")
|
|
51
|
+
|
|
52
|
+
def lookup(
|
|
53
|
+
self,
|
|
54
|
+
token_ids: Union[torch.Tensor, list[int]],
|
|
55
|
+
lookup_id: str,
|
|
56
|
+
request_configs: Optional[dict] = None,
|
|
57
|
+
) -> Optional[int]:
|
|
58
|
+
try:
|
|
59
|
+
if not self.enable_blending:
|
|
60
|
+
# Process tokens to get hashes and offsets
|
|
61
|
+
hashes = []
|
|
62
|
+
offsets = []
|
|
63
|
+
for start, end, key in self.token_database.process_tokens(
|
|
64
|
+
token_ids, make_key=False
|
|
65
|
+
):
|
|
66
|
+
hashes.append(key)
|
|
67
|
+
offsets.append(end - start)
|
|
68
|
+
if not hashes:
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
# Call LMCacheEngine lookup with hashes and offsets
|
|
72
|
+
result = self.lmcache_engine.lookup(
|
|
73
|
+
hashes=hashes,
|
|
74
|
+
offsets=offsets,
|
|
75
|
+
lookup_id=lookup_id,
|
|
76
|
+
pin=True,
|
|
77
|
+
request_configs=request_configs,
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
# For blending mode, pass tokens directly
|
|
81
|
+
result = self.lmcache_engine.lookup(
|
|
82
|
+
tokens=token_ids,
|
|
83
|
+
lookup_id=lookup_id,
|
|
84
|
+
pin=True,
|
|
85
|
+
request_configs=request_configs,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"Error in bypass lookup: {e}")
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
def supports_producer_reuse(self) -> bool:
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
def close(self):
|
|
98
|
+
# No resources to clean up for bypass client
|
|
99
|
+
logger.info("LMCacheBypassLookupClient closed")
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
# Third Party
|
|
6
|
+
import torch
|
|
7
|
+
|
|
8
|
+
# First Party
|
|
9
|
+
from lmcache.logging import init_logger
|
|
10
|
+
from lmcache.utils import CacheEngineKey
|
|
11
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
12
|
+
from lmcache.v1.lookup_client.abstract_client import LookupClientInterface
|
|
13
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
14
|
+
|
|
15
|
+
logger = init_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MooncakeLookupClient(LookupClientInterface):
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
config: LMCacheEngineConfig,
|
|
22
|
+
metadata: LMCacheMetadata,
|
|
23
|
+
master_addr: str,
|
|
24
|
+
):
|
|
25
|
+
# Third Party
|
|
26
|
+
from mooncake.store import MooncakeDistributedStore
|
|
27
|
+
|
|
28
|
+
self.store = MooncakeDistributedStore()
|
|
29
|
+
self.store.setup(
|
|
30
|
+
"localhost",
|
|
31
|
+
"P2PHANDSHAKE",
|
|
32
|
+
0,
|
|
33
|
+
16 * 1024 * 1024,
|
|
34
|
+
"tcp",
|
|
35
|
+
"",
|
|
36
|
+
master_addr,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Initialize token database for processing tokens
|
|
40
|
+
assert isinstance(config, LMCacheEngineConfig), (
|
|
41
|
+
"LMCache v1 configuration is should be passed."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# First Party
|
|
45
|
+
from lmcache.v1.token_database import ChunkedTokenDatabase
|
|
46
|
+
|
|
47
|
+
assert not config.enable_blending, (
|
|
48
|
+
"LMCache v1 blending is not supported in MooncakeLookupClient yet."
|
|
49
|
+
)
|
|
50
|
+
self.token_database = ChunkedTokenDatabase(config, metadata)
|
|
51
|
+
|
|
52
|
+
def lookup(
|
|
53
|
+
self,
|
|
54
|
+
token_ids: Union[torch.Tensor, list[int]],
|
|
55
|
+
lookup_id: Optional[str] = None,
|
|
56
|
+
request_configs: Optional[dict] = None,
|
|
57
|
+
) -> Optional[int]:
|
|
58
|
+
# process token_ids to cacheengine keys
|
|
59
|
+
keys = []
|
|
60
|
+
ends = []
|
|
61
|
+
for start, end, key in self.token_database.process_tokens(token_ids):
|
|
62
|
+
assert isinstance(key, CacheEngineKey)
|
|
63
|
+
keys.append(key.to_string())
|
|
64
|
+
ends.append(end)
|
|
65
|
+
|
|
66
|
+
# Use batch_is_exist to check all keys at once
|
|
67
|
+
# rets is list of int: 1 = found, 0 = not found, -1 = error
|
|
68
|
+
rets = self.store.batch_is_exist(keys)
|
|
69
|
+
|
|
70
|
+
# Find the first key that doesn't exist (ret != 1)
|
|
71
|
+
# This follows the same logic as cache engine's lookup method
|
|
72
|
+
for i, ret in enumerate(rets):
|
|
73
|
+
if ret != 1: # Not found or error
|
|
74
|
+
# Return the end position of the previous chunk
|
|
75
|
+
# If i == 0, no chunks were found, return 0
|
|
76
|
+
return ends[i - 1] if i > 0 else 0
|
|
77
|
+
|
|
78
|
+
# All keys were found, return the last end position
|
|
79
|
+
return ends[-1] if ends else 0
|
|
80
|
+
|
|
81
|
+
def supports_producer_reuse(self) -> bool:
|
|
82
|
+
"""Return True as MooncakeLookupClient supports producer kvcache reuse"""
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
def close(self):
|
|
86
|
+
# nothing here
|
|
87
|
+
pass
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
# Standard
|
|
4
|
+
from typing import Dict, Type
|
|
5
|
+
import importlib
|
|
6
|
+
import inspect
|
|
7
|
+
import pkgutil
|
|
8
|
+
|
|
9
|
+
# First Party
|
|
10
|
+
from lmcache.logging import init_logger
|
|
11
|
+
from lmcache.v1.lookup_client.record_strategies.base import (
|
|
12
|
+
AsyncRecorder,
|
|
13
|
+
RecordStrategy,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = init_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _discover_strategies() -> Dict[str, Type[RecordStrategy]]:
|
|
20
|
+
strategies = {}
|
|
21
|
+
# First Party
|
|
22
|
+
from lmcache.v1.lookup_client import record_strategies
|
|
23
|
+
|
|
24
|
+
for importer, modname, ispkg in pkgutil.iter_modules(
|
|
25
|
+
record_strategies.__path__, record_strategies.__name__ + "."
|
|
26
|
+
):
|
|
27
|
+
try:
|
|
28
|
+
module = importlib.import_module(modname)
|
|
29
|
+
for name, obj in inspect.getmembers(module, inspect.isclass):
|
|
30
|
+
if issubclass(obj, RecordStrategy) and obj is not RecordStrategy:
|
|
31
|
+
# Use module name as strategy name
|
|
32
|
+
strategy_name = modname.split(".")[-1]
|
|
33
|
+
strategies[strategy_name] = obj
|
|
34
|
+
except Exception:
|
|
35
|
+
continue
|
|
36
|
+
return strategies
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_strategies_cache = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _get_strategies() -> Dict[str, Type[RecordStrategy]]:
|
|
43
|
+
global _strategies_cache
|
|
44
|
+
if _strategies_cache is None:
|
|
45
|
+
_strategies_cache = _discover_strategies()
|
|
46
|
+
return _strategies_cache
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def create_record_strategy(config) -> RecordStrategy:
|
|
50
|
+
strategies = _get_strategies()
|
|
51
|
+
strategy_name = config.chunk_statistics_strategy
|
|
52
|
+
chunk_size = config.chunk_size
|
|
53
|
+
if strategy_name not in strategies:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
f"Unknown strategy: {strategy_name}. Available: {list(strategies.keys())}"
|
|
56
|
+
)
|
|
57
|
+
return strategies[strategy_name](config, chunk_size) # type: ignore[call-arg]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def list_record_strategies() -> list[str]:
|
|
61
|
+
return list(_get_strategies().keys())
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
__all__ = [
|
|
65
|
+
"AsyncRecorder",
|
|
66
|
+
"RecordStrategy",
|
|
67
|
+
"create_record_strategy",
|
|
68
|
+
"list_record_strategies",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def __getattr__(name):
|
|
73
|
+
strategies = _get_strategies()
|
|
74
|
+
for strategy_class in strategies.values():
|
|
75
|
+
if strategy_class.__name__ == name:
|
|
76
|
+
return strategy_class
|
|
77
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
# Standard
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import Any, Union
|
|
6
|
+
import queue
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
# Third Party
|
|
11
|
+
import torch
|
|
12
|
+
|
|
13
|
+
# First Party
|
|
14
|
+
from lmcache.logging import init_logger
|
|
15
|
+
from lmcache.v1.token_database import ChunkedTokenDatabase
|
|
16
|
+
|
|
17
|
+
logger = init_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RecordStrategy(ABC):
|
|
21
|
+
"""Base class for chunk recording strategies."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, chunk_size: int):
|
|
24
|
+
"""Initialize the recording strategy.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
chunk_size: Size of each token chunk for processing
|
|
28
|
+
"""
|
|
29
|
+
self.chunk_size = chunk_size
|
|
30
|
+
self.total_chunks = 0
|
|
31
|
+
self.unique_chunks_count = 0
|
|
32
|
+
self.lock = threading.RLock()
|
|
33
|
+
|
|
34
|
+
self._token_db = ChunkedTokenDatabase()
|
|
35
|
+
self._token_db.chunk_size = chunk_size
|
|
36
|
+
|
|
37
|
+
def _compute_chunk_hashes(self, token_ids: list[int]) -> list[int]:
|
|
38
|
+
"""Compute prefix hashes for all chunks using ChunkedTokenDatabase.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
token_ids: List of token IDs to process
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
List of hash values (integers) for each chunk.
|
|
45
|
+
"""
|
|
46
|
+
chunk_hashes = []
|
|
47
|
+
for _, _, hash_val in self._token_db.process_tokens(
|
|
48
|
+
tokens=token_ids, make_key=False
|
|
49
|
+
):
|
|
50
|
+
chunk_hashes.append(hash_val)
|
|
51
|
+
return chunk_hashes
|
|
52
|
+
|
|
53
|
+
def _compute_chunk_hashes_hex(self, token_ids: list[int]) -> list[str]:
|
|
54
|
+
"""Compute prefix hashes for all chunks and return as hex strings.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
token_ids: List of token IDs to process
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
List of hash values (hex strings) for each chunk.
|
|
61
|
+
"""
|
|
62
|
+
chunk_hashes = []
|
|
63
|
+
for hash_val in self._compute_chunk_hashes(token_ids):
|
|
64
|
+
if hash_val < 0:
|
|
65
|
+
hash_val = hash_val & ((1 << 64) - 1)
|
|
66
|
+
chunk_hashes.append(hex(hash_val))
|
|
67
|
+
return chunk_hashes
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def preprocess(self, token_ids: list[int]) -> Any:
|
|
71
|
+
"""Preprocess token IDs before recording.
|
|
72
|
+
|
|
73
|
+
This method is called to transform raw token IDs into a format suitable
|
|
74
|
+
for the specific recording strategy. For example, it might compute hash
|
|
75
|
+
positions for a bloom filter or convert hashes to hex strings for file storage.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
token_ids: List of token IDs to preprocess
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Preprocessed data in strategy-specific format. The return type depends
|
|
82
|
+
on the concrete strategy implementation.
|
|
83
|
+
"""
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def record(self, preprocessed_data: Any, lookup_id: str) -> None:
|
|
88
|
+
"""Record the preprocessed chunk data.
|
|
89
|
+
|
|
90
|
+
This method performs the actual recording operation using the preprocessed
|
|
91
|
+
data. It should update internal statistics (total_chunks,
|
|
92
|
+
unique_chunks_count) and perform strategy-specific recording (e.g., update
|
|
93
|
+
bloom filter, write to file).
|
|
94
|
+
|
|
95
|
+
This method must be thread-safe as it may be called from async worker
|
|
96
|
+
threads.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
preprocessed_data: Data returned from preprocess() method
|
|
100
|
+
lookup_id: Unique identifier for this lookup operation
|
|
101
|
+
"""
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def reset(self) -> None:
|
|
106
|
+
"""Reset all statistics and internal state.
|
|
107
|
+
|
|
108
|
+
This method should clear all recorded data and reset counters to their
|
|
109
|
+
initial state. It should be safe to call at any time.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def get_statistics(self) -> dict:
|
|
113
|
+
"""Get current statistics.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Dictionary containing statistics about recorded chunks, including:
|
|
117
|
+
- total_chunks: Total number of chunks processed
|
|
118
|
+
- unique_chunks: Number of unique chunks seen
|
|
119
|
+
- duplicate_chunks: Number of duplicate chunks
|
|
120
|
+
- reuse_rate: Ratio of duplicate to total chunks
|
|
121
|
+
"""
|
|
122
|
+
with self.lock:
|
|
123
|
+
dup = self.total_chunks - self.unique_chunks_count
|
|
124
|
+
base_stats = {
|
|
125
|
+
"total_chunks": self.total_chunks,
|
|
126
|
+
"unique_chunks": self.unique_chunks_count,
|
|
127
|
+
"duplicate_chunks": dup,
|
|
128
|
+
"reuse_rate": dup / self.total_chunks if self.total_chunks > 0 else 0.0,
|
|
129
|
+
}
|
|
130
|
+
return base_stats
|
|
131
|
+
|
|
132
|
+
def setup_metrics(self, prometheus_logger) -> None:
|
|
133
|
+
"""Setup Prometheus metrics for this strategy.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
prometheus_logger: Prometheus logger instance to register metrics with
|
|
137
|
+
"""
|
|
138
|
+
prometheus_logger.chunk_statistics_total_chunks.set_function(
|
|
139
|
+
lambda: self.total_chunks
|
|
140
|
+
)
|
|
141
|
+
prometheus_logger.chunk_statistics_unique_chunks.set_function(
|
|
142
|
+
lambda: self.unique_chunks_count
|
|
143
|
+
)
|
|
144
|
+
prometheus_logger.chunk_statistics_reuse_rate.set_function(
|
|
145
|
+
lambda: (self.total_chunks - self.unique_chunks_count) / self.total_chunks
|
|
146
|
+
if self.total_chunks > 0
|
|
147
|
+
else 0.0
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def close(self) -> None: # noqa: B027
|
|
151
|
+
"""Clean up resources.
|
|
152
|
+
|
|
153
|
+
This method is called when the strategy is no longer needed. Subclasses
|
|
154
|
+
should override this to clean up any resources (e.g., close file handles).
|
|
155
|
+
"""
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class AsyncRecorder:
|
|
160
|
+
"""Async processing infrastructure for RecordStrategy.
|
|
161
|
+
|
|
162
|
+
This class wraps a RecordStrategy and provides asynchronous processing
|
|
163
|
+
capabilities using a background worker thread and queue.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def __init__(
|
|
167
|
+
self,
|
|
168
|
+
strategy: RecordStrategy,
|
|
169
|
+
queue_capacity: int = 100000,
|
|
170
|
+
preprocess_in_caller: bool = False,
|
|
171
|
+
):
|
|
172
|
+
"""Initialize the async recorder.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
strategy: The RecordStrategy instance to wrap
|
|
176
|
+
queue_capacity: Maximum number of items in the async queue
|
|
177
|
+
preprocess_in_caller: If True, preprocess in caller thread before queueing;
|
|
178
|
+
if False, preprocess in worker thread
|
|
179
|
+
"""
|
|
180
|
+
self.strategy = strategy
|
|
181
|
+
self.queue_capacity = queue_capacity
|
|
182
|
+
self.preprocess_in_caller = preprocess_in_caller
|
|
183
|
+
|
|
184
|
+
self.async_queue: queue.Queue = queue.Queue(maxsize=queue_capacity)
|
|
185
|
+
self.async_shutdown = False
|
|
186
|
+
self.queue_full_blocks = 0
|
|
187
|
+
self.queue_max_size = 0
|
|
188
|
+
self.lock = threading.RLock()
|
|
189
|
+
|
|
190
|
+
self.async_worker_thread = threading.Thread(
|
|
191
|
+
target=self._async_worker,
|
|
192
|
+
daemon=True,
|
|
193
|
+
name=f"{strategy.__class__.__name__}AsyncWorker",
|
|
194
|
+
)
|
|
195
|
+
self.async_worker_thread.start()
|
|
196
|
+
|
|
197
|
+
def _async_worker(self) -> None:
|
|
198
|
+
"""Background worker thread that processes queued items."""
|
|
199
|
+
while not self.async_shutdown:
|
|
200
|
+
try:
|
|
201
|
+
item = self.async_queue.get(timeout=0.1)
|
|
202
|
+
if item is None:
|
|
203
|
+
break
|
|
204
|
+
data, lookup_id = item
|
|
205
|
+
if self.preprocess_in_caller:
|
|
206
|
+
preprocessed_data = data
|
|
207
|
+
else:
|
|
208
|
+
preprocessed_data = self.strategy.preprocess(data)
|
|
209
|
+
self.strategy.record(preprocessed_data, lookup_id)
|
|
210
|
+
self.async_queue.task_done()
|
|
211
|
+
except queue.Empty:
|
|
212
|
+
continue
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.error("Async worker error: %s", e, exc_info=True)
|
|
215
|
+
|
|
216
|
+
# Process remaining items
|
|
217
|
+
while not self.async_queue.empty():
|
|
218
|
+
try:
|
|
219
|
+
item = self.async_queue.get_nowait()
|
|
220
|
+
if item is not None:
|
|
221
|
+
data, lookup_id = item
|
|
222
|
+
if self.preprocess_in_caller:
|
|
223
|
+
preprocessed_data = data
|
|
224
|
+
else:
|
|
225
|
+
preprocessed_data = self.strategy.preprocess(data)
|
|
226
|
+
self.strategy.record(preprocessed_data, lookup_id)
|
|
227
|
+
self.async_queue.task_done()
|
|
228
|
+
except (queue.Empty, Exception):
|
|
229
|
+
break
|
|
230
|
+
|
|
231
|
+
def record_async(
|
|
232
|
+
self, token_ids: Union[torch.Tensor, list[int]], lookup_id: str
|
|
233
|
+
) -> None:
|
|
234
|
+
"""Record token IDs asynchronously.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
token_ids: Token IDs to record (tensor or list)
|
|
238
|
+
lookup_id: Unique identifier for this lookup operation
|
|
239
|
+
"""
|
|
240
|
+
if isinstance(token_ids, torch.Tensor):
|
|
241
|
+
token_ids = token_ids.tolist()
|
|
242
|
+
|
|
243
|
+
if self.preprocess_in_caller:
|
|
244
|
+
data = self.strategy.preprocess(token_ids)
|
|
245
|
+
else:
|
|
246
|
+
data = token_ids
|
|
247
|
+
|
|
248
|
+
self._queue_item((data, lookup_id))
|
|
249
|
+
|
|
250
|
+
def _queue_item(self, item, timeout: float = 10.0) -> None:
|
|
251
|
+
"""Add item to async queue with timeout handling."""
|
|
252
|
+
try:
|
|
253
|
+
self.async_queue.put(item, block=True, timeout=timeout)
|
|
254
|
+
except queue.Full:
|
|
255
|
+
with self.lock:
|
|
256
|
+
self.queue_full_blocks += 1
|
|
257
|
+
self.async_queue.put(item, block=True)
|
|
258
|
+
|
|
259
|
+
def get_statistics(self) -> dict:
|
|
260
|
+
"""Get statistics including async queue metrics.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Dictionary containing strategy statistics plus async queue metrics
|
|
264
|
+
"""
|
|
265
|
+
stats = self.strategy.get_statistics()
|
|
266
|
+
with self.lock:
|
|
267
|
+
queue_size = self.async_queue.qsize()
|
|
268
|
+
self.queue_max_size = max(self.queue_max_size, queue_size)
|
|
269
|
+
stats["async_queue"] = {
|
|
270
|
+
"capacity": self.queue_capacity,
|
|
271
|
+
"current_size": queue_size,
|
|
272
|
+
"max_size_reached": self.queue_max_size,
|
|
273
|
+
"full_blocks": self.queue_full_blocks,
|
|
274
|
+
"utilization": queue_size / self.queue_capacity
|
|
275
|
+
if self.queue_capacity > 0
|
|
276
|
+
else 0.0,
|
|
277
|
+
}
|
|
278
|
+
return stats
|
|
279
|
+
|
|
280
|
+
def wait_for_completion(self, timeout: float = 5.0) -> bool:
|
|
281
|
+
"""Wait for async queue to be processed.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
timeout: Maximum time to wait in seconds
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
True if queue is empty, False if timeout occurred
|
|
288
|
+
"""
|
|
289
|
+
start_time = time.time()
|
|
290
|
+
while time.time() - start_time < timeout:
|
|
291
|
+
if self.async_queue.empty():
|
|
292
|
+
time.sleep(0.01)
|
|
293
|
+
if self.async_queue.empty():
|
|
294
|
+
return True
|
|
295
|
+
time.sleep(0.01)
|
|
296
|
+
return self.async_queue.empty()
|
|
297
|
+
|
|
298
|
+
def reset(self) -> None:
|
|
299
|
+
"""Reset strategy and clear async queue."""
|
|
300
|
+
self.wait_for_completion(timeout=5.0)
|
|
301
|
+
with self.lock:
|
|
302
|
+
self.strategy.reset()
|
|
303
|
+
self.queue_full_blocks = 0
|
|
304
|
+
self.queue_max_size = 0
|
|
305
|
+
self._clear_queue()
|
|
306
|
+
|
|
307
|
+
def _clear_queue(self) -> None:
|
|
308
|
+
"""Clear all items from async queue."""
|
|
309
|
+
while not self.async_queue.empty():
|
|
310
|
+
try:
|
|
311
|
+
self.async_queue.get_nowait()
|
|
312
|
+
except queue.Empty:
|
|
313
|
+
break
|
|
314
|
+
|
|
315
|
+
def close(self) -> None:
|
|
316
|
+
"""Shutdown async worker and clean up resources."""
|
|
317
|
+
self.async_shutdown = True
|
|
318
|
+
try:
|
|
319
|
+
self.async_queue.put(None, block=False)
|
|
320
|
+
except queue.Full:
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
self.async_worker_thread.join(timeout=5.0)
|
|
324
|
+
if self.async_worker_thread.is_alive():
|
|
325
|
+
logger.warning("Async worker did not stop gracefully")
|
|
326
|
+
|
|
327
|
+
self.strategy.close()
|