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
lmcache/v1/protocol.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import IntEnum, auto
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
import struct
|
|
7
|
+
|
|
8
|
+
# Third Party
|
|
9
|
+
import torch
|
|
10
|
+
|
|
11
|
+
# First Party
|
|
12
|
+
from lmcache.logging import init_logger
|
|
13
|
+
from lmcache.utils import CacheEngineKey, LayerCacheEngineKey, parse_cache_key
|
|
14
|
+
from lmcache.v1.memory_management import MemoryFormat
|
|
15
|
+
|
|
16
|
+
logger = init_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
MAX_KEY_LENGTH = 150
|
|
20
|
+
REMOTE_METADATA_FMT: Optional[str] = None
|
|
21
|
+
REMOTE_METADATA_BYTES: Optional[int] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ClientCommand(IntEnum):
|
|
25
|
+
PUT = auto()
|
|
26
|
+
GET = auto()
|
|
27
|
+
EXIST = auto()
|
|
28
|
+
LIST = auto()
|
|
29
|
+
HEALTH = auto()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ServerReturnCode(IntEnum):
|
|
33
|
+
SUCCESS = 200
|
|
34
|
+
FAIL = 400
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
DTYPE_TO_INT = {
|
|
38
|
+
None: 0,
|
|
39
|
+
torch.half: 1,
|
|
40
|
+
torch.float16: 2,
|
|
41
|
+
torch.bfloat16: 3,
|
|
42
|
+
torch.float: 4,
|
|
43
|
+
torch.float32: 4,
|
|
44
|
+
torch.float64: 5,
|
|
45
|
+
torch.double: 5,
|
|
46
|
+
torch.uint8: 6,
|
|
47
|
+
torch.float8_e4m3fn: 7,
|
|
48
|
+
torch.float8_e5m2: 8,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
INT_TO_DTYPE = {
|
|
52
|
+
0: None,
|
|
53
|
+
1: torch.half,
|
|
54
|
+
2: torch.float16,
|
|
55
|
+
3: torch.bfloat16,
|
|
56
|
+
4: torch.float,
|
|
57
|
+
5: torch.float64,
|
|
58
|
+
6: torch.uint8,
|
|
59
|
+
7: torch.float8_e4m3fn,
|
|
60
|
+
8: torch.float8_e5m2,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# TODO (Jiayi): Add more backends
|
|
64
|
+
LOCATION_TO_INT = {
|
|
65
|
+
None: 0,
|
|
66
|
+
"LocalCPUBackend": 1,
|
|
67
|
+
"LocalDiskBackend": 2,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
INT_TO_LOCATION = {
|
|
71
|
+
0: None,
|
|
72
|
+
1: "LocalCPUBackend",
|
|
73
|
+
2: "LocalDiskBackend",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def init_remote_metadata_info(num_groups: int):
|
|
78
|
+
global REMOTE_METADATA_FMT
|
|
79
|
+
global REMOTE_METADATA_BYTES
|
|
80
|
+
# length, fmt, (dtype, shape0, shape1, shape2, shape3) * num_groups
|
|
81
|
+
fmt_length = 2 + 5 * num_groups
|
|
82
|
+
REMOTE_METADATA_FMT = "i" * fmt_length
|
|
83
|
+
REMOTE_METADATA_BYTES = 4 * fmt_length
|
|
84
|
+
logger.info(
|
|
85
|
+
"init remote metadata info with groups: %s, "
|
|
86
|
+
"remote metadata fmt: %s, remote metadata bytes: %s",
|
|
87
|
+
num_groups,
|
|
88
|
+
REMOTE_METADATA_FMT,
|
|
89
|
+
REMOTE_METADATA_BYTES,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_remote_metadata_bytes():
|
|
94
|
+
global REMOTE_METADATA_BYTES
|
|
95
|
+
assert REMOTE_METADATA_BYTES is not None
|
|
96
|
+
return REMOTE_METADATA_BYTES
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def pad_shape_to_4d(shape: torch.Size) -> list[int]:
|
|
100
|
+
"""Pad a shape with fewer than 4 dimensions to 4D using trailing
|
|
101
|
+
zeros.
|
|
102
|
+
|
|
103
|
+
Shapes that are already 4D are returned as-is. For shapes with
|
|
104
|
+
fewer dimensions the missing trailing slots are filled with ``0``.
|
|
105
|
+
This is consistent with the convention used by
|
|
106
|
+
:class:`BinaryMemoryObj` (``[length, 0, 0, 0]``).
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
shape: The original tensor shape (1-D to 4-D).
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
A list of exactly 4 integers representing the padded shape.
|
|
113
|
+
|
|
114
|
+
Raises:
|
|
115
|
+
AssertionError: If the shape has more than 4 dimensions.
|
|
116
|
+
"""
|
|
117
|
+
assert len(shape) <= 4, (
|
|
118
|
+
f"Shape dimension must be <= 4 for serialization, got {len(shape)}"
|
|
119
|
+
)
|
|
120
|
+
if len(shape) == 4:
|
|
121
|
+
return list(shape)
|
|
122
|
+
|
|
123
|
+
padded = list(shape) + [0] * (4 - len(shape))
|
|
124
|
+
return padded
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def strip_shape_padding(
|
|
128
|
+
dims: list[int],
|
|
129
|
+
fmt: Optional[MemoryFormat] = MemoryFormat.UNDEFINED,
|
|
130
|
+
) -> torch.Size:
|
|
131
|
+
"""Strip trailing-zero padding that was added by
|
|
132
|
+
:func:`pad_shape_to_4d`.
|
|
133
|
+
|
|
134
|
+
Trailing zeros are removed so that the original dimensionality is
|
|
135
|
+
restored. At least one dimension is always preserved.
|
|
136
|
+
|
|
137
|
+
For ``BINARY`` and ``BINARY_BUFFER`` formats, the shape is returned
|
|
138
|
+
as-is because these formats inherently use 4-D shapes with zero
|
|
139
|
+
padding (e.g., ``[length, 0, 0, 0]``).
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
dims: A list of 4 integers read from the serialized format.
|
|
143
|
+
fmt: The memory format of the serialized data.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
A :class:`torch.Size` with the padding removed.
|
|
147
|
+
"""
|
|
148
|
+
if fmt in (MemoryFormat.BINARY, MemoryFormat.BINARY_BUFFER):
|
|
149
|
+
# These formats use 4D shapes with legitimate zero dimensions.
|
|
150
|
+
# Skip stripping to preserve the original shape.
|
|
151
|
+
return torch.Size(dims)
|
|
152
|
+
|
|
153
|
+
end = len(dims)
|
|
154
|
+
while end > 1 and dims[end - 1] == 0:
|
|
155
|
+
end -= 1
|
|
156
|
+
return torch.Size(dims[:end])
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass
|
|
160
|
+
class RemoteMetadata:
|
|
161
|
+
length: int
|
|
162
|
+
shapes: list[torch.Size]
|
|
163
|
+
dtypes: list[torch.dtype]
|
|
164
|
+
fmt: MemoryFormat
|
|
165
|
+
|
|
166
|
+
def _prepare_params(self):
|
|
167
|
+
params = [self.length, int(self.fmt.value)]
|
|
168
|
+
for shape, dtype in zip(self.shapes, self.dtypes, strict=True):
|
|
169
|
+
padded = pad_shape_to_4d(shape)
|
|
170
|
+
params.append(DTYPE_TO_INT[dtype])
|
|
171
|
+
params.extend(padded)
|
|
172
|
+
return params
|
|
173
|
+
|
|
174
|
+
def serialize_into(self, buffer):
|
|
175
|
+
assert REMOTE_METADATA_FMT is not None
|
|
176
|
+
params = self._prepare_params()
|
|
177
|
+
struct.pack_into(REMOTE_METADATA_FMT, buffer, 0, *params)
|
|
178
|
+
|
|
179
|
+
def serialize(self) -> bytes:
|
|
180
|
+
assert REMOTE_METADATA_FMT is not None
|
|
181
|
+
params = self._prepare_params()
|
|
182
|
+
packed_bytes = struct.pack(REMOTE_METADATA_FMT, *params)
|
|
183
|
+
return packed_bytes
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def deserialize(s: bytes) -> "RemoteMetadata":
|
|
187
|
+
assert REMOTE_METADATA_FMT is not None
|
|
188
|
+
# length, fmt, (dtype, shape0, shape1, shape2, shape3) * num_groups
|
|
189
|
+
result = struct.unpack_from(REMOTE_METADATA_FMT, s)
|
|
190
|
+
length = result[0]
|
|
191
|
+
memory_fmt = MemoryFormat(result[1])
|
|
192
|
+
shapes = []
|
|
193
|
+
dtypes = []
|
|
194
|
+
for i in range(2, len(result), 5):
|
|
195
|
+
dims = list(result[i + 1 : i + 5])
|
|
196
|
+
shapes.append(strip_shape_padding(dims, memory_fmt))
|
|
197
|
+
dtypes.append(INT_TO_DTYPE[result[i]])
|
|
198
|
+
|
|
199
|
+
return RemoteMetadata(
|
|
200
|
+
length,
|
|
201
|
+
shapes,
|
|
202
|
+
dtypes,
|
|
203
|
+
memory_fmt,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# TODO(Jiayi): Server and client message can be merged into one.
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class ClientMetaMessage:
|
|
212
|
+
"""
|
|
213
|
+
Request message from LMCache workers or servers.
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
command: ClientCommand
|
|
217
|
+
key: Union[CacheEngineKey, LayerCacheEngineKey]
|
|
218
|
+
length: int
|
|
219
|
+
fmt: MemoryFormat
|
|
220
|
+
dtype: Optional[torch.dtype]
|
|
221
|
+
shape: torch.Size
|
|
222
|
+
location: Optional[str] = None
|
|
223
|
+
|
|
224
|
+
def serialize(self) -> bytes:
|
|
225
|
+
key_str = self.key.to_string()
|
|
226
|
+
assert len(key_str) <= MAX_KEY_LENGTH, (
|
|
227
|
+
f"Key length {len(key_str)} exceeds maximum {MAX_KEY_LENGTH}"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# NOTE(Jiayi): 4 is the maximum dimension of memory object.
|
|
231
|
+
# Pass in shape [x, 0, 0, 0] if it is a bytes memory object
|
|
232
|
+
padded = pad_shape_to_4d(self.shape)
|
|
233
|
+
|
|
234
|
+
packed_bytes = struct.pack(
|
|
235
|
+
f"iiiiiiiii{MAX_KEY_LENGTH}s",
|
|
236
|
+
self.command.value,
|
|
237
|
+
self.length,
|
|
238
|
+
int(self.fmt.value),
|
|
239
|
+
DTYPE_TO_INT[self.dtype],
|
|
240
|
+
LOCATION_TO_INT[self.location],
|
|
241
|
+
padded[0],
|
|
242
|
+
padded[1],
|
|
243
|
+
padded[2],
|
|
244
|
+
padded[3],
|
|
245
|
+
key_str.encode().ljust(MAX_KEY_LENGTH),
|
|
246
|
+
)
|
|
247
|
+
return packed_bytes
|
|
248
|
+
|
|
249
|
+
@staticmethod
|
|
250
|
+
def deserialize(s: bytes) -> "ClientMetaMessage":
|
|
251
|
+
command, length, fmt, dtype, location, shape0, shape1, shape2, shape3, key = (
|
|
252
|
+
struct.unpack(f"iiiiiiiii{MAX_KEY_LENGTH}s", s)
|
|
253
|
+
)
|
|
254
|
+
shape = strip_shape_padding([shape0, shape1, shape2, shape3], MemoryFormat(fmt))
|
|
255
|
+
return ClientMetaMessage(
|
|
256
|
+
ClientCommand(command),
|
|
257
|
+
parse_cache_key(key.decode().strip()),
|
|
258
|
+
length,
|
|
259
|
+
MemoryFormat(fmt),
|
|
260
|
+
INT_TO_DTYPE[dtype],
|
|
261
|
+
shape,
|
|
262
|
+
INT_TO_LOCATION[location],
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
@staticmethod
|
|
266
|
+
def packlength() -> int:
|
|
267
|
+
# NOTE: 9 is the number of integers
|
|
268
|
+
return 4 * 9 + MAX_KEY_LENGTH
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@dataclass
|
|
272
|
+
class ServerMetaMessage:
|
|
273
|
+
"""
|
|
274
|
+
Reply message from LMCache workers or servers.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
code: ServerReturnCode
|
|
278
|
+
length: int
|
|
279
|
+
fmt: MemoryFormat
|
|
280
|
+
dtype: Optional[torch.dtype]
|
|
281
|
+
shape: torch.Size
|
|
282
|
+
location: Optional[str] = None
|
|
283
|
+
|
|
284
|
+
def serialize(self) -> bytes:
|
|
285
|
+
padded = pad_shape_to_4d(self.shape)
|
|
286
|
+
packed_bytes = struct.pack(
|
|
287
|
+
"iiiiiiiii",
|
|
288
|
+
self.code.value,
|
|
289
|
+
self.length,
|
|
290
|
+
int(self.fmt.value),
|
|
291
|
+
DTYPE_TO_INT[self.dtype],
|
|
292
|
+
padded[0],
|
|
293
|
+
padded[1],
|
|
294
|
+
padded[2],
|
|
295
|
+
padded[3],
|
|
296
|
+
LOCATION_TO_INT[self.location],
|
|
297
|
+
)
|
|
298
|
+
return packed_bytes
|
|
299
|
+
|
|
300
|
+
@staticmethod
|
|
301
|
+
def packlength() -> int:
|
|
302
|
+
return 4 * 9
|
|
303
|
+
|
|
304
|
+
@staticmethod
|
|
305
|
+
def deserialize(s: bytes) -> "ServerMetaMessage":
|
|
306
|
+
code, length, fmt, dtype, shape0, shape1, shape2, shape3, location = (
|
|
307
|
+
struct.unpack("iiiiiiiii", s)
|
|
308
|
+
)
|
|
309
|
+
shape = strip_shape_padding([shape0, shape1, shape2, shape3], MemoryFormat(fmt))
|
|
310
|
+
return ServerMetaMessage(
|
|
311
|
+
ServerReturnCode(code),
|
|
312
|
+
length,
|
|
313
|
+
MemoryFormat(fmt),
|
|
314
|
+
INT_TO_DTYPE[dtype],
|
|
315
|
+
shape,
|
|
316
|
+
INT_TO_LOCATION[location],
|
|
317
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# First Party
|
|
3
|
+
from lmcache.v1.rpc.transport import (
|
|
4
|
+
RpcClientTransport,
|
|
5
|
+
RpcServerTransport,
|
|
6
|
+
)
|
|
7
|
+
from lmcache.v1.rpc.zmq_transport import (
|
|
8
|
+
ZmqReqRepClientTransport,
|
|
9
|
+
ZmqRouterServerTransport,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"RpcClientTransport",
|
|
14
|
+
"RpcServerTransport",
|
|
15
|
+
"ZmqReqRepClientTransport",
|
|
16
|
+
"ZmqRouterServerTransport",
|
|
17
|
+
]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Abstract transport interfaces for RPC communication.
|
|
3
|
+
|
|
4
|
+
These interfaces decouple the lookup client/server business logic
|
|
5
|
+
from the underlying communication mechanism (e.g., ZMQ, gRPC).
|
|
6
|
+
|
|
7
|
+
The transport layer is responsible for serialization/deserialization
|
|
8
|
+
of structured data, so that upper-layer business logic only works
|
|
9
|
+
with Python objects (no raw bytes).
|
|
10
|
+
|
|
11
|
+
TODO: Implement async transport interfaces for
|
|
12
|
+
LMCacheAsyncLookupClient/Server.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# Standard
|
|
16
|
+
from typing import Any
|
|
17
|
+
import abc
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RpcClientTransport(abc.ABC):
|
|
21
|
+
"""Abstract transport for RPC client-side communication.
|
|
22
|
+
|
|
23
|
+
Handles sending requests to multiple server ranks and
|
|
24
|
+
collecting responses. The transport is responsible for
|
|
25
|
+
connection management, retries, and error recovery.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def send_and_recv_all(
|
|
30
|
+
self,
|
|
31
|
+
msg: list[Any],
|
|
32
|
+
) -> list[bytes]:
|
|
33
|
+
"""Send structured data to all ranks and collect
|
|
34
|
+
responses.
|
|
35
|
+
|
|
36
|
+
The transport is responsible for serializing each
|
|
37
|
+
element of msg before sending and deserializing
|
|
38
|
+
responses.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
msg: List of Python objects to send as
|
|
42
|
+
message frames. Each element will be
|
|
43
|
+
serialized by the transport's codec.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of raw response bytes, one per rank.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
RpcTransportError: If communication fails
|
|
50
|
+
after retries.
|
|
51
|
+
"""
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
@abc.abstractmethod
|
|
56
|
+
def world_size(self) -> int:
|
|
57
|
+
"""Return the number of server ranks."""
|
|
58
|
+
raise NotImplementedError
|
|
59
|
+
|
|
60
|
+
@abc.abstractmethod
|
|
61
|
+
def close(self) -> None:
|
|
62
|
+
"""Close the transport and release resources."""
|
|
63
|
+
raise NotImplementedError
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RpcServerTransport(abc.ABC):
|
|
67
|
+
"""Abstract transport for RPC server-side communication.
|
|
68
|
+
|
|
69
|
+
Handles receiving requests and sending responses back.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@abc.abstractmethod
|
|
73
|
+
def recv_request(
|
|
74
|
+
self,
|
|
75
|
+
) -> tuple[bytes, list[Any]] | None:
|
|
76
|
+
"""Receive a request from a client.
|
|
77
|
+
|
|
78
|
+
The transport deserializes each data frame into
|
|
79
|
+
a Python object before returning.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
A tuple of (identity, data_frames) on success
|
|
83
|
+
where data_frames contains deserialized Python
|
|
84
|
+
objects, or None on timeout / no data available.
|
|
85
|
+
"""
|
|
86
|
+
raise NotImplementedError
|
|
87
|
+
|
|
88
|
+
@abc.abstractmethod
|
|
89
|
+
def send_response(
|
|
90
|
+
self,
|
|
91
|
+
identity: bytes,
|
|
92
|
+
response: bytes,
|
|
93
|
+
) -> None:
|
|
94
|
+
"""Send a response back to the client.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
identity: The client identity from recv_request.
|
|
98
|
+
response: The raw response bytes to send.
|
|
99
|
+
"""
|
|
100
|
+
raise NotImplementedError
|
|
101
|
+
|
|
102
|
+
@abc.abstractmethod
|
|
103
|
+
def close(self) -> None:
|
|
104
|
+
"""Close the transport and release resources."""
|
|
105
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""ZMQ-based transport implementations for RPC communication.
|
|
3
|
+
|
|
4
|
+
TODO: Implement ZmqPushTransport and ZmqPullTransport for
|
|
5
|
+
LMCacheAsyncLookupClient/Server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Standard
|
|
9
|
+
from collections import namedtuple
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
# Third Party
|
|
13
|
+
import msgspec
|
|
14
|
+
import zmq
|
|
15
|
+
|
|
16
|
+
# First Party
|
|
17
|
+
from lmcache.logging import init_logger
|
|
18
|
+
from lmcache.v1.rpc.transport import (
|
|
19
|
+
RpcClientTransport,
|
|
20
|
+
RpcServerTransport,
|
|
21
|
+
)
|
|
22
|
+
from lmcache.v1.rpc_utils import (
|
|
23
|
+
get_zmq_context,
|
|
24
|
+
get_zmq_socket,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
logger = init_logger(__name__)
|
|
28
|
+
|
|
29
|
+
SocketParams = namedtuple("SocketParams", ["socket_path", "rank"])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ZmqReqRepClientTransport(RpcClientTransport):
|
|
33
|
+
"""ZMQ REQ socket transport for synchronous RPC clients.
|
|
34
|
+
|
|
35
|
+
Manages multiple REQ sockets (one per server rank) and
|
|
36
|
+
provides send/recv with timeout + automatic socket
|
|
37
|
+
recreation on failure.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
socket_params: list[SocketParams],
|
|
43
|
+
timeout_ms: int,
|
|
44
|
+
):
|
|
45
|
+
self.ctx = get_zmq_context(use_asyncio=False)
|
|
46
|
+
self.socket_params = socket_params
|
|
47
|
+
self.timeout_ms = timeout_ms
|
|
48
|
+
self._world_size = len(socket_params)
|
|
49
|
+
self.encoder = msgspec.msgpack.Encoder()
|
|
50
|
+
|
|
51
|
+
self.sockets: list[zmq.Socket] = []
|
|
52
|
+
for params in self.socket_params:
|
|
53
|
+
logger.info(
|
|
54
|
+
"Transport connecting to rank %s with socket path %s",
|
|
55
|
+
params.rank,
|
|
56
|
+
params.socket_path,
|
|
57
|
+
)
|
|
58
|
+
socket = self._create_socket(params)
|
|
59
|
+
self.sockets.append(socket)
|
|
60
|
+
|
|
61
|
+
def _create_socket(self, params: SocketParams) -> zmq.Socket:
|
|
62
|
+
"""Create and configure a REQ socket."""
|
|
63
|
+
socket = get_zmq_socket(
|
|
64
|
+
self.ctx,
|
|
65
|
+
params.socket_path,
|
|
66
|
+
"ipc",
|
|
67
|
+
zmq.REQ,
|
|
68
|
+
"connect",
|
|
69
|
+
)
|
|
70
|
+
socket.setsockopt(zmq.RCVTIMEO, self.timeout_ms)
|
|
71
|
+
socket.setsockopt(zmq.SNDTIMEO, self.timeout_ms)
|
|
72
|
+
return socket
|
|
73
|
+
|
|
74
|
+
def _recreate_all_sockets(self) -> None:
|
|
75
|
+
"""Recreate all sockets after a failure."""
|
|
76
|
+
for rank_idx in range(self._world_size):
|
|
77
|
+
old_socket = self.sockets[rank_idx]
|
|
78
|
+
if old_socket is not None:
|
|
79
|
+
try:
|
|
80
|
+
old_socket.close(linger=0)
|
|
81
|
+
except zmq.ZMQError as e:
|
|
82
|
+
logger.warning(
|
|
83
|
+
"ZMQ error closing old socket for rank %s: %s",
|
|
84
|
+
rank_idx,
|
|
85
|
+
e,
|
|
86
|
+
)
|
|
87
|
+
except AttributeError:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
params = self.socket_params[rank_idx]
|
|
91
|
+
logger.info(
|
|
92
|
+
"Recreating socket for rank %s with path %s",
|
|
93
|
+
params.rank,
|
|
94
|
+
params.socket_path,
|
|
95
|
+
)
|
|
96
|
+
self.sockets[rank_idx] = self._create_socket(params)
|
|
97
|
+
|
|
98
|
+
def send_and_recv_all(
|
|
99
|
+
self,
|
|
100
|
+
msg: list[Any],
|
|
101
|
+
) -> list[bytes]:
|
|
102
|
+
"""Send msg to all ranks and collect responses.
|
|
103
|
+
|
|
104
|
+
Each element of msg is serialized via msgpack before
|
|
105
|
+
sending. On timeout or ZMQ error, recreates all
|
|
106
|
+
sockets and returns an empty list.
|
|
107
|
+
"""
|
|
108
|
+
encoded = [self.encoder.encode(m) for m in msg]
|
|
109
|
+
results: list[bytes] = []
|
|
110
|
+
failed_rank = -1
|
|
111
|
+
try:
|
|
112
|
+
for i in range(self._world_size):
|
|
113
|
+
failed_rank = i
|
|
114
|
+
self.sockets[i].send_multipart(encoded, copy=False)
|
|
115
|
+
|
|
116
|
+
for i in range(self._world_size):
|
|
117
|
+
failed_rank = i
|
|
118
|
+
resp = self.sockets[i].recv()
|
|
119
|
+
results.append(resp)
|
|
120
|
+
except zmq.Again as e:
|
|
121
|
+
logger.error(
|
|
122
|
+
"Timeout occurred for rank %s, recreating all sockets. Error: %s",
|
|
123
|
+
failed_rank,
|
|
124
|
+
e,
|
|
125
|
+
)
|
|
126
|
+
self._recreate_all_sockets()
|
|
127
|
+
return []
|
|
128
|
+
except zmq.ZMQError as e:
|
|
129
|
+
logger.error(
|
|
130
|
+
"ZMQ error for rank %s: %s, recreating all sockets",
|
|
131
|
+
failed_rank,
|
|
132
|
+
e,
|
|
133
|
+
)
|
|
134
|
+
self._recreate_all_sockets()
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
return results
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def world_size(self) -> int:
|
|
141
|
+
return self._world_size
|
|
142
|
+
|
|
143
|
+
def close(self) -> None:
|
|
144
|
+
for socket in self.sockets:
|
|
145
|
+
try:
|
|
146
|
+
socket.close(linger=0)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.warning("Error closing socket: %s", e)
|
|
149
|
+
try:
|
|
150
|
+
if self.ctx:
|
|
151
|
+
self.ctx.term()
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.warning("Error terminating ZMQ context: %s", e)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class ZmqRouterServerTransport(RpcServerTransport):
|
|
157
|
+
"""ZMQ ROUTER socket transport for synchronous RPC servers.
|
|
158
|
+
|
|
159
|
+
Listens for incoming requests and sends responses back
|
|
160
|
+
using ROUTER socket identity-based routing.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def __init__(
|
|
164
|
+
self,
|
|
165
|
+
socket_path: str,
|
|
166
|
+
recv_timeout_ms: int = 1000,
|
|
167
|
+
):
|
|
168
|
+
self.decoder = msgspec.msgpack.Decoder()
|
|
169
|
+
self.ctx = zmq.Context() # type: ignore[attr-defined]
|
|
170
|
+
self.socket = get_zmq_socket(
|
|
171
|
+
self.ctx,
|
|
172
|
+
socket_path,
|
|
173
|
+
"ipc",
|
|
174
|
+
zmq.ROUTER, # type: ignore[attr-defined]
|
|
175
|
+
"bind",
|
|
176
|
+
)
|
|
177
|
+
self.socket.setsockopt(zmq.RCVTIMEO, recv_timeout_ms)
|
|
178
|
+
self.socket_path = socket_path
|
|
179
|
+
|
|
180
|
+
def recv_request(
|
|
181
|
+
self,
|
|
182
|
+
) -> tuple[bytes, list[Any]] | None:
|
|
183
|
+
"""Receive a request.
|
|
184
|
+
|
|
185
|
+
Returns (identity, data_frames) or None on timeout.
|
|
186
|
+
Each data frame is deserialized via msgpack.
|
|
187
|
+
ROUTER socket frames:
|
|
188
|
+
[0] = identity, [1] = empty delimiter, [2:] = data
|
|
189
|
+
"""
|
|
190
|
+
try:
|
|
191
|
+
frames = self.socket.recv_multipart(copy=False)
|
|
192
|
+
except zmq.Again:
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
identity = frames[0].bytes
|
|
196
|
+
# frames[1] is the empty delimiter from REQ socket
|
|
197
|
+
raw_frames = frames[2:]
|
|
198
|
+
if len(raw_frames) < 3:
|
|
199
|
+
logger.warning("Malformed request received: not enough frames.")
|
|
200
|
+
return None
|
|
201
|
+
data_frames = [self.decoder.decode(f) for f in raw_frames]
|
|
202
|
+
return (identity, data_frames)
|
|
203
|
+
|
|
204
|
+
def send_response(
|
|
205
|
+
self,
|
|
206
|
+
identity: bytes,
|
|
207
|
+
response: bytes,
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Send response back via ROUTER socket."""
|
|
210
|
+
self.socket.send_multipart([identity, b"", response])
|
|
211
|
+
|
|
212
|
+
def close(self) -> None:
|
|
213
|
+
self.socket.close(linger=0)
|