HTTP API#

当 MP 服务器通过 lmcache server 启动时(推荐入口点),会同时暴露一个基于 FastAPI 的 HTTP 前端和 vLLM 使用的 ZMQ 套接字。此 HTTP API 面向运维人员、编排工具(例如 Kubernetes)和调试工具——它位于推理数据路径上。

路由的来源#

路由由三个来源组装而成,在启动时由 HTTPAPIRegistry 合并为一个 FastAPI 应用:

  • MP 原生路由 — 在 lmcache/v1/multiprocess/http_apis/ 下任何名为 *_api.py 的模块,如果暴露了模块级的 router(一个 fastapi.APIRouter),将会被自动发现。这涵盖了运维操作面:状态、缓存控制、L2 管理、配额和运行时重新配置。

  • 共享“公共”路由lmcache/v1/multiprocess/http_apis/common_api.py 聚合了 lmcache/v1/internal_api_server/common/ 下的每个兼容的 router(跳过任何列在 _MP_INCOMPATIBLE_MODULES 中的模块,目前为空),并将它们转发到自动发现流水线。这些是与嵌入 vLLM 的 API 服务器共享的跨服务器诊断(/env/loglevel/metrics/threads/periodic-threads*/run_script)。在 internal_api_server/common 下添加新的兼容模块无需在 MP 端进行任何改动。

  • 重新导出的版本路由 — 基本信息组 lmcache/v1/multiprocess/http_apis/info_api.py 包含来自 lmcache/v1/internal_api_server/vllm/version_api.py 的路由器,暴露 /version/lmc_version/commit_id,以及 //healthcheck/status

服务器配置#

参数

默认

描述

--http-host

0.0.0.0

绑定 HTTP 服务器的主机。

--http-port

8080

绑定 HTTP 服务器的端口。

示例:

lmcache server \
    --l1-size-gb 100 --eviction-policy LRU \
    --http-host 0.0.0.0 --http-port 8080

以下所有示例都假设服务器可以通过 http://localhost:8080 访问。

端点概述#

下面的路由按目的分组。操作接口(健康检查、状态、缓存和存储控制)位于顶层路径;从共享的 internal_api_server 包继承的路由保持其原始路径,以与嵌入的 vLLM API 服务器兼容。

备注

多个处理程序在响应 主体 中报告失败,而不是通过非 200 状态码(例如 DELETE /cache/objects 返回 200ok=false,而 /periodic-threads-health 返回 200healthy=false)。错误字段名称也不统一:/healthcheck/cache/clear 在失败时使用 reason,而 /status/config/cache/checksums 使用 error。每个端点的详细信息如下是权威的。

存活性和健康

方法

路径

目的

GET

/

静态存活检测(不触及引擎)。

GET

/healthcheck

K8s 存活/就绪探针;503 直到引擎初始化完成。

检查和状态

方法

路径

目的

GET

/status

用于检查和调试的详细引擎快照(L1、L2、注册的上下文、会话、预取作业)。

GET

/config

转储合并的服务器配置对象(mpstorage_managerobservability)。

GET

/config/adapters

列出活动缓存适配器(type_nametierprimaryreconfigurable)。取代 /reconfigure/backends

GET

/version

