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
@@ -0,0 +1,320 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from threading import Lock
4
+ from typing import Dict, Optional
5
+ import abc
6
+ import asyncio
7
+ import functools
8
+ import hashlib
9
+ import logging
10
+ import time
11
+
12
+ # First Party
13
+ from lmcache.utils import CacheEngineKey
14
+ from lmcache.v1.config import LMCacheEngineConfig
15
+ from lmcache.v1.memory_management import MemoryObj
16
+ from lmcache.v1.storage_backend.connector.base_connector import RemoteConnector
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class AuditConnectorMeta(abc.ABCMeta):
22
+ """Metaclass that dynamically generates wrapper methods for all
23
+ RemoteConnector methods
24
+ """
25
+
26
+ def __new__(mcs, name, bases, namespace):
27
+ # Get all methods from RemoteConnector including abstract methods
28
+ # We need to check both the class dict and inherited methods
29
+ all_methods = {}
30
+
31
+ # Collect methods from RemoteConnector and its bases
32
+ for base in RemoteConnector.__mro__:
33
+ for method_name, method_obj in base.__dict__.items():
34
+ if method_name not in all_methods and callable(method_obj):
35
+ all_methods[method_name] = method_obj
36
+
37
+ for method_name, method in all_methods.items():
38
+ # Skip private methods and methods already defined in namespace
39
+ if method_name.startswith("_") or method_name in namespace:
40
+ continue
41
+
42
+ # Skip class methods, static methods, and properties
43
+ if isinstance(method, (classmethod, staticmethod, property)):
44
+ continue
45
+
46
+ # Check if method is marked with @NotAudit
47
+ is_not_audit = getattr(method, "_not_audit", False)
48
+
49
+ # Determine if method is async
50
+ is_async = asyncio.iscoroutinefunction(method)
51
+
52
+ # Create appropriate wrapper and add to namespace
53
+ if is_not_audit:
54
+ if is_async:
55
+ namespace[method_name] = mcs._create_passthrough_async_method(
56
+ method_name, method
57
+ )
58
+ else:
59
+ namespace[method_name] = mcs._create_passthrough_sync_method(
60
+ method_name, method
61
+ )
62
+ else:
63
+ if is_async:
64
+ namespace[method_name] = mcs._create_audit_async_method(
65
+ method_name, method
66
+ )
67
+ else:
68
+ namespace[method_name] = mcs._create_audit_sync_method(
69
+ method_name, method
70
+ )
71
+
72
+ # Create the class with all methods in namespace
73
+ cls = super().__new__(mcs, name, bases, namespace)
74
+
75
+ # Clear abstract methods since we've implemented them all via wrappers
76
+ # The wrappers delegate to real_connector which has the actual implementations
77
+ if hasattr(cls, "__abstractmethods__"):
78
+ cls.__abstractmethods__ = frozenset()
79
+
80
+ return cls
81
+
82
+ @staticmethod
83
+ def _create_passthrough_async_method(method_name: str, original_method):
84
+ """Create a pass-through async method without logging"""
85
+
86
+ @functools.wraps(original_method)
87
+ async def wrapper(self, *args, **kwargs):
88
+ real_method = getattr(self.real_connector, method_name)
89
+ return await real_method(*args, **kwargs)
90
+
91
+ wrapper.__name__ = method_name
92
+ wrapper.__qualname__ = f"AuditConnector.{method_name}"
93
+ return wrapper
94
+
95
+ @staticmethod
96
+ def _create_passthrough_sync_method(method_name: str, original_method):
97
+ """Create a pass-through sync method without logging"""
98
+
99
+ @functools.wraps(original_method)
100
+ def wrapper(self, *args, **kwargs):
101
+ real_method = getattr(self.real_connector, method_name)
102
+ return real_method(*args, **kwargs)
103
+
104
+ wrapper.__name__ = method_name
105
+ wrapper.__qualname__ = f"AuditConnector.{method_name}"
106
+ return wrapper
107
+
108
+ @staticmethod
109
+ def _create_audit_async_method(method_name: str, original_method):
110
+ """Create an audit async method with logging"""
111
+
112
+ @functools.wraps(original_method)
113
+ async def wrapper(self, *args, **kwargs):
114
+ # Special handling for put/get methods with checksum
115
+ if method_name == "put":
116
+ return await self._audit_put(*args, **kwargs)
117
+ elif method_name == "get":
118
+ return await self._audit_get(*args, **kwargs)
119
+
120
+ # Check if method is in excluded commands
121
+ if hasattr(self, "excluded_cmds") and method_name in self.excluded_cmds:
122
+ real_method = getattr(self.real_connector, method_name)
123
+ return await real_method(*args, **kwargs)
124
+
125
+ # Generic audit logging
126
+ self.logger.debug(
127
+ f"[REMOTE_AUDIT][{self.real_connector}]:{method_name.upper()}|START"
128
+ )
129
+ t1 = time.perf_counter()
130
+ try:
131
+ real_method = getattr(self.real_connector, method_name)
132
+ result = await real_method(*args, **kwargs)
133
+ t2 = time.perf_counter()
134
+ cost = (t2 - t1) * 1000
135
+ self.logger.info(
136
+ f"[REMOTE_AUDIT][{self.real_connector}]:{method_name.upper()}|"
137
+ f"SUCCESS|Cost:{cost:.6f}ms"
138
+ )
139
+ return result
140
+ except Exception as e:
141
+ self.logger.error(
142
+ f"[REMOTE_AUDIT][{self.real_connector}]:{method_name.upper()}|"
143
+ f"FAILED|Error: {str(e)}"
144
+ )
145
+ raise
146
+
147
+ wrapper.__name__ = method_name
148
+ wrapper.__qualname__ = f"AuditConnector.{method_name}"
149
+ return wrapper
150
+
151
+ @staticmethod
152
+ def _create_audit_sync_method(method_name: str, original_method):
153
+ """Create an audit sync method with logging"""
154
+
155
+ @functools.wraps(original_method)
156
+ def wrapper(self, *args, **kwargs):
157
+ # Check if method is in excluded commands
158
+ if hasattr(self, "excluded_cmds") and method_name in self.excluded_cmds:
159
+ real_method = getattr(self.real_connector, method_name)
160
+ return real_method(*args, **kwargs)
161
+
162
+ self.logger.debug(
163
+ f"[REMOTE_AUDIT][{self.real_connector}]:{method_name.upper()}|START"
164
+ )
165
+ t1 = time.perf_counter()
166
+ try:
167
+ real_method = getattr(self.real_connector, method_name)
168
+ result = real_method(*args, **kwargs)
169
+ t2 = time.perf_counter()
170
+ cost = (t2 - t1) * 1000
171
+ self.logger.info(
172
+ f"[REMOTE_AUDIT][{self.real_connector}]:{method_name.upper()}|"
173
+ f"SUCCESS|Cost:{cost:.6f}ms"
174
+ )
175
+ return result
176
+ except Exception as e:
177
+ self.logger.error(
178
+ f"[REMOTE_AUDIT][{self.real_connector}]:{method_name.upper()}|"
179
+ f"FAILED|Error: {str(e)}"
180
+ )
181
+ raise
182
+
183
+ wrapper.__name__ = method_name
184
+ wrapper.__qualname__ = f"AuditConnector.{method_name}"
185
+ return wrapper
186
+
187
+
188
+ class AuditConnector(RemoteConnector, metaclass=AuditConnectorMeta):
189
+ """Audit wrapper for RemoteConnector that dynamically wraps all methods.
190
+
191
+ Features:
192
+ - Automatically wraps all RemoteConnector methods
193
+ - Methods marked with @NotAudit are forwarded without logging
194
+ - Configurable checksum verification via URL parameter
195
+ - Logs all operations with timestamps
196
+ - Optional checksum validation for put/get operations
197
+ """
198
+
199
+ def __init__(
200
+ self, real_connector: RemoteConnector, lmcache_config: LMCacheEngineConfig
201
+ ):
202
+ self.real_connector = real_connector
203
+ self.verify_checksum = (
204
+ lmcache_config.extra_config is not None
205
+ and "audit_verify_checksum" in lmcache_config.extra_config
206
+ and lmcache_config.extra_config["audit_verify_checksum"]
207
+ )
208
+ self.calc_checksum = (
209
+ lmcache_config.extra_config is not None
210
+ and "audit_calc_checksum" in lmcache_config.extra_config
211
+ and lmcache_config.extra_config["audit_calc_checksum"]
212
+ )
213
+ self.checksum_registry: Dict[CacheEngineKey, str] = {}
214
+ self.registry_lock = Lock() if self.verify_checksum else None
215
+
216
+ # Parse audit exclude commands
217
+ self.excluded_cmds = set()
218
+ if (
219
+ lmcache_config.extra_config
220
+ and "audit_exclude_cmds" in lmcache_config.extra_config
221
+ ):
222
+ exclude_cmds = lmcache_config.extra_config["audit_exclude_cmds"]
223
+ if exclude_cmds:
224
+ self.excluded_cmds = {cmd.strip() for cmd in exclude_cmds.split(",")}
225
+
226
+ self.logger = logger.getChild("audit")
227
+
228
+ logger.info(
229
+ f"[REMOTE_AUDIT][{self.real_connector}]:INITIALIZED|"
230
+ f"Calc Checksum:{self.calc_checksum}|"
231
+ f"Verify Checksum: {self.verify_checksum}|"
232
+ f"Excluded Cmds: {self.excluded_cmds}"
233
+ )
234
+
235
+ def _calculate_checksum(self, data: bytes) -> str:
236
+ """Calculate SHA-256 checksum for data validation"""
237
+ return hashlib.sha256(data).hexdigest()
238
+
239
+ async def _audit_put(self, key: CacheEngineKey, memory_obj: MemoryObj):
240
+ """Store data with optional checksum tracking"""
241
+ data = memory_obj.byte_array
242
+ checksum = self._calculate_checksum(data) if self.calc_checksum else "N/A"
243
+ data_size = len(data)
244
+ self.logger.debug(
245
+ f"[REMOTE_AUDIT][{self.real_connector}]:PUT|START|Size:{data_size}|"
246
+ f"Checksum:{checksum[:8]}|Saved:{len(self.checksum_registry)}|Key:{key}"
247
+ )
248
+
249
+ try:
250
+ t1 = time.perf_counter()
251
+ await self.real_connector.put(key, memory_obj)
252
+ t2 = time.perf_counter()
253
+ cost = (t2 - t1) * 1000
254
+ if self.registry_lock:
255
+ with self.registry_lock:
256
+ self.checksum_registry[key] = checksum
257
+ self.logger.info(
258
+ f"[REMOTE_AUDIT][{self.real_connector}]:PUT|SUCCESS|Size:{data_size}|"
259
+ f"Checksum:{checksum[:8]}|Cost:{cost:.6f}ms|Saved:"
260
+ f"{len(self.checksum_registry)}|Key:{key}"
261
+ )
262
+
263
+ except Exception as e:
264
+ self.logger.error(
265
+ f"[REMOTE_AUDIT][{self.real_connector}]:PUT|FAILED|Size:{data_size}|"
266
+ f"Key:{key}|Error: {str(e)}"
267
+ )
268
+ raise
269
+
270
+ async def _audit_get(self, key: CacheEngineKey) -> Optional[MemoryObj]:
271
+ """Retrieve data with optional integrity check"""
272
+ self.logger.debug(
273
+ f"[REMOTE_AUDIT][{self.real_connector}]:GET|START|"
274
+ f"Saved:{len(self.checksum_registry)}|Key:{key}"
275
+ )
276
+
277
+ try:
278
+ t1 = time.perf_counter()
279
+ result = await self.real_connector.get(key)
280
+ t2 = time.perf_counter()
281
+ if result is None:
282
+ self.logger.info(
283
+ f"[REMOTE_AUDIT][{self.real_connector}]:GET|MISS|Key:{key}|"
284
+ f"Saved: {len(self.checksum_registry)}"
285
+ )
286
+ return None
287
+
288
+ current_data = result.byte_array
289
+ current_checksum = (
290
+ self._calculate_checksum(current_data) if self.calc_checksum else "N/A"
291
+ )
292
+ data_size = len(current_data)
293
+
294
+ if self.registry_lock:
295
+ with self.registry_lock:
296
+ expected_checksum = self.checksum_registry.get(key)
297
+
298
+ if expected_checksum and current_checksum != expected_checksum:
299
+ self.logger.error(
300
+ f"[REMOTE_AUDIT][{self.real_connector}]:"
301
+ f"GET|MISMATCH|Size:{data_size}|"
302
+ f"Expected:<{expected_checksum[:8]}>|"
303
+ f"Actual:<{current_checksum[:8]}>|Key:{key}"
304
+ )
305
+ return None
306
+
307
+ cost = (t2 - t1) * 1000
308
+ self.logger.info(
309
+ f"[REMOTE_AUDIT][{self.real_connector}]:GET|SUCCESS|"
310
+ f"Checksum:{current_checksum[:8]}|"
311
+ f"Cost:{cost:.6f}ms|Saved:{len(self.checksum_registry)}|Key:{key}"
312
+ )
313
+ return result
314
+
315
+ except Exception as e:
316
+ self.logger.error(
317
+ f"[REMOTE_AUDIT][{self.real_connector}]:GET|"
318
+ f"FAILED|Key:{key}|Error: {str(e)}"
319
+ )
320
+ raise
@@ -0,0 +1,379 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from typing import List, Optional
4
+ import abc
5
+ import asyncio
6
+
7
+ # Third Party
8
+ import torch
9
+
10
+ # First Party
11
+ from lmcache.integration.vllm.utils import get_size_bytes
12
+ from lmcache.logging import init_logger
13
+ from lmcache.utils import CacheEngineKey
14
+ from lmcache.v1.config import LMCacheEngineConfig
15
+ from lmcache.v1.memory_management import MemoryFormat, MemoryObj
16
+ from lmcache.v1.metadata import LMCacheMetadata
17
+ from lmcache.v1.protocol import get_remote_metadata_bytes, init_remote_metadata_info
18
+
19
+ logger = init_logger(__name__)
20
+
21
+
22
+ def NotAudit(func):
23
+ """
24
+ Decorator to mark methods that should not be audited.
25
+ These methods will be directly forwarded to the real connector without logging.
26
+ """
27
+ func._not_audit = True
28
+ return func
29
+
30
+
31
+ class RemoteConnector(metaclass=abc.ABCMeta):
32
+ """
33
+ Interface for remote connector
34
+ """
35
+
36
+ def __init__(
37
+ self, config: LMCacheEngineConfig, metadata: Optional[LMCacheMetadata]
38
+ ):
39
+ """
40
+ Initialize some common attributes, which will be used in the subclasses.
41
+ - `save_chunk_meta` is a flag to indicate whether to save the chunk meta info.
42
+ - `meta_shapes` is a list of shapes of lmcache full chunk.
43
+ - `meta_dtypes` is a list of dtypes of lmcache chunk.
44
+ - `meta_fmt` is the memory format of the lmcache chunk.
45
+ - `full_chunk_size_bytes` is the size of the lmcache full chunk.
46
+ - `single_token_size` is the size of a single token.`
47
+ - `remote_metadata_bytes` is the size of the remote metadata.
48
+
49
+ Input:
50
+ config: the lmcache engine config
51
+ metadata: the lmcache engine metadata
52
+ """
53
+ # TODO(chunxiaozheng): support layerwise here
54
+ assert metadata is not None
55
+ self.save_chunk_meta: bool = (
56
+ config.extra_config is None
57
+ or config.extra_config.get("save_chunk_meta", True)
58
+ or config.use_layerwise
59
+ )
60
+ self.meta_shapes: list[torch.Size] = metadata.get_shapes()
61
+ self.meta_dtypes: list[torch.dtype] = metadata.get_dtypes()
62
+ self.meta_fmt: MemoryFormat = (
63
+ MemoryFormat.KV_MLA_FMT if metadata.use_mla else MemoryFormat.KV_2LTD
64
+ )
65
+ self.full_chunk_size_bytes: int = get_size_bytes(
66
+ self.meta_shapes, self.meta_dtypes
67
+ )
68
+ assert self.full_chunk_size_bytes % metadata.chunk_size == 0
69
+ self.single_token_size = self.full_chunk_size_bytes // metadata.chunk_size
70
+
71
+ # init remote metadata info
72
+ init_remote_metadata_info(metadata.get_num_groups())
73
+ self.remote_metadata_bytes = get_remote_metadata_bytes()
74
+ logger.info(
75
+ "init remote connector metadata info, shapes: %s, dtypes: %s, fmt: %s, "
76
+ "full chunk size: %s, single token size: %s, remote metadata bytes: %s",
77
+ self.meta_shapes,
78
+ self.meta_dtypes,
79
+ self.meta_fmt,
80
+ self.full_chunk_size_bytes,
81
+ self.single_token_size,
82
+ self.remote_metadata_bytes,
83
+ )
84
+
85
+ @NotAudit
86
+ def reshape_partial_chunk(
87
+ self,
88
+ memory_obj: MemoryObj,
89
+ bytes_read: int,
90
+ ) -> MemoryObj:
91
+ assert self.full_chunk_size_bytes is not None
92
+ assert self.single_token_size is not None
93
+ if (
94
+ bytes_read == 0
95
+ or bytes_read % self.single_token_size != 0
96
+ or bytes_read > self.full_chunk_size_bytes
97
+ ):
98
+ raise ValueError(
99
+ f"bytes_read: {bytes_read} is illegal, "
100
+ f"single_token_size: {self.single_token_size}, "
101
+ f"full_chunk_size_bytes: {self.full_chunk_size_bytes}"
102
+ )
103
+
104
+ if bytes_read == self.full_chunk_size_bytes:
105
+ # full chunk, return directly
106
+ return memory_obj
107
+
108
+ # NOTE: for unfull chunk, we have no way to verify
109
+ shape_list = list(memory_obj.meta.shape)
110
+ if len(shape_list) == 4:
111
+ # Standard: [2, num_layers, num_tokens, hidden_dim]
112
+ # or MLA: [1, num_layers, num_tokens, hidden_dim]
113
+ token_dim = 2
114
+ num_tokens = bytes_read // self.single_token_size
115
+ else:
116
+ # Layerwise 3D: [num_tokens, 2, hidden_dim]
117
+ # Layerwise MLA 2D: [num_tokens, hidden_dim]
118
+ token_dim = 0
119
+ # single_token_size spans ALL layers, but bytes_read is
120
+ # for a single layer. Compute per-layer token size from
121
+ # the non-token dimensions of the shape.
122
+ elements_per_token = 1
123
+ for i in range(1, len(shape_list)):
124
+ elements_per_token *= shape_list[i]
125
+ assert memory_obj.meta.dtype is not None
126
+ dtype_size = memory_obj.meta.dtype.itemsize
127
+ per_layer_token_size = elements_per_token * dtype_size
128
+ num_tokens = bytes_read // per_layer_token_size
129
+ shape_list[token_dim] = num_tokens
130
+ actual_shape = torch.Size(shape_list)
131
+ memory_obj.raw_data = memory_obj.raw_data[:bytes_read]
132
+ memory_obj.meta.shape = actual_shape
133
+ # Sync group_prefix_sum so that get_size() / byte_array reflect the
134
+ # truncated size rather than the original full-chunk size.
135
+ if hasattr(memory_obj, "group_prefix_sum"):
136
+ memory_obj.group_prefix_sum = [0, bytes_read] # type: ignore[attr-defined]
137
+
138
+ return memory_obj
139
+
140
+ @NotAudit
141
+ def post_init(self):
142
+ """
143
+ Post-initialization method to be called after the connector is created.
144
+ This can be used to perform any additional setup required by the connector.
145
+ """
146
+ logger.info("Dummy post-initializing remote connector")
147
+
148
+ @abc.abstractmethod
149
+ async def exists(self, key: CacheEngineKey) -> bool:
150
+ """
151
+ Check if the remote server contains the key
152
+
153
+ Input:
154
+ key: a CacheEngineKey
155
+
156
+ Returns:
157
+ True if the cache engine contains the key, False otherwise
158
+ """
159
+ raise NotImplementedError
160
+
161
+ @abc.abstractmethod
162
+ def exists_sync(self, key: CacheEngineKey) -> bool:
163
+ """
164
+ Check if the remote server contains the key synchronized
165
+
166
+ Input:
167
+ key: a CacheEngineKey
168
+
169
+ Returns:
170
+ True if the cache engine contains the key, False otherwise
171
+ """
172
+ raise NotImplementedError
173
+
174
+ @abc.abstractmethod
175
+ async def get(self, key: CacheEngineKey) -> Optional[MemoryObj]:
176
+ """
177
+ Get the memory_obj of the corresponding key
178
+
179
+ Input:
180
+ key: the key of the corresponding object
181
+
182
+ Returns:
183
+ The memory_obj of the corresponding key
184
+ Return None if the key does not exist
185
+ """
186
+ raise NotImplementedError
187
+
188
+ @abc.abstractmethod
189
+ async def put(self, key: CacheEngineKey, memory_obj: MemoryObj):
190
+ """
191
+ Send the memory_obj with the corresponding key directly
192
+ to the remote server. Will decrease the ref count after
193
+ send finishes.
194
+
195
+ Input:
196
+ key: the CacheEngine key
197
+ memory_obj: the memory_obj of the corresponding key
198
+ """
199
+ raise NotImplementedError
200
+
201
+ @abc.abstractmethod
202
+ async def list(self) -> List[str]:
203
+ """
204
+ List all keys in the remote server
205
+
206
+ Returns:
207
+ A list of keys in the remote server
208
+ """
209
+ raise NotImplementedError
210
+
211
+ @abc.abstractmethod
212
+ async def close(self):
213
+ """
214
+ Close remote server
215
+
216
+ """
217
+ raise NotImplementedError
218
+
219
+ def support_ping(self) -> bool:
220
+ """
221
+ Check if the connector supports ping operation
222
+
223
+ Returns:
224
+ True if ping is supported, False otherwise
225
+ """
226
+ return False
227
+
228
+ async def ping(self) -> int:
229
+ """
230
+ Ping the remote server
231
+
232
+ Returns:
233
+ The error code, 0 means success
234
+ """
235
+ raise NotImplementedError
236
+
237
+ def support_batched_get(self) -> bool:
238
+ """
239
+ Check if the connector supports batched get
240
+
241
+ Returns:
242
+ True if batched get is supported, False otherwise
243
+ """
244
+ return False
245
+
246
+ async def batched_get(
247
+ self, keys: List[CacheEngineKey]
248
+ ) -> List[Optional[MemoryObj]]:
249
+ """
250
+ Batched get the memory_objs of the corresponding keys
251
+
252
+ Input:
253
+ keys: the keys of the corresponding objects
254
+
255
+ Returns:
256
+ The memory_objs of the corresponding keys
257
+ Return None if the key does not exist
258
+ """
259
+ raise NotImplementedError
260
+
261
+ def support_batched_put(self) -> bool:
262
+ """
263
+ Check if the connector supports batched put
264
+ Returns:
265
+ True if batched put is supported, False otherwise
266
+ """
267
+ return False
268
+
269
+ async def batched_put(
270
+ self, keys: List[CacheEngineKey], memory_objs: List[MemoryObj]
271
+ ):
272
+ """
273
+ Batched put the memory_objs with the corresponding keys
274
+ Input:
275
+ keys: the keys of the corresponding objects
276
+ memory_objs: the memory_objs of the corresponding keys
277
+ """
278
+ raise NotImplementedError
279
+
280
+ def support_batched_async_contains(self) -> bool:
281
+ return True
282
+
283
+ async def batched_async_contains(
284
+ self,
285
+ lookup_id: str,
286
+ keys: List[CacheEngineKey],
287
+ pin: bool = False,
288
+ ) -> int:
289
+ """Check how many keys exist in file system in batch
290
+
291
+ Args:
292
+ lookup_id: Identifier for this lookup operation
293
+ keys: List of keys to check
294
+ pin: Whether to pin the keys (not used in FS connector)
295
+
296
+ Returns:
297
+ Number of consecutive keys that exist, starting from the first key
298
+ """
299
+ tasks = [self.exists(key) for key in keys]
300
+ results = await asyncio.gather(*tasks)
301
+ if False in results:
302
+ return results.index(False)
303
+ return len(results)
304
+
305
+ def support_batched_get_non_blocking(self) -> bool:
306
+ return True
307
+
308
+ async def batched_get_non_blocking(
309
+ self,
310
+ lookup_id: str,
311
+ keys: List[CacheEngineKey],
312
+ ) -> List[MemoryObj]:
313
+ """Batched get the memory_objs of the corresponding keys (non-blocking)
314
+
315
+ This method returns only the consecutive prefix of successfully retrieved
316
+ memory objects. Once a key is not found (None) or an exception occurs,
317
+ all subsequent memory objects (even if successfully retrieved) will be
318
+ released to avoid memory leaks, and only the prefix before the first
319
+ failure will be returned.
320
+
321
+ Args:
322
+ lookup_id: Identifier for this lookup operation
323
+ keys: List of keys to get
324
+
325
+ Returns:
326
+ List of consecutive memory objects from the beginning until the first
327
+ failure (None or Exception). Empty list if the first key fails.
328
+ """
329
+ # Use asyncio.gather to fetch all keys concurrently
330
+ results = await asyncio.gather(
331
+ *(self.get(key) for key in keys), return_exceptions=True
332
+ )
333
+
334
+ # Only return consecutive prefix of valid memory objects
335
+ memory_objs = []
336
+ found_failure = False
337
+ for result in results:
338
+ if found_failure:
339
+ # Release subsequent memory objects to avoid memory leak
340
+ if isinstance(result, MemoryObj):
341
+ result.ref_count_down()
342
+ elif isinstance(result, MemoryObj):
343
+ memory_objs.append(result)
344
+ else:
345
+ # First failure encountered (None or Exception)
346
+ if isinstance(result, Exception):
347
+ logger.warning(f"Exception during batched get: {result}")
348
+ found_failure = True
349
+
350
+ return memory_objs
351
+
352
+ def remove_sync(self, key: CacheEngineKey) -> bool:
353
+ """
354
+ Remove a memory object.
355
+
356
+ :param CacheEngineKey key: The key of the MemoryObj.
357
+
358
+ :return: a bool indicates whether remove is successful.
359
+ """
360
+ raise NotImplementedError
361
+
362
+ def batched_contains(self, keys: List[CacheEngineKey]) -> int:
363
+ """
364
+ Batched contains.
365
+
366
+ :param List[CacheEngineKey] keys: The keys to check.
367
+
368
+ :return: Return hit chunks by prefix match.
369
+ """
370
+ raise NotImplementedError
371
+
372
+ def support_batched_contains(self) -> bool:
373
+ """
374
+ Is supported batched_contains
375
+ """
376
+ return False
377
+
378
+ def __repr__(self) -> str:
379
+ return f"<{self.__class__.__name__}>"