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.
Files changed (399) hide show
  1. lmcache/__init__.py +84 -0
  2. lmcache/_version.py +24 -0
  3. lmcache/cli/__init__.py +1 -0
  4. lmcache/cli/commands/__init__.py +34 -0
  5. lmcache/cli/commands/base.py +157 -0
  6. lmcache/cli/commands/bench/__init__.py +557 -0
  7. lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
  8. lmcache/cli/commands/bench/engine_bench/config.py +245 -0
  9. lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
  10. lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
  11. lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
  12. lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
  13. lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
  14. lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
  15. lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
  16. lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
  17. lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
  18. lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
  19. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
  20. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
  21. lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
  22. lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
  23. lmcache/cli/commands/describe.py +310 -0
  24. lmcache/cli/commands/kvcache.py +133 -0
  25. lmcache/cli/commands/mock.py +75 -0
  26. lmcache/cli/commands/ping.py +113 -0
  27. lmcache/cli/commands/query/__init__.py +155 -0
  28. lmcache/cli/commands/query/prompt.py +134 -0
  29. lmcache/cli/commands/query/request.py +357 -0
  30. lmcache/cli/commands/server.py +99 -0
  31. lmcache/cli/commands/tool/__init__.py +63 -0
  32. lmcache/cli/commands/tool/cache_simulator.py +113 -0
  33. lmcache/cli/commands/trace/__init__.py +505 -0
  34. lmcache/cli/commands/trace/dispatch.py +249 -0
  35. lmcache/cli/commands/trace/driver.py +372 -0
  36. lmcache/cli/commands/trace/stats.py +289 -0
  37. lmcache/cli/documents/lmcache.txt +11 -0
  38. lmcache/cli/main.py +42 -0
  39. lmcache/cli/metrics/__init__.py +29 -0
  40. lmcache/cli/metrics/formatter.py +171 -0
  41. lmcache/cli/metrics/handler.py +94 -0
  42. lmcache/cli/metrics/metrics.py +161 -0
  43. lmcache/cli/metrics/section.py +77 -0
  44. lmcache/connections.py +173 -0
  45. lmcache/integration/__init__.py +2 -0
  46. lmcache/integration/base_service_factory.py +165 -0
  47. lmcache/integration/request_telemetry/__init__.py +1 -0
  48. lmcache/integration/request_telemetry/base.py +51 -0
  49. lmcache/integration/request_telemetry/factory.py +113 -0
  50. lmcache/integration/request_telemetry/fastapi.py +109 -0
  51. lmcache/integration/request_telemetry/noop.py +35 -0
  52. lmcache/integration/sglang/__init__.py +2 -0
  53. lmcache/integration/sglang/sglang_adapter.py +326 -0
  54. lmcache/integration/sglang/utils.py +39 -0
  55. lmcache/integration/vllm/__init__.py +1 -0
  56. lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
  57. lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
  58. lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
  59. lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
  60. lmcache/integration/vllm/utils.py +433 -0
  61. lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
  62. lmcache/integration/vllm/vllm_service_factory.py +339 -0
  63. lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
  64. lmcache/logging.py +107 -0
  65. lmcache/native_storage_ops.pyi +230 -0
  66. lmcache/non_cuda_equivalents.py +1424 -0
  67. lmcache/observability.py +1958 -0
  68. lmcache/storage_backend/serde/__init__.py +1 -0
  69. lmcache/storage_backend/serde/cachegen_basics.py +210 -0
  70. lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
  71. lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
  72. lmcache/storage_backend/serde/serde.py +75 -0
  73. lmcache/tools/__init__.py +1 -0
  74. lmcache/tools/cache_simulator/README.md +392 -0
  75. lmcache/tools/cache_simulator/__init__.py +1 -0
  76. lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
  77. lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
  78. lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
  79. lmcache/tools/cache_simulator/lru_cache.py +124 -0
  80. lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
  81. lmcache/tools/cache_simulator/simulator.py +795 -0
  82. lmcache/tools/controller_benchmark/README.md +161 -0
  83. lmcache/tools/controller_benchmark/__init__.py +1 -0
  84. lmcache/tools/controller_benchmark/__main__.py +331 -0
  85. lmcache/tools/controller_benchmark/benchmark.py +660 -0
  86. lmcache/tools/controller_benchmark/config.py +44 -0
  87. lmcache/tools/controller_benchmark/constants.py +10 -0
  88. lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
  89. lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
  90. lmcache/tools/controller_benchmark/handlers/base.py +47 -0
  91. lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
  92. lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
  93. lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
  94. lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
  95. lmcache/tools/controller_benchmark/handlers/register.py +56 -0
  96. lmcache/tools/mp_status_viewer/__init__.py +1 -0
  97. lmcache/tools/mp_status_viewer/__main__.py +95 -0
  98. lmcache/usage_context.py +417 -0
  99. lmcache/utils.py +665 -0
  100. lmcache/v1/__init__.py +2 -0
  101. lmcache/v1/api_server/__init__.py +2 -0
  102. lmcache/v1/api_server/__main__.py +537 -0
  103. lmcache/v1/basic_check.py +112 -0
  104. lmcache/v1/cache_controller/__init__.py +9 -0
  105. lmcache/v1/cache_controller/commands/__init__.py +15 -0
  106. lmcache/v1/cache_controller/commands/base.py +35 -0
  107. lmcache/v1/cache_controller/commands/full_sync.py +49 -0
  108. lmcache/v1/cache_controller/config.py +176 -0
  109. lmcache/v1/cache_controller/controller_manager.py +535 -0
  110. lmcache/v1/cache_controller/controllers/__init__.py +11 -0
  111. lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
  112. lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
  113. lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
  114. lmcache/v1/cache_controller/executor.py +463 -0
  115. lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
  116. lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
  117. lmcache/v1/cache_controller/frontend/static/index.html +234 -0
  118. lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
  119. lmcache/v1/cache_controller/full_sync_sender.py +475 -0
  120. lmcache/v1/cache_controller/locks.py +149 -0
  121. lmcache/v1/cache_controller/message.py +828 -0
  122. lmcache/v1/cache_controller/observability.py +208 -0
  123. lmcache/v1/cache_controller/utils.py +679 -0
  124. lmcache/v1/cache_controller/worker.py +665 -0
  125. lmcache/v1/cache_engine.py +2058 -0
  126. lmcache/v1/cache_interface.py +19 -0
  127. lmcache/v1/check/__init__.py +74 -0
  128. lmcache/v1/check/check_mode_gen.py +86 -0
  129. lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
  130. lmcache/v1/check/check_mode_test_remote.py +155 -0
  131. lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
  132. lmcache/v1/check/utils.py +571 -0
  133. lmcache/v1/compute/__init__.py +2 -0
  134. lmcache/v1/compute/attention/__init__.py +0 -0
  135. lmcache/v1/compute/attention/abstract.py +39 -0
  136. lmcache/v1/compute/attention/flash_attn.py +129 -0
  137. lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
  138. lmcache/v1/compute/attention/metadata.py +85 -0
  139. lmcache/v1/compute/attention/utils.py +14 -0
  140. lmcache/v1/compute/blend/__init__.py +7 -0
  141. lmcache/v1/compute/blend/blender.py +168 -0
  142. lmcache/v1/compute/blend/metadata.py +34 -0
  143. lmcache/v1/compute/blend/utils.py +63 -0
  144. lmcache/v1/compute/models/__init__.py +0 -0
  145. lmcache/v1/compute/models/base.py +141 -0
  146. lmcache/v1/compute/models/llama.py +9 -0
  147. lmcache/v1/compute/models/qwen3.py +24 -0
  148. lmcache/v1/compute/models/utils.py +68 -0
  149. lmcache/v1/compute/positional_encoding.py +199 -0
  150. lmcache/v1/config.py +848 -0
  151. lmcache/v1/config_base.py +848 -0
  152. lmcache/v1/distributed/api.py +248 -0
  153. lmcache/v1/distributed/config.py +321 -0
  154. lmcache/v1/distributed/error.py +64 -0
  155. lmcache/v1/distributed/eviction.py +192 -0
  156. lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
  157. lmcache/v1/distributed/eviction_policy/factory.py +27 -0
  158. lmcache/v1/distributed/eviction_policy/lru.py +244 -0
  159. lmcache/v1/distributed/eviction_policy/noop.py +50 -0
  160. lmcache/v1/distributed/internal_api.py +170 -0
  161. lmcache/v1/distributed/l1_manager.py +835 -0
  162. lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
  163. lmcache/v1/distributed/l2_adapters/base.py +360 -0
  164. lmcache/v1/distributed/l2_adapters/config.py +385 -0
  165. lmcache/v1/distributed/l2_adapters/factory.py +205 -0
  166. lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
  167. lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
  168. lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
  169. lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
  170. lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
  171. lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
  172. lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
  173. lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
  174. lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
  175. lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
  176. lmcache/v1/distributed/memory_manager.py +179 -0
  177. lmcache/v1/distributed/storage_controller.py +39 -0
  178. lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
  179. lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
  180. lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
  181. lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
  182. lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
  183. lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
  184. lmcache/v1/distributed/storage_manager.py +532 -0
  185. lmcache/v1/event_manager.py +145 -0
  186. lmcache/v1/exceptions/__init__.py +16 -0
  187. lmcache/v1/gpu_connector/__init__.py +126 -0
  188. lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
  189. lmcache/v1/gpu_connector/gpu_ops.py +85 -0
  190. lmcache/v1/gpu_connector/hpu_connector.py +326 -0
  191. lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
  192. lmcache/v1/gpu_connector/utils.py +890 -0
  193. lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
  194. lmcache/v1/health_monitor/__init__.py +1 -0
  195. lmcache/v1/health_monitor/base.py +587 -0
  196. lmcache/v1/health_monitor/checks/__init__.py +1 -0
  197. lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
  198. lmcache/v1/health_monitor/constants.py +36 -0
  199. lmcache/v1/internal_api_server/__init__.py +0 -0
  200. lmcache/v1/internal_api_server/api_registry.py +59 -0
  201. lmcache/v1/internal_api_server/api_server.py +120 -0
  202. lmcache/v1/internal_api_server/common/__init__.py +1 -0
  203. lmcache/v1/internal_api_server/common/env_api.py +22 -0
  204. lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
  205. lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
  206. lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
  207. lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
  208. lmcache/v1/internal_api_server/common/thread_api.py +63 -0
  209. lmcache/v1/internal_api_server/controller/__init__.py +1 -0
  210. lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
  211. lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
  212. lmcache/v1/internal_api_server/utils.py +43 -0
  213. lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
  214. lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
  215. lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
  216. lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
  217. lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
  218. lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
  219. lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
  220. lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
  221. lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
  222. lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
  223. lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
  224. lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
  225. lmcache/v1/kv_layer_groups.py +267 -0
  226. lmcache/v1/lazy_memory_allocator.py +284 -0
  227. lmcache/v1/lookup_client/__init__.py +25 -0
  228. lmcache/v1/lookup_client/abstract_client.py +77 -0
  229. lmcache/v1/lookup_client/async_lookup_message.py +50 -0
  230. lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
  231. lmcache/v1/lookup_client/factory.py +251 -0
  232. lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
  233. lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
  234. lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
  235. lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
  236. lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
  237. lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
  238. lmcache/v1/lookup_client/record_strategies/base.py +327 -0
  239. lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
  240. lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
  241. lmcache/v1/manager.py +539 -0
  242. lmcache/v1/memory_management.py +2619 -0
  243. lmcache/v1/metadata.py +114 -0
  244. lmcache/v1/mp_observability/AGENTS.override.md +21 -0
  245. lmcache/v1/mp_observability/README.md +204 -0
  246. lmcache/v1/mp_observability/config.py +340 -0
  247. lmcache/v1/mp_observability/event.py +100 -0
  248. lmcache/v1/mp_observability/event_bus.py +313 -0
  249. lmcache/v1/mp_observability/otel_init.py +145 -0
  250. lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
  251. lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
  252. lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
  253. lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
  254. lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
  255. lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
  256. lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
  257. lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
  258. lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
  259. lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
  260. lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
  261. lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
  262. lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
  263. lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
  264. lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
  265. lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
  266. lmcache/v1/mp_observability/trace/__init__.py +50 -0
  267. lmcache/v1/mp_observability/trace/codecs.py +255 -0
  268. lmcache/v1/mp_observability/trace/decorator.py +147 -0
  269. lmcache/v1/mp_observability/trace/format.py +132 -0
  270. lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
  271. lmcache/v1/mp_observability/trace/reader.py +167 -0
  272. lmcache/v1/mp_observability/trace/recorder.py +300 -0
  273. lmcache/v1/multiprocess/__init__.py +0 -0
  274. lmcache/v1/multiprocess/affinity_pool.py +102 -0
  275. lmcache/v1/multiprocess/blend_server_v2.py +891 -0
  276. lmcache/v1/multiprocess/config.py +253 -0
  277. lmcache/v1/multiprocess/custom_types.py +281 -0
  278. lmcache/v1/multiprocess/futures.py +194 -0
  279. lmcache/v1/multiprocess/gpu_context.py +511 -0
  280. lmcache/v1/multiprocess/http_server.py +235 -0
  281. lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
  282. lmcache/v1/multiprocess/mq.py +732 -0
  283. lmcache/v1/multiprocess/protocol.py +86 -0
  284. lmcache/v1/multiprocess/protocols/README.md +213 -0
  285. lmcache/v1/multiprocess/protocols/__init__.py +127 -0
  286. lmcache/v1/multiprocess/protocols/base.py +89 -0
  287. lmcache/v1/multiprocess/protocols/blend.py +109 -0
  288. lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
  289. lmcache/v1/multiprocess/protocols/controller.py +53 -0
  290. lmcache/v1/multiprocess/protocols/debug.py +34 -0
  291. lmcache/v1/multiprocess/protocols/engine.py +146 -0
  292. lmcache/v1/multiprocess/protocols/observability.py +39 -0
  293. lmcache/v1/multiprocess/server.py +1134 -0
  294. lmcache/v1/multiprocess/session.py +190 -0
  295. lmcache/v1/multiprocess/token_hasher.py +441 -0
  296. lmcache/v1/offload_server/__init__.py +17 -0
  297. lmcache/v1/offload_server/abstract_server.py +37 -0
  298. lmcache/v1/offload_server/message.py +30 -0
  299. lmcache/v1/offload_server/zmq_server.py +122 -0
  300. lmcache/v1/periodic_thread.py +579 -0
  301. lmcache/v1/pin_monitor.py +246 -0
  302. lmcache/v1/plugin/__init__.py +0 -0
  303. lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
  304. lmcache/v1/protocol.py +317 -0
  305. lmcache/v1/rpc/__init__.py +17 -0
  306. lmcache/v1/rpc/transport.py +105 -0
  307. lmcache/v1/rpc/zmq_transport.py +213 -0
  308. lmcache/v1/rpc_utils.py +165 -0
  309. lmcache/v1/server/__init__.py +2 -0
  310. lmcache/v1/server/__main__.py +170 -0
  311. lmcache/v1/server/storage_backend/__init__.py +21 -0
  312. lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
  313. lmcache/v1/server/storage_backend/local_backend.py +75 -0
  314. lmcache/v1/server/utils.py +21 -0
  315. lmcache/v1/standalone/__init__.py +1 -0
  316. lmcache/v1/standalone/__main__.py +583 -0
  317. lmcache/v1/standalone/manager.py +80 -0
  318. lmcache/v1/standalone/standalone_service_factory.py +86 -0
  319. lmcache/v1/storage_backend/__init__.py +313 -0
  320. lmcache/v1/storage_backend/abstract_backend.py +445 -0
  321. lmcache/v1/storage_backend/audit_backend.py +233 -0
  322. lmcache/v1/storage_backend/batched_message_sender.py +222 -0
  323. lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
  324. lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
  325. lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
  326. lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
  327. lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
  328. lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
  329. lmcache/v1/storage_backend/connector/__init__.py +443 -0
  330. lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
  331. lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
  332. lmcache/v1/storage_backend/connector/base_connector.py +379 -0
  333. lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
  334. lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
  335. lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
  336. lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
  337. lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
  338. lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
  339. lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
  340. lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
  341. lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
  342. lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
  343. lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
  344. lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
  345. lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
  346. lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
  347. lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
  348. lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
  349. lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
  350. lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
  351. lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
  352. lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
  353. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
  354. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
  355. lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
  356. lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
  357. lmcache/v1/storage_backend/gds_backend.py +1199 -0
  358. lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
  359. lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
  360. lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
  361. lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
  362. lmcache/v1/storage_backend/local_disk_backend.py +656 -0
  363. lmcache/v1/storage_backend/maru_backend.py +734 -0
  364. lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
  365. lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
  366. lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
  367. lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
  368. lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
  369. lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
  370. lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
  371. lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
  372. lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
  373. lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
  374. lmcache/v1/storage_backend/p2p_backend.py +788 -0
  375. lmcache/v1/storage_backend/path_sharder.py +117 -0
  376. lmcache/v1/storage_backend/pd_backend.py +646 -0
  377. lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
  378. lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
  379. lmcache/v1/storage_backend/remote_backend.py +624 -0
  380. lmcache/v1/storage_backend/resp_client.py +227 -0
  381. lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
  382. lmcache/v1/storage_backend/storage_manager.py +1352 -0
  383. lmcache/v1/system_detection.py +110 -0
  384. lmcache/v1/token_database.py +551 -0
  385. lmcache/v1/transfer_channel/__init__.py +83 -0
  386. lmcache/v1/transfer_channel/abstract.py +285 -0
  387. lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
  388. lmcache/v1/transfer_channel/nixl_channel.py +639 -0
  389. lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
  390. lmcache/v1/transfer_channel/transfer_utils.py +63 -0
  391. lmcache/v1/utils/__init__.py +1 -0
  392. lmcache/v1/utils/bloom_filter.py +109 -0
  393. lmcache/v1/utils/cache_utils.py +125 -0
  394. lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
  395. lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
  396. lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
  397. lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
  398. lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
  399. 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)