组合版本字符串 (\"<version>-<commit_id>\")。

GET

/lmc_version

LMCache 包版本字符串。

GET

/commit_id

构建提交 ID。

缓存管理

方法

路径

目的

GET

/cache/objects

对驻留在某个层/适配器中的对象进行分页(查询:tieradaptermodel_namepage_sizepage_token)。

DELETE

/cache/objects

Delete a caller-supplied list of object keys from L1, L2, or both (body: keys, tier, force, adapter). force deletes L1 keys even if locked.

POST

/cache/prefetches

通过从 L2 提前加载令牌序列来预热节点的 L1;返回 request_id

GET

/cache/prefetches/{request_id}

轮询已提交的热预取(待处理 / 已完成)。

POST

/cache/clear

强制清除某个层的常驻缓存(请求体:tier = l1force)。

POST

/cache/checksums

计算 KV Cache 块的 MD5 校验和(诊断 / 往返完整性检查)。

配额管理

方法

路径

目的

GET

/quota

列出每个注册的 cache_salt 配额及其实时使用情况。

PUT

/quota/{cache_salt}

设置或更新 cache_salt 的配额(以 GB 为单位)。

GET

/quota/{cache_salt}

读取单个 cache_salt 的配额和实时使用情况。

DELETE

/quota/{cache_salt}

移除 cache_salt 的配额条目(其数据将在下一个周期被逐出)。

运行时 L2 重新配置

方法

路径

目的

GET

/reconfigure/{backend}/status

报告一个后端类型的可运行管理 L2 适配器。 (要发现可重新配置的后端,请使用 GET /config/adapters 并读取 reconfigurable 标志。)

POST

/reconfigure/{backend}/{operation}

对后端适配器应用一个运行时重新配置操作。

可观察性

方法

路径

目的

GET

/metrics

Prometheus 展示格式。

POST

/metrics/reset

将所有可观察性指标重置为初始状态。

诊断与调试

方法

路径

目的

GET

/loglevel

列出或检查日志记录器级别;也接受 level 来修改一个。

GET

/threads

列出活动的 Python 线程及其堆栈跟踪。

GET

/periodic-threads

列出注册的周期性线程及其摘要计数。

GET

/periodic-threads/{thread_name}

单个周期线程的详细状态。

GET

/periodic-threads-health

对关键/高层周期线程的快速健康检查。

GET

/env

转储进程环境变量(JSON主体,text/plain)。

POST

/run_script

在受限的沙箱中执行上传的 Python 脚本。

存活性和健康状态#

GET /#

基本的存活检查。返回一个静态负载,指示 HTTP 服务器正在运行;它会触及缓存引擎。对于还验证引擎是否已初始化的探测,请使用 /healthcheck

响应 (200 OK):

{
  "status": "ok",
  "service": "LMCache HTTP API"
}

HTTP 状态码:

  • 200: 服务器正常运行(无条件;不影响引擎)。

示例:

curl -s http://localhost:8080/

GET /healthcheck#

健康检查端点适用于 Kubernetes 的存活和就绪探针。200 响应意味着 HTTP 服务器处于活动状态 并且 MP 缓存引擎对象已连接到 app.state503 响应表示引擎尚未存在(仍在初始化中,或初始化失败)。该检查验证引擎属性是否已设置;它不会调用引擎以确认更深层次的存活状态。

响应 (200 OK):

{
  "status": "healthy"
}

响应 (503 服务不可用):

{
  "status": "unhealthy",
  "reason": "engine not initialized"
}

HTTP 状态码:

  • 200: 引擎已连接到 app.state (正常)。

  • 503: 引擎未初始化(仍在启动中,或初始化失败)。

示例:

curl -s http://localhost:8080/healthcheck

Kubernetes 探针代码片段:

livenessProbe:
  httpGet:
    path: /healthcheck
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 10
readinessProbe:
  httpGet:
    path: /healthcheck
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

检查和状态#

GET /status#

返回 MP 引擎内部状态的详细快照。有效负载由 MPCacheServer.report_status() 组装:一组固定的引擎级字段、完整的存储管理器状态,以及每个加载模块贡献的键(因此确切的键集取决于哪些模块处于活动状态 — registered_gpu_ids / cache_context_meta 来自传输模块,active_prefetch_jobs 来自查找模块,混合模式添加它们自己的字段)。旨在供操作员和调试使用,而非监控(使用 Prometheus 指标获取时间序列数据 — 参见 可观察性)。

响应 (200 OK):

{
  "is_healthy": true,
  "engine_type": "MPCacheServer",
  "chunk_size": 256,
  "hash_algorithm": "builtin-hash",
  "active_sessions": 2,
  "registered_gpu_ids": [0, 1],
  "cache_context_meta": {
    "0": {
      "model_name": "meta-llama/Llama-3.1-8B-Instruct",
      "world_size": 1,
      "kv_cache_layout": {
        "num_layers": 32,
        "num_blocks": 12345,
        "cache_size_per_token": 131072,
        "kernel_groups": [
          {
            "kernel_group_idx": 0,
            "engine_group_idx": 0,
            "object_group_idx": 0,
            "num_layers": 32,
            "layer_indices": [0, 1, "..."],
            "tokens_per_block": 16,
            "slots_per_block": 16,
            "dtype": "torch.bfloat16",
            "engine_kv_concrete_shape": "...",
            "is_mla": false,
            "engine_kv_format": "...",
            "engine_kv_shape": "...",
            "attention_backend": "..."
          }
        ]
      }
    }
  },
  "active_prefetch_jobs": 0,
  "storage_manager": {
    "is_healthy": true,
    "...": "backend-specific fields"
  }
}

响应 (503 服务不可用) 当引擎尚未初始化时:

{
  "error": "engine not initialized"
}

HTTP 状态码:

  • 200: 状态快照已返回。

  • 503: 引擎尚未在 app.state 上初始化。

示例:

curl -s http://localhost:8080/status | jq

GET /config#

以单一缩进 JSON 文档返回注册在 app.state.configs 上的全部服务器端配置对象(通常包括 mpstorage_managerobservability)。数据类通过 safe_asdict 序列化;其他值通过 make_json_safe 处理。可用于确认进程实际加载的配置内容(含环境变量覆盖),无需重启服务。

响应 (200 OK):

{
  "mp": {
    "http_host": "0.0.0.0",
    "http_port": 8080,
    "...": "..."
  },
  "storage_manager": {
    "...": "..."
  },
  "observability": {
    "...": "..."
  }
}

响应503 服务不可用),当配置尚未连接到 app.state 时:

{
  "error": "configs not initialized"
}

HTTP 状态码:

  • 200: 配置文档已返回。

  • 503: 配置尚未连接到 app.state

示例:

curl -s http://localhost:8080/config | jq

GET /config/adapters#

列出引擎已加载的每个 L2 适配器,按配置顺序排列。加载了哪些存储后端是配置检查的内容,因此它位于配置组中,而不是在 /cache 下。这是唯一的活动适配器列表;它取代了旧的 GET /reconfigure/backends``(可重新配置的后端是 ``type_name 值,其 reconfigurable 标志为 true)。

响应 (200 OK):

{
  "adapters": [
    {"index": 0, "type_name": "S3L2Adapter", "tier": "l2", "primary": true, "reconfigurable": false},
    {"index": 1, "type_name": "dax", "tier": "l2", "primary": false, "reconfigurable": true}
  ]
}

primary 仅在第一个条目上为 truereconfigurable 对于接受 /reconfigure 操作的适配器为 true — 将该适配器的 type_name 作为 {backend} 路径参数传递给 GET /reconfigure/{backend}/status 和重新配置操作。没有 L2 后端的引擎返回 {\"adapters\": []}``(仍然是 ``200 — 引擎已初始化,只是没有 L2 存储)。

HTTP 状态码:

  • 200: 成功(包括无适配器的情况)。

  • 503: 引擎未初始化。

示例:

curl -s http://localhost:8080/config/adapters | jq

GET /version, GET /lmc_version, GET /commit_id#

版本描述符。每个返回一个裸 JSON 字符串(而不是对象):

  • GET /version — 来自 lmcache.utils.get_version() 的组合描述符,格式为 \"<version>-<commit_id>\"(例如 \"0.3.1-ca79ea33\")。在没有构建时版本元数据的源代码检出中,每个缺失的组件将回退为字面值 \"NA\"(因此没有元数据的构建返回 \"NA-NA\")。

  • GET /lmc_version — 原始包版本字符串 (lmcache.utils.VERSION);当生成的 lmcache._version 模块缺失时返回空字符串 \"\"

  • GET /commit_id — 构建中嵌入的 git 提交 ID (lmcache.utils.COMMIT_ID);当不可用时为空字符串 \"\"

这三者都是无条件的 200 OK

响应 (200 OK):

"0.3.1-ca79ea33"

HTTP 状态码:

  • 200: 返回的版本字符串(对所有三个路由无条件)。

示例:

curl -s http://localhost:8080/version
curl -s http://localhost:8080/lmc_version
curl -s http://localhost:8080/commit_id

缓存管理#

POST /cache/clear#

强制清除当前在某个层级(今天为 l1)中持有的 所有 KV 缓存数据。

警告

此端点为破坏性操作,会绕过读/写锁。正在进行的存储或预取操作可能因此损坏。仅在服务器空闲时,或在已知缓存状态异常需要恢复时使用。

请求体: 可选 -- 缺失(或空)体将使用以下默认值。

字段

类型

描述

tier

字符串

可选(默认 l1)。要清除的层级;目前仅支持 l1。任何其他值将返回 400

force

布尔值

可选(默认 true)。当前被接受但 不被遵循 -- 清除操作始终强制清除(活动锁被忽略),无论该值如何。

响应 (200 OK):

{"status": "ok", "cleared": {"tier": "l1"}}

HTTP 状态码:

  • 200: 层已清除。

  • 400: 不支持的 tier``(除了 ``l1 以外的任何内容)。

  • 503: 服务器未初始化 ({\"detail\": \"服务器未初始化\"})。

示例:

# the body is optional; this clears the default tier (l1)
curl -s -X POST http://localhost:8080/cache/clear

POST /cache/checksums#

计算引擎 KV Cache 的 MD5 校验和,每个哈希块分组为 chunk_size 块。MP 模式通过块 ID 原生地访问 KV 存储(与 STORE / RETRIEVE 使用的单位相同),因此该端点完全以块为中心。旨在用于诊断和来自 lmcache bench server 的往返完整性检查——不用于推理数据路径。

请求主体:

字段

必需的

描述

block_ids

引擎块 ID 作为整数的 JSON 列表,例如 [0, 1, 2, 3]

chunk_size

正整数 — 每个哈希块的块数。

instance_id

否(默认 0

引擎上注册的 KV 上下文 ID。

layerwise

否(默认 false

如果为 true,则返回按 "layer_<idx>" 键入的逐层校验和;否则返回每个块的所有层的单个聚合摘要。

响应 (200 OK):

{
  "status": "success",
  "chunk_size": 2,
  "num_chunks": 2,
  "chunk_checksums": ["<md5>", "<md5>"],
  "layerwise": false,
  "block_id_ranges": "0,[2,5],8"
}

layerwise=true 时,chunk_checksums 是一个以 "layer_<idx>" 为键的字典,其值是逐层列表。

HTTP 状态码:

  • 200: 成功。

  • 400: block_ids 为空,或者 chunk_size 缺失或非正数。

  • 404: instance_id 未注册,或者注册的 KV 张量为空。

  • 501: 引擎没有 cache_contexts,或者该端点不支持 KV 格式(页面缓冲融合和跨层布局在真正需要之前被拒绝)。

  • 503: 引擎尚未在 app.state 上初始化。

示例:

curl -s -X POST http://localhost:8080/cache/checksums \
    -H 'Content-Type: application/json' \
    -d '{"block_ids": [0, 1, 2, 3], "chunk_size": 2}'

缓存对象和预取#

两个端点 — DELETE /cache/objectsGET /cache/objects — 允许操作员从配置的缓存后端中清除键并列举当前驻留的内容。(要列举配置的后端本身,请在配置组中使用 GET /config/adapters。)

另一对 — POST /cache/prefetchesGET /cache/prefetches/{request_id} — 允许操作员(或协调者)**预热**节点的 L1 以便在流量到来之前从 L2 加载,并轮询加载直到完成;它们在本节末尾有文档说明。协调者提供了一个基于 instance_id 的变体 — 参见 多服务器协调

两个对象端点都接受一个可选的 adapter 选择器 — 在 GET /cache/objects 上的查询参数(?adapter=<type_name>)和在 DELETE /cache/objects 上的主体字段。省略它将针对 **主要**(首次配置的)适配器。当多个适配器共享同一个 type_name 时,第一个匹配的适配器将生效。使用 GET /config/adapters 来了解有效的选择器。

这两个接口旨在用于操作员/管理员工作流程(“清除该用户的键”,“显示当前驻留的内容”,“在重命名后收集孤儿”)。它们**不**在推理数据路径上。

DELETE /cache/objects deletes from L1, L2, or both, selected by its tier field (default l2). GET /cache/objects lists L2 only.

协调器的逐出循环自动使用 DELETE /cache/objects``(参见 :doc:`coordinator` “L2 使用跟踪和逐出”);``GET /cache/objects 端点也支持协调器的启动重新同步。手动使用 curl 保留给临时操作员操作和调试。

DELETE /cache/objects#

Delete a caller-supplied list of keys from L1, L2, or both (tier). Idempotent: absent keys are skipped silently. L2 deletes skip keys locked by in-flight store/load tasks so an active transfer is never corrupted; L1 deletes skip read/write-locked keys unless force is set. Blocking adapter I/O is run off the event loop. When the tier includes L2 the primary (or selected) L2 adapter must be configured, else 503; a pure l1 delete needs no adapter.

Per-key successful L2 deletions fire on_l2_keys_deleted on the adapter's listeners — when the coordinator is wired (see --coordinator-l2-event-reporting), the deletions show up at the coordinator's POST /quota/events as "type": "delete" events. The coordinator's eviction + usage trackers learn about the deletion from that event flow, not from the response of this call.

请求主体:

字段

类型

描述

keys

列表[编码对象键]

必填。要删除的对象键(见下方模式)。每个请求的批量限制为 10000 个键。

tier

字符串

Optional (default l2). One of l1 / l2 / all.

force

布尔值

Optional (default false). When true, delete an L1 key even if it is read/write-locked (no effect on L2).

adapter

字符串

Optional (default: primary, first-configured adapter). The type_name of the target L2 adapter (see GET /config/adapters); ignored when tier is l1.

每个 EncodedObjectKey

{
  "chunk_hash_hex": "abc123...",
  "model_name": "meta-llama/Llama-3-8B",
  "kv_rank": 0,
  "object_group_id": 0,
  "cache_salt": "user-a"
}

object_group_id``(默认 ``0)和 cache_salt``(默认 ``"")是为了与旧的 wire 负载向后兼容而可选的。

响应 (200 OK):

{
  "deleted": 4,
  "skipped": 0,
  "ok": true
}

deleted is the total keys removed across the requested tiers (L1 removals plus the L2 batch size); skipped is the L1 keys refused because they were locked (non-force only). On an L2 adapter failure the response is still 200 with ok=false and an error field carrying the reason.

HTTP 状态码:

  • 200: request processed (check ok for the L2 adapter outcome).

  • 400: 批量超出限制,或者一个键负载违反了 ObjectKey 不变式(错误的十六进制,model_name 中的 @,禁止的 cache_salt 字符)。

  • 404: ``adapter``(主体)与任何已配置的适配器不匹配。

  • 422: Pydantic 级别的主体形状失败(缺少 keys,字段类型错误)。

  • 503: engine not initialized, or no L2 adapters configured (only when tier includes L2).

Example: delete from both tiers.

curl -s -X DELETE http://localhost:8080/cache/objects \
    -H 'Content-Type: application/json' \
    -d '{
        "tier": "all",
        "keys": [
          {"chunk_hash_hex": "aa", "model_name": "m",
           "kv_rank": 0, "object_group_id": 0, "cache_salt": "user-a"}
        ]
    }'
# -> {"deleted": 2, "skipped": 0, "ok": true}

GET /cache/objects#

分页当前驻留在一个 L2 适配器中的键。

查询参数:

名称

默认

描述

adapter

主适配器

目标适配器的 type_name``(参见 ``GET /config/adapters)。省略以目标主适配器(第一个配置的适配器)。当多个适配器共享同一 type_name 时,优先匹配第一个。

model_name

将结果限制为与 model_name 匹配的键。

page_size

500

每页最大条目数。必须在 [1, 5000] 范围内;超出范围的值将被拒绝并返回 422(不会被静默限制)。

page_token

来自上一页的 next_page_token 的不透明游标。在第一次调用时省略;在后续调用中逐字传回。

页面令牌是适配器私有的;请勿解析或修改它。支持列出功能的适配器(目前仅通过 ListObjectsV2 的 S3 适配器)保证尽力而为的一致性,而不是快照隔离——在分页遍历期间的并发存储或删除可能导致键出现、消失或在页面之间移动。

响应 (200 OK):

{
  "adapter": "S3L2Adapter",
  "entries": [
    {
      "key": {
        "chunk_hash_hex": "abc123",
        "model_name": "meta-llama/Llama-3-8B",
        "kv_rank": 0,
        "object_group_id": 0,
        "cache_salt": "user-a"
      },
      "size_bytes": 4194304
    }
  ],
  "next_page_token": "opaque-cursor-string"
}

next_page_token 在列出内容耗尽时为 null

HTTP 状态码:

  • 200: 成功。

  • 400: 错误的 page_token(适配器级别)。

  • 404: ?adapter=<name> 与任何已配置的适配器不匹配。

  • 422: page_size 超出 [1, 5000] 范围。

  • 501: 选定的适配器不支持列出。在 v1 中,只有 S3L2Adapter 支持;由 SerdeL2AdapterWrapper 包装的适配器继承被包装适配器的行为。

  • 503: 引擎未初始化,或未配置 L2 适配器。

示例: 为模型分页每个键。

next=""
while :; do
  page=$(curl -s "http://localhost:8080/cache/objects?model_name=meta-llama/Llama-3-8B&page_size=500&page_token=$next")
  echo "$page" | jq '.entries[]'
  next=$(echo "$page" | jq -r '.next_page_token // empty')
  [ -z "$next" ] && break
done

POST /cache/prefetches#

通过从 L2 提前 加载一个令牌序列的块来预热一个节点的 L1,以便使用它们的第一个请求命中 L1,而不是在线支付 L2 获取的费用。当工作负载即将路由到一个节点(流量转移、热共享系统提示)时,这非常有用。

调用者通过 token ids 描述内容,而不是通过内部缓存键(键是内容哈希加上每个排名的布局位图,调用者无法构造)。服务器对 tokens 进行哈希,将每个块扩展到节点的各个排名,并提交一个 warm 预取:加载的块被 保留**(永久)并保持 **解锁 — 没有下游读取器来固定它们,因此后续的真实查找会自行加锁。调用立即返回;加载在存储管理器自己的线程中运行。它在所有配置的 L2 适配器之间合并,因此没有 ?adapter= 选择器。

请求主体:

字段

类型

描述

model_name

字符串

提供的模型 ID,完全按照注册的方式(例如 Qwen/Qwen3-8B)。

world_size

整数

节点的 KV 布局和排名分发中注册的值 vLLM(对于单 GPU,TP=1 部署为 1)。

token_ids

列表[int]

提示令牌 ID。必须使用与存储相同的分词器/特殊令牌设置,并且至少包含一个完整的 chunk_size 令牌——只有完整的块会被预热。

cache_salt

字符串

每个租户的密钥隔离;必须与存储匹配(默认 "")。

响应 (202 Accepted):

{"request_id": "abc123", "chunks": 12, "status": "submitted"}

当序列短于一个块时,不会提交任何内容,也没有 request_id 可供轮询:

{"chunks": 0, "status": "noop"}

HTTP 状态码:

  • 202: 已提交(或如上所述的 noop)。

  • 400: token_ids 超过每个请求的上限,或 cache_salt 违反了其不变性。

  • 409: 未为 (model_name, world_size) 注册布局 — 该模型尚未在此节点上分配 KV Cache(请先启动 vLLM)。

  • 422: 请求体未通过字段级验证。

  • 503: 引擎未初始化。

示例:

curl -s -X POST http://localhost:8080/cache/prefetches \
    -H 'Content-Type: application/json' \
    -d '{"model_name": "Qwen/Qwen3-8B", "world_size": 1,
         "token_ids": [101, 102, 103], "cache_salt": "user-a"}'
# -> {"request_id": "abc123", "chunks": 1, "status": "submitted"}

GET /cache/prefetches/{request_id}#

轮询已提交的热预取。热预取不持有锁,因此轮询仅报告进度;第一个观察到完成的轮询将删除作业(精确一次),因此对同一 ID 的后续轮询返回 404

响应 (200 OK) 当负载运行时:

{"status": "pending"}

…并且一旦完成:

{"status": "completed", "found_keys": 12, "total_keys": 12}

found_keys / total_keys 计数仅包括由此请求 从 L2 加载的 块;已经驻留在 L1 的块在 reserve_write 时被跳过且不计入,因此部分驻留的热请求会低估驻留块的数量(冷请求会加载并计算所有内容)。热请求使用间隙容忍的 SPARSE 剪裁策略,因此一个已经驻留的块不会阻止其他块的加载——它会加载每个尚未驻留的块并报告该数量。不计算驻留块是故意的:一个已经存在的条目可能是来自另一个查找的瞬态临时条目,因此将其视为热请求可能会误导。

HTTP 状态码:

  • 200: 状态报告(pendingcompleted)。

  • 404: 未知 request_id — 已经完成并消费,或从未提交。

  • 503: 引擎未初始化。

示例:

curl -s http://localhost:8080/cache/prefetches/abc123
# -> {"status": "completed", "found_keys": 1, "total_keys": 1}

配额管理#

这些端点管理由 IsolatedLRU 逐出策略(通过 --eviction-policy IsolatedLRU 选择)消耗的每个 cache_salt 存储预算。配额是 软性 的:设置限制并不会拒绝写入 — 任何超出预算的 cache_salt 会在下一个逐出周期(约 1 秒)被逐出。没有注册配额的 cache_salt 有一个有效限制为 0 字节,因此其数据将在下一个周期被清除(白名单语义)。

对于未使用 --eviction-policy IsolatedLRU 启动的引擎,这些端点为空操作:QuotaManager 仍然存在,但 LRU 策略会忽略已注册的配额。

空盐的 URL 转义。 cache_salt=\"\"(无盐/匿名流量)不能出现在 URL 路径参数中,因此 API 接受哨兵 _default 作为替代。PUT /quota/_default 设置 cache_salt=\"\" 的配额,_default 在空盐的响应中被回显。合法存储数据的用户 cache_salt=\"_default\" 不能通过此 HTTP API 与匿名流量区分管理——两者映射到相同的路径参数;选择任何其他值(例如 \"default\")以消除歧义。

PUT /quota/{cache_salt}#

创建或更新配额。

路径参数: cache_salt — 租户标识符(使用 _default 表示空盐;请参阅章节介绍)。

请求主体:

字段

类型

描述

limit_gb

浮点数

必需。配额以 GB 为单位。必须是有限的且非负的。

响应 (200 OK):

{"cache_salt": "alice", "limit_gb": 10.0, "status": "ok"}

HTTP 状态码:

  • 200: 配额已设置或更新。

  • 400: JSON 格式错误,缺少 limit_gblimit_gb 非数字,nan / inf,或负值。

  • 503: 引擎未初始化。

示例:

curl -s -X PUT http://localhost:8080/quota/alice \
    -H 'Content-Type: application/json' \
    -d '{"limit_gb": 10.0}'

GET /quota/{cache_salt}#

读取当前配额和一个 cache_salt 的实时使用情况。

路径参数: cache_salt — 租户标识符(使用 _default 表示空盐)。

响应 (200 OK):

{
  "cache_salt": "alice",
  "limit_gb": 10.0,
  "current_usage_gb": 2.137,
  "exists": true
}

existsfalse 当这个 cache_salt 从未注册过配额时(limit_gb 此时为 0.0,而 current_usage_gb 反映当前为该盐缓存的字节数——这些字节将在下一个周期下逐出 IsolatedLRU)。这个端点对于未知的盐永远不会返回 404

HTTP 状态码:

  • 200: 返回配额和使用情况(即使盐没有注册配额——此时 existsfalse;永远不会 404)。

  • 503: 引擎未初始化。

示例:

curl -s http://localhost:8080/quota/alice | jq

DELETE /quota/{cache_salt}#

删除 cache_salt 的配额条目。任何仍然缓存于此 cache_salt 下的字节将在下一个逐出周期中超出预算(有效限制降至 0),并将被逐出。

路径参数: cache_salt — 租户标识符(使用 _default 表示空盐)。

响应 (200 OK):

{"cache_salt": "alice", "status": "removed"}

当给定的 cache_salt 没有注册配额时,响应为 {\"cache_salt\": \"...\", \"status\": \"not_found\"}(仍然是 200 OK)。

HTTP 状态码:

  • 200: 配额条目已移除 ("removed") 或不存在 ("not_found");永远不会是 404

  • 503: 引擎未初始化。

示例:

curl -s -X DELETE http://localhost:8080/quota/alice

GET /quota#

列出每个注册的配额及其实时使用情况。

响应 (200 OK):

{
  "users": {
    "alice": {"limit_gb": 10.0, "current_usage_gb": 2.137},
    "bob":   {"limit_gb":  4.0, "current_usage_gb": 0.812}
  }
}

只有带有 注册 配额的 cache_salt 值会出现;空盐会在 _default 键下报告。

HTTP 状态码:

  • 200: 返回配额列表 ({\"users\": {}} 当没有注册时)。

  • 503: 引擎未初始化。

示例:

curl -s http://localhost:8080/quota | jq

运行时 L2 重新配置#

这些端点在服务器具有可运行时重新配置的 L2 适配器时可用。它们仅更改 LMCache 的运行时映射和元数据;后端资源如 DAX 设备路径必须已经存在,并且服务器必须能够读取和写入。该端点将 backendoperation 和 JSON 请求体路由到通用 L2 适配器重新配置 API,而后端特定的验证和迁移语义则保留在适配器内部。

backendoperation 路径段被规范化(去除空格并转为小写)。在请求体内,adapter_index(默认为 0)是 后端本地 的——它仅索引该后端的适配器,而不是整个引擎的适配器列表。如果 L2 适配器被序列化/反序列化包装,后端字符串仍然是配置的 L2 适配器类型,而不是序列化/反序列化包装类型。

备注

这里接受的后端字符串通过 GET /config/adapters 发现:每个 reconfigurable 标志为 true 的适配器都可以通过其 type_name 进行访问。

GET /reconfigure/{backend}/status#

报告一个后端类型的运行时可管理适配器。每个适配器条目的 adapter_index 被重写为其 后端本地 的 0 基索引(在操作请求体中传回的值)。

路径参数: backend — 适配器 type_name``(标准化:去除空格并小写;通过 ``GET /config/adapters 发现有效值)。

响应 (200 OK):

{
  "enabled": true,
  "backend": "dax",
  "num_adapters": 1,
  "adapters": [
    {"adapter_index": 0, "...": "backend-specific adapter fields"}
  ]
}

一个未知或空的后端返回 enabled=falsenum_adapters=0adapters=[](这不是 404)。

HTTP 状态码:

  • 200:返回的状态(包括上述未知后端的情况)。

  • 400: backend 为空。

  • 503: 引擎未初始化。

示例:

curl -s http://localhost:8080/reconfigure/dax/status | jq

POST /reconfigure/{backend}/{operation}#

将一个重新配置操作应用于后端适配器。请求体是一个 JSON 对象,其接受的字段取决于后端和操作。200 响应是存储管理器的 reconfigure_l2_adapter 返回的内容(一个后端定义的字典)。

路径参数: backend``(适配器 ``type_name)和 operation``(例如 ``add / remove / resize 用于 dax)。两者都经过标准化(去除空格并转为小写)。

对于 通用 路径(任何后端除了 dax),请求体包含 adapter_index 以及任何特定于后端的字段,这些字段会逐字转发给适配器。

对于 Device-DAX (backend=dax),使用 JSON 请求体,因为 DAX 路径包含斜杠。接受的操作和字段包括:

操作

主体字段

add

device_path(str,必填),size(字节数或字符串,例如"100GiB",必填),adapter_index(默认0)。

remove

device_path (str, 必需), mode (migrate | evict | drain, 默认 migrate), force (bool, 默认 false), adapter_index (默认 0)。

resize

device_path(str,必需),size(int 或 string,必需),modemigrate | evict,默认 migrate),force(bool,默认 false),adapter_index(默认 0)。

size 接受一个整数字节数或带有基于 1024 的单位后缀的字符串(bkibmibgibtib 及其 k/m/g/t 别名),例如 "100GiB";它必须解析为一个正值。

响应 (200 OK):

主体是后端的 reconfigure_l2_adapter 结果(一个后端定义的字典)。成功的 DAX add 看起来像这样:

{
  "status": "ok",
  "operation": "add",
  "adapter_index": 0,
  "device": {"device_path": "/dev/dax0.0", "state": "active", "size_bytes": 107374182400}
}

HTTP 状态码:

  • 200: 成功(主体是存储管理器的重新配置结果)。

  • 400: 空的 backend/operation,不支持的 DAX 操作,或无效的 size

  • 404: adapter_index 超出后端范围。

  • 422: 请求体验证失败(例如,缺少必填字段或 DAX 请求体中的未知字段——DAX 请求体会拒绝额外字段)。

  • 503: 引擎未初始化。

示例:

curl -s -X POST http://localhost:8080/reconfigure/dax/add \
    -H 'Content-Type: application/json' \
    -d '{"device_path": "/dev/dax0.0", "size": "100GiB"}'

有关详细的请求示例、模式语义和验证指导,请参阅 设备-DAX (/dev/dax)

可观察性#

GET /metrics#

默认 prometheus_client 注册表中注册的每个指标的 Prometheus 展示格式 (Content-Type: text/plain)。直接从 Prometheus 抓取此数据。有关导出指标的列表,请参见 可观察性

响应 (200 OK, text/plain): Prometheus 展示格式的指标主体。

HTTP 状态码:

  • 200:返回的指标。

示例:

curl -s http://localhost:8080/metrics

POST /metrics/reset#

将所有 LMCache 可观察性指标重置为初始状态(reset_observability_metrics)。面向测试框架和基准测试,不适用于生产环境。

响应200 OK, text/plain):

ok

HTTP 状态码:

  • 200:指标已重置。

示例:

curl -s -X POST http://localhost:8080/metrics/reset

诊断与调试#

GET /loglevel#

在运行时检查或修改 Python 日志记录器级别。所有响应都是 text/plain。该端点有三种模式,由查询参数驱动:

查询

行为

(无参数)

列出所有在 logging 中注册的记录器及其级别。

?logger_name=<name>

返回指定记录器的有效级别。

?logger_name=<name>&level=<LEVEL>

将命名的日志记录器(及其处理程序)设置为 LEVELDEBUG/INFO/WARNING/ERROR/CRITICAL;不区分大小写)。如果级别未知,则返回 400

传递 level 而不带 logger_name 不匹配任何模式,并返回 200 和一个 null 响应体。

响应 (200 OK, text/plain): 每个注册的日志记录器一行 ``<logger>: <LEVEL>``(列表模式),一个 ``<logger>: <LEVEL>``(读取模式),或更新级别的确认(设置模式)。

HTTP 状态码:

  • 200: 列出、读取或设置的级别(也包括上述的 null-body 情况)。

  • 400: level 不是已知的日志级别。

示例:

# list everything
curl -s http://localhost:8080/loglevel

# read one
curl -s 'http://localhost:8080/loglevel?logger_name=lmcache'

# elevate to DEBUG
curl -s 'http://localhost:8080/loglevel?logger_name=lmcache&level=DEBUG'

GET /threads#

列出服务器进程中活动的 Python 线程及其堆栈跟踪,并提供总计摘要(Content-Type: text/plain)。对于实时调试挂起或失控的工作线程非常有用。

查询

行为

?name=<substr>

仅保留名称包含 <substr> 的线程(不区分大小写)。

?thread_id=<int>

仅保留 ident 匹配的线程。

警告

响应包含实时堆栈跟踪,并可能泄露内部代码路径和状态。在生产环境中限制对该端点的网络访问。

响应 (200 OK, text/plain):总线程数摘要,后面跟着每个线程的名称、ident 和当前堆栈跟踪。

HTTP 状态码:

  • 200: 返回线程列表。

示例:

curl -s 'http://localhost:8080/threads?name=periodic'

GET /periodic-threads#

返回 PeriodicThreadRegistry 的 JSON 快照:按级别统计以及每个线程的状态(上次运行时间戳、最新摘要等)。

查询

行为

?level=critical|high|medium|low

仅包含给定级别的线程。对未知情况返回 400

?running_only=true

仅包含当前正在运行的线程。

?active_only=true

仅包括被视为活动的线程(最近的滴答)。

响应 (200 OK):

{
  "summary": {
    "total_count": 4,
    "running_count": 4,
    "active_count": 4,
    "by_level": {
      "critical": {"total": 1, "running": 1, "active": 1},
      "high":     {"total": 2, "running": 2, "active": 2},
      "medium":   {"total": 1, "running": 1, "active": 1},
      "low":      {"total": 0, "running": 0, "active": 0}
    }
  },
  "threads": [
    {
      "name": "...",
      "level": "high",
      "interval": 5.0,
      "is_running": true,
      "is_active": true,
      "last_run_ago": 1.2,
      "total_runs": 120,
      "failed_runs": 0,
      "success_rate": 100.0,
      "last_summary": {"...": "..."}
    }
  ]
}

HTTP 状态码:

  • 200: 返回快照。

  • 400: 未知 level 过滤器。

示例:

curl -s 'http://localhost:8080/periodic-threads?level=critical' | jq

GET /periodic-threads/{thread_name}#

单个周期性线程的详细状态(与上面 threads 列表中显示的每个线程对象相同)。

路径参数: thread_name — 注册的周期线程名称。

响应 (200 OK):

{
  "name": "storage-flush",
  "level": "critical",
  "interval": 5.0,
  "is_running": true,
  "is_active": true,
  "last_run_ago": 1.2,
  "total_runs": 120,
  "failed_runs": 0,
  "success_rate": 100.0,
  "last_summary": {"...": "..."}
}

响应 (404 Not Found) 当名称未知时:

{"error": "Thread not found: <name>"}

HTTP 状态码:

  • 200: 返回的线程状态。

  • 404: 没有名为该名称的周期性线程。

示例:

curl -s http://localhost:8080/periodic-threads/storage-flush | jq

GET /periodic-threads-health#

快速健康检查,仅涵盖 criticalhigh 级别的周期性线程。当线程被标记为运行但在预期间隔内未进行计时时,它会被标记为不健康。始终返回 200 — 健康状态通过 healthy 布尔值传达,而不是通过 HTTP 状态。

响应 (200 OK):

{
  "healthy": true,
  "unhealthy_count": 0,
  "unhealthy_threads": []
}

当出现滞后时:

{
  "healthy": false,
  "unhealthy_count": 1,
  "unhealthy_threads": [
    {
      "name": "storage-flush",
      "level": "critical",
      "last_run_ago": 42.5,
      "interval": 5.0
    }
  ]
}

HTTP 状态码:

  • 200:健康报告 — 始终为 200,即使在不健康时(healthy 布尔值为 false)。

示例:

curl -s http://localhost:8080/periodic-threads-health

GET /env#

将进程环境变量转储为排序后的美观打印 JSON 文档。响应的 Content-Typetext/plain,因此可以直接通过管道传输到终端。

警告

有效负载包含 每个 环境变量,包括通过环境注入的任何秘密。没有任何编辑或身份验证 — 在生产环境中限制对该端点的网络访问。

响应200 OK, text/plain):

{
  "HOME": "/root",
  "LMCACHE_LOG_LEVEL": "INFO",
  "PATH": "/usr/local/bin:/usr/bin"
}

HTTP 状态码:

  • 200: 环境转储已返回。

示例:

curl -s http://localhost:8080/env

POST /run_script#

在服务器进程中执行上传的 Python 脚本。脚本作为多部分表单数据上传,字段名称为 script,并在受限的 __builtins__ 下被 exec 执行(仅允许 printstrintfloatlistdicttupleset 和受保护的 __import__)。仅允许导入在 --script-allowed-imports 中列出的模块;运行的 FastAPI app 会注入到脚本的全局变量中。如果脚本赋值给名为 result 的变量,则返回其字符串化值;否则返回正文为 "脚本成功执行"Content-Type: text/plain)。

请求体: 多部分表单数据,包含一个 script 文件字段(要执行的 Python 源代码)。不是 JSON 格式的请求体。

危险

此端点在进程中运行调用者提供的代码。受限的内置函数不是安全沙箱——结合注入的app对象和任何允许的导入,将其视为完全的远程代码执行。切勿在不受信任的网络上公开它。

响应 (200 OK, text/plain): 如果脚本分配了字符串化的 result 变量,则返回该变量,否则返回 脚本成功执行

HTTP 状态码:

  • 200: 脚本已执行。

  • 400: 未提供 script 文件。

  • 500: 在导入设置或执行期间引发了异常(主体:"执行脚本时出错:<reason>")。

示例:

curl -s -X POST http://localhost:8080/run_script \
    -F 'script=@my_script.py'

添加新端点#

端点会从 lmcache/v1/multiprocess/http_apis/ 自动发现。要添加一个新的仅限 MP 的端点:

  1. 在该目录中创建一个名为 <name>_api.py 的新模块。

  2. 定义一个模块级的 router = APIRouter()

  3. 使用 FastAPI 装饰器在 router 上注册处理程序。

  4. 通过 request.app.state.engine 访问引擎,并检查 None 情况(引擎尚未初始化)。

HTTPAPIRegistry 将在启动时自动加载模块 — 无需编辑中央注册列表。

如果路由足够通用,可以与嵌入 vLLM 的 API 服务器共享,请将其添加到 lmcache/v1/internal_api_server/common/ 下。它将在 MP 端通过 common_api.py 被加载,除非其模块名称在 _MP_INCOMPATIBLE_MODULES 中列出(该列表保留给需要 vLLM 特定 app.state 属性的模块;当前列表为空)。位于 internal_api_server/vllm/ 下的处理程序仍然可以通过从 http_apis/ 下的组模块中包含其路由器在 MP 服务器上显示(如 info_api.py 对版本端点所做的那样)。

添加新端点时,请在此页面上添加一个对应的章节,说明该端点的用途、请求/响应结构以及一个示例 curl 调用。