跳到主要内容

模型服务

后端 API​

模型服务路由分布在 llm_d_lens/model_service/router.py 的用户、管理员、公共和内部路由器中。

列出模型访问令牌​

列出调用方当前有效的模型访问令牌。响应永远不包含明文令牌值,只包含用于识别和日后吊销所需的元数据。

GET /api/v1/model-service/tokens

请求:无请求体。调用方必须已通过 Lens 用户会话进行身份验证。

响应:返回一个 items 数组,其中每个元素都是经过 ModelAccessToken.api_payload() 投影后的 ModelAccessToken 记录,因此 tokenHash 字段被省略。

{
"items": [
{
"id": "mat-7f2c4a19b6d1",
"userId": "user-123",
"name": "laptop",
"tokenHint": "9mQx",
"status": "active",
"lastUsedAt": "2026-09-23T08:15:00Z",
"lastUsedIp": "10.0.0.25",
"createdAt": "2026-09-21T12:00:00Z"
}
]
}

创建模型访问令牌​

为调用方创建一个新的模型访问令牌。明文令牌仅返回一次,与持久化保存的令牌元数据一起返回。

POST /api/v1/model-service/tokens

请求体(TokenCreateRequest):

  • name(string):令牌的显示名称。
{
"name": "automation"
}

响应:一个 ModelAccessToken 载荷,外加一次性的 token 字符串。

{
"id": "mat-2c47de48a35b",
"userId": "user-123",
"name": "automation",
"tokenHint": "K8xP",
"status": "active",
"createdAt": "2026-09-23T09:00:00Z",
"token": "lens-mk-bTj9X7mW4zP1qN8sR6uC2yF5hL0aV3dK1mQ"
}

重新生成模型访问令牌​

吊销调用方当前有效的一个或多个令牌,然后签发一个新令牌。你可以选择性地为替换后的新令牌指定一个名称。

POST /api/v1/model-service/tokens/regenerate

请求体:可选的 TokenCreateRequest。若省略,该路由使用 "default"。

{
"name": "default"
}

响应:新签发的令牌元数据,外加新的明文令牌。

{
"id": "mat-54b2a51d9d93",
"userId": "user-123",
"name": "default",
"tokenHint": "F6rN",
"status": "active",
"createdAt": "2026-09-23T09:05:00Z",
"token": "lens-mk-Gq4nP1sT8uV2xY5zA7bC9dE3fH6jK8mN0pR"
}

吊销模型访问令牌​

吊销调用方此前签发的一个模型访问令牌。吊销后,该令牌将无法再用于网关访问。

DELETE /api/v1/model-service/tokens/{token_id}

请求:无请求体。

  • token_id(string,路径参数):令牌 ID,例如 mat-7f2c4a19b6d1。

响应:204 No Content。该端点不返回 JSON 响应体。

列出可访问的模型​

列出有在线且权重为正的提供方、并且调用方可访问的活动模型。 消费者必须在模型所属集群拥有推理权限,并获得直接或经用户组授予的模型授权; 非 self-scoped 操作者保留其绑定 scope 内的推理权限。部署分享或所有权不授予消费者推理权限。 /v1/models 和 Gateway 鉴权使用同一规则。条目包含从 clusterId 解析出的 clusterName, 用于区分不同集群中相同的公开名称。

GET /api/v1/model-service/models

请求:无请求体。调用方必须已通过 Lens 用户会话进行身份验证。

响应:返回一个 items 数组,其中每个元素都是附加了 clusterName 的 ModelServiceGroup 载荷。

{
"items": [
{
"id": "msg-4a8b41f3dd6c",
"name": "qwen3-8b",
"modelRef": "Qwen/Qwen3-8B",
"displayName": "Qwen3 8B",
"description": "Shared Qwen 3 8B service",
"selectionPolicy": "random",
"status": "active",
"clusterId": "cluster-a",
"clusterName": "Cluster A",
"servedName": "Qwen/Qwen3-8B",
"baseModel": "Qwen/Qwen3-8B",
"createdByUserId": "user-123",
"createdAt": "2026-09-20T15:00:00Z",
"updatedAt": "2026-09-22T11:30:00Z"
}
]
}

获取网关连接信息​

返回客户端用于配置其 OpenAI 客户端的 Gateway 连接信息。每个集群都运行自己的 Gateway,因此响应会为调用方可访问的每个集群分别列出一条连接;当设置了 LENS_MODEL_GATEWAY_PUBLIC_URL 时,它会作为单一覆盖值应用到每个集群。

GET /api/v1/model-service/connection

请求:无请求体。

响应:全局覆盖值(未设置时为空字符串),外加一个按集群分组的列表。

{
"baseUrl": "",
"clusters": [
{
"clusterId": "cluster-a",
"clusterName": "Cluster A",
"provider": "istio",
"gatewayName": "lens-inference-gateway",
"address": "10.0.0.10",
"publicUrl": null,
"port": null,
"baseUrl": "http://10.0.0.10/v1",
"ready": true
}
]
}

列出兼容 OpenAI 的模型​

针对一个有效的模型令牌返回 OpenAI 风格的 GET /v1/models 响应。模型列表会被过滤为该令牌所属用户可见的那些组。

GET /v1/models

请求:无请求体。使用 Authorization: ****** 或 x-api-key: <token> 传递模型令牌。

响应:标准的兼容 OpenAI 的模型列表载荷。

{
"object": "list",
"data": [
{
"id": "qwen3-8b",
"object": "model",
"created": 1727107200,
"owned_by": "lens"
}
]
}

列出使用记录​

列出调用方最近的使用记录,通过 limit 和 offset 进行简单的分页。每个条目都是存储的 UsageRecord,并经过驼峰命名的 API 别名投影。llm-d EPP 仅上报输入/输出 token,因此 cachedInputTokens 目前始终为 0(保留该字段是为了兼容会暴露该数据的引擎)。

GET /api/v1/model-service/usage/records

请求:无请求体。

  • limit(integer,查询参数,默认值 100,最大值 500):要返回的记录数量。
  • offset(integer,查询参数,默认值 0):调用方流水账中从 0 开始计数的偏移量。

响应:一个 items 数组,包含若干条使用记录。

{
"items": [
{
"id": "usg-8c2d9e1fa410",
"requestId": "req-20260923-0001",
"userId": "user-123",
"tokenId": "mat-2c47de48a35b",
"groupId": "msg-4a8b41f3dd6c",
"groupName": "qwen3-8b",
"clusterId": "cluster-a",
"executionId": "exec-qwen3-8b",
"modelRef": "Qwen/Qwen3-8B",
"provider": "vllm",
"inputTokens": 512,
"cachedInputTokens": 128,
"cacheWriteTokens": 0,
"outputTokens": 196,
"usageSource": "engine",
"status": "success",
"streaming": false,
"ttftMs": 240,
"durationMs": 1310,
"createdAt": "2026-09-23T08:40:00Z"
}
]
}

获取每日使用总量​

将调用方最近的使用记录按天聚合为 token 总量。该处理函数复用同一份使用流水账,并通过 _daily_totals 对其进行分桶统计。

GET /api/v1/model-service/usage/timeseries

请求:无请求体。

  • days(integer,查询参数,默认值 30,取值范围 1..365):要包含的最近天数。

响应:每日使用量分桶,其中包含每个 token 类别的总量。

{
"items": [
{
"date": "2026-09-22",
"requests": 4,
"inputTokens": 1200,
"cachedInputTokens": 300,
"cacheWriteTokens": 0,
"outputTokens": 640
},
{
"date": "2026-09-23",
"requests": 2,
"inputTokens": 530,
"cachedInputTokens": 64,
"cacheWriteTokens": 0,
"outputTokens": 211
}
]
}

列出模型路由组​

列出调用方具备集群级读取权限的模型组。每个组是路由到部署 InferencePool 的公开模型名称。 GET /api/v1/model-service/admin/groups/{group_id} 也要求相应 scope 内的 model-service:group:read。模型响应还包含可空的 ownerUserId、ownerGroupId; 所有权本身不授予消费者推理权限。

模型推理授权​

所有访问管理端点都要求调用方在模型所属集群拥有 model-service:group:share。 admin 和 maintainer 具备该权限,但它不包含模型本身或 Gateway 的管理权限。 授权覆盖该模型的所有提供方,不包含管理 API。重命名保留授权,删除模型清理授权; 即使模型为空,只要还有授权,也不能迁移到其他集群。

方法/api/v1/model-service/admin/groups/{group_id} 下的路径响应
GET/access{"items":[...]},包含授权 ID、subjectType、subjectId、subjectName、permission、createdAt
GET/access/options{"users":[{"id":"user-123","username":"alice"}],"groups":[{"id":"group-123","name":"team"}]}
POST/accessHTTP 201,包含授权 ID、对象和推理权限
DELETE/access/{binding_id}HTTP 204,空响应体

POST 请求体:

{"subjectType":"user","subjectId":"user-123"}

响应示例:

{"id":"binding-123","subjectType":"user","subjectId":"user-123","permission":"model-service:inference:use"}

向用户组授权时使用 subjectType: "group"。不接受角色、集群或所有者字段; 服务器固定授权角色,并从模型记录确定集群。对象必须已在该集群拥有推理权限。 options 只返回符合条件对象的 ID 和显示名称,不暴露完整身份记录;禁用或受保护用户不能选择。

scope 不足返回 403;资源不存在或撤权绑定不匹配返回 404;重复授权返回 409; 无效或多余字段返回 422。撤权影响使用已有 API key 的后续请求,不中断已开始的流, 也不修改 HTTPRoute 权重。

模型路由组列表示例​

GET /api/v1/model-service/admin/groups

请求:无请求体。

响应:返回一个 items 数组,其中每个元素都是 ModelServiceGroup 载荷。

{
"items": [
{
"id": "msg-4a8b41f3dd6c",
"name": "qwen3-8b",
"modelRef": "Qwen/Qwen3-8B",
"clusterId": "cluster-a",
"servedName": "Qwen/Qwen3-8B",
"baseModel": "Qwen/Qwen3-8B",
"displayName": "Qwen3 8B",
"description": "Shared Qwen 3 8B service",
"selectionPolicy": "random",
"status": "active",
"createdByUserId": "user-123",
"createdAt": "2026-09-20T15:00:00Z",
"updatedAt": "2026-09-22T11:30:00Z"
}
]
}

创建模型路由组​

创建集群内公开模型名称对应的路由组。name、clusterId 必填,名称在集群内唯一, 所有提供方必须位于该集群。提供 executionId 时,组与初始成员在同一事务中创建, 供 Add model 使用。部署必须就绪且有模型名称;服务端从部署推导 modelRef、 servedName,将 baseModel 设为公开名称,忽略调用方对这三个字段的覆盖。 不传 executionId 时仍支持单独创建组。

POST /api/v1/model-service/admin/groups

请求体(GroupCreateRequest):

  • name(string):客户端在 Chat Completions API 中使用的公开模型名称。
  • clusterId(string):该服务所属的集群(必填);其提供方必须都位于该集群内,且名称在该集群内唯一。
  • displayName(string,可选):便于在 UI 中展示的标签;默认为 name。
  • description(string,可选):自由格式的描述。
  • executionId(string,可选):作为初始成员发布的就绪部署;需要该集群中的 model-service:group:manage 和 model-service:member:manage 权限。
  • modelRef / servedName / baseModel(string,可选,高级选项):传入 executionId 时由服务端推导;否则 modelRef 默认为 name,baseModel 默认为 modelRef,servedName 为 null。
  • selectionPolicy(string,遗留字段):为兼容性而接受此参数;llm-d 数据面会忽略它。
{
"name": "qwen3-8b",
"clusterId": "cluster-a",
"executionId": "exec-123",
"displayName": "Qwen3 8B",
"description": "Shared Qwen 3 8B service"
}

响应:201 和创建后的 ModelServiceGroup。传入 executionId 时成员同时提交, 并在后台安排 Gateway 调谐。权限或范围不足返回 403,目标不存在返回 404, 名称重复、集群不匹配、部署未就绪或无模型名称返回 409,输入无效返回 422。 事务失败不会留下任一记录。

{
"id": "msg-4a8b41f3dd6c",
"name": "qwen3-8b",
"modelRef": "Qwen/Qwen3-8B",
"clusterId": "cluster-a",
"servedName": "Qwen/Qwen3-8B",
"baseModel": "qwen3-8b",
"displayName": "Qwen3 8B",
"description": "Shared Qwen 3 8B service",
"selectionPolicy": "random",
"status": "active",
"createdByUserId": "user-123",
"createdAt": "2026-09-23T09:10:00Z",
"updatedAt": "2026-09-23T09:10:00Z"
}

将就绪部署发布为公开模型服务​

把单个就绪部署发布为新的集群级公开模型名称,无需授予完整的模型组管理权限。公开名称尚不存在时,Add model 会使用此接口。调用者需要部署所在集群的 model-service:member:manage 权限。服务使用默认的 random 选择策略;编辑模型服务元数据或选择策略仍需要 model-service:group:manage。

POST /api/v1/model-service/admin/groups/publish

请求体(GroupPublishRequest):

  • name(string):公开模型名称。
  • clusterId(string):部署所在集群。
  • executionId(string):要发布的就绪部署。
  • displayName / description(string,可选)。

响应:201 和创建后的 ModelServiceGroup;部署会作为首个成员提交,并安排 Gateway 调谐。错误码与创建模型组一致(403、404、409、422)。

{
"name": "qwen3-8b",
"clusterId": "cluster-a",
"executionId": "exec-123"
}

列出已发布的模型服务成员​

列出为路由组提供后端支持的已发布成员。该路由可以选择按某个组 ID 进行过滤,并添加一个根据 execution_name() 推导出来的、便于阅读的部署 name 字段。

GET /api/v1/model-service/admin/members

请求:无请求体。

  • group_id(string,查询参数,可选):仅返回某个路由组的成员。

响应:items 成员载荷数组,包含整数 weight,范围为 0 到 1000000。

{
"items": [
{
"id": "msm-32d55cb7f9a1",
"groupId": "msg-4a8b41f3dd6c",
"executionId": "exec-qwen3-8b",
"clusterId": "cluster-a",
"targetNamespace": "ns",
"targetService": "vllm",
"targetPort": 8000,
"endpointKind": "vllm",
"weight": 1,
"status": "active",
"publishedByUserId": "user-123",
"createdAt": "2026-09-23T09:12:00Z",
"updatedAt": "2026-09-23T09:12:00Z",
"name": "qwen-qwen3-8b-202609210558"
}
]
}

将一个部署发布为成员​

将一个就绪的部署执行发布到某个路由组中。该路由会根据执行 ID 推导出实际的命名空间、服务、端口和端点类型。该执行所在的集群必须与该组所在的集群一致,否则调用会被以 409(model_member_conflict)拒绝。

每个新提供方默认权重为 1,已有权重保持不变。通过流量分配接口修改权重。 HTTP 处理函数会安排调谐,直接 Python 调用只持久化记录。

POST /api/v1/model-service/admin/members

请求体(MemberCreateRequest):

  • groupId(string):目标路由组 ID。
  • executionId(string):就绪的部署执行 ID。
  • poolName(string,可选,高级选项):由部署所拥有的 InferencePool 名称;默认为部署保存的池名称,形式为 namespace/pool(跨命名空间唯一)。
{
"groupId": "msg-4a8b41f3dd6c",
"executionId": "exec-qwen3-8b",
"poolName": "ns/qwen"
}

响应:创建成功后的 ModelServiceMember。

{
"id": "msm-32d55cb7f9a1",
"groupId": "msg-4a8b41f3dd6c",
"executionId": "exec-qwen3-8b",
"clusterId": "cluster-a",
"targetNamespace": "ns",
"targetService": "qwen-epp",
"targetPort": 8000,
"endpointKind": "llm-d-epp",
"weight": 1,
"poolName": "qwen",
"eppRef": "qwen-epp",
"status": "active",
"ownerUserId": "user-123",
"publishedByUserId": "user-123",
"createdAt": "2026-09-23T09:12:00Z",
"updatedAt": "2026-09-23T09:12:00Z"
}

保存并应用流量分配​

原子更新全部权重,并可添加部署提供方,然后调谐集群的共享 Gateway 和加权 HTTPRoute。 需要模型组所在集群中的 model-service:member:manage 权限。

PUT /api/v1/model-service/admin/groups/{group_id}/traffic

请求体(TrafficUpdateRequest):

  • members:完整的 1 到 16 个成员,每项包含 executionId 和 0..1000000 范围内的整数 weight。不能遗漏已有成员;移除成员请使用删除接口。
  • expectedWeights:之前读取的执行 ID 到权重的映射,包含所有已有提供方,不含新提供方。
{
"members": [
{"executionId": "stable", "weight": 90},
{"executionId": "canary", "weight": 10}
],
"expectedWeights": {"stable": 1}
}

新部署及从零权重激活的提供方必须就绪、位于同一集群、使用不同的 InferencePool/EPP,且提供方模型名称与组一致。至少一个未禁用提供方的权重为正。 零权重成员保留但不写入 backendRefs,离线成员也不接收流量。

响应:200,包含完整更新后的 items(包括 weight)和 operation (含 status、message、detailJson 的 GatewayOperation)。 操作失败不会回滚已保存权重。 succeeded 只表示已提交配置,不代表已测得流量比例。

格式或范围错误返回 422,快照过期或分配无效返回 409 model_traffic_conflict, 目标不存在返回 404,权限不足返回 403。保存后的传输或领域异常返回 502 model_traffic_apply_failed,明确说明配置已保存;重新读取分配,修复后重试 Reconcile。

render_model_route(..., pools=[PoolBinding(...)]) 生成精确的相对权重、一个 IPP 模型映射及跨命名空间 ReferenceGrant。保留原有 pool=PoolBinding(...) 单后端调用, 但不能同时传入两种形式。PoolBinding.weight 默认为 1。

获取使用量分析数据​

为管理员仪表盘返回聚合后的模型服务使用量分析数据。该载荷包括总体合计、时间序列、分组明细以及可用的筛选维度选项。

GET /api/v1/model-service/admin/usage/analytics

请求:无请求体。

  • since(string,查询参数,可选):ISO 时间戳,作为下限。
  • until(string,查询参数,可选):ISO 时间戳,作为上限。
  • interval(string,查询参数,默认值 day):day 或 hour。
  • group_by(string,查询参数,默认值 model):model、user 或 cluster。
  • user_id(string[],查询参数,可选):可重复传入的用户 ID。
  • group_id(string[],查询参数,可选):可重复传入的组 ID。
  • cluster_id(string[],查询参数,可选):可重复传入的集群 ID。

响应:由 build_usage_analytics() 构建的分析数据。

{
"interval": "day",
"groupBy": "model",
"totals": {
"requests": 3,
"inputTokens": 17,
"cachedInputTokens": 2,
"outputTokens": 6,
"tokens": 25
},
"series": [
{
"bucket": "2026-09-21",
"requests": 2,
"inputTokens": 13,
"cachedInputTokens": 2,
"outputTokens": 6,
"tokens": 21
},
{
"bucket": "2026-09-22",
"requests": 1,
"inputTokens": 4,
"cachedInputTokens": 0,
"outputTokens": 0,
"tokens": 4
}
],
"groups": [
{
"key": "m1",
"label": "m1",
"totals": {
"requests": 2,
"inputTokens": 13,
"cachedInputTokens": 2,
"outputTokens": 6,
"tokens": 21
},
"series": [
{
"bucket": "2026-09-21",
"requests": 2,
"inputTokens": 13,
"cachedInputTokens": 2,
"outputTokens": 6,
"tokens": 21
}
]
}
],
"facets": {
"models": [
{
"key": "g-m1",
"label": "m1"
}
],
"users": [
{
"key": "u1",
"label": "u1"
}
],
"clusters": [
{
"key": "c1",
"label": "c1"
}
]
}
}

跨集群列出使用记录​

为管理员仪表盘列出跨用户/模型/集群的最近使用记录,可以选择按路由组或集群进行过滤,并以 limit 作为上限。集群可见性仍会被限制为调用方可访问的集群范围。

GET /api/v1/model-service/admin/usage

请求:无请求体。

  • group_id(string[],查询参数,可选):可重复传入的路由组 ID。
  • cluster_id(string[],查询参数,可选):可重复传入的集群 ID。
  • limit(integer,查询参数,默认值 100,最大值 500):要返回的记录数量。

响应:一个 items 数组,包含若干条使用记录(结构与 /usage/records 相同)。

{
"items": [
{
"id": "usg-8c2d9e1fa410",
"requestId": "req-20260923-0001",
"userId": "user-123",
"tokenId": "mat-2c47de48a35b",
"groupId": "msg-4a8b41f3dd6c",
"groupName": "qwen3-8b",
"clusterId": "cluster-a",
"executionId": "exec-qwen3-8b",
"modelRef": "Qwen/Qwen3-8B",
"provider": "vllm",
"inputTokens": 512,
"cachedInputTokens": 0,
"cacheWriteTokens": 0,
"outputTokens": 196,
"usageSource": "engine",
"status": "success",
"streaming": false,
"ttftMs": 240,
"durationMs": 1310,
"createdAt": "2026-09-23T08:40:00Z"
}
]
}

获取网关状态​

获取跨集群的当前网关状态:已发布的成员、固定使用的提供方,以及共享 Gateway 的已编程状态。

gatewayNamespace 和 ippNamespace 返回带集群注册稳定短 ID 的实际命名空间。

GET /api/v1/model-service/admin/gateway/status

请求:无请求体。

响应:序列化后的 GatewayStatus 载荷。

{
"clusters": [
{
"clusterId": "1234abcd",
"clusterName": "Primary cluster",
"members": [
{
"groupId": "msg-4a8b41f3dd6c",
"groupName": "qwen3-8b",
"executionId": "exec-qwen3-8b",
"endpointKind": "llm-d-epp",
"poolName": "qwen",
"target": "qwen-epp.ns:8000",
"status": "active"
}
],
"gatewayProvider": "istio",
"gatewayState": "installed",
"gatewayNamespace": "lens-gateway-1234abcd",
"gatewayName": "lens-inference-gateway",
"gatewayReady": true,
"gatewayRunning": true,
"ippState": "installed",
"ippReady": false,
"ippName": "lens-ipp",
"ippNamespace": "lens-gateway-1234abcd",
"lastOperation": {
"id": "gop-2a7f0d9c0c11",
"kind": "reconcile-gateway",
"status": "succeeded",
"clusterId": "1234abcd",
"message": "gateway reconciled (istio); 1 model route(s)",
"createdAt": "2026-09-23T09:15:00Z",
"finishedAt": "2026-09-23T09:15:18Z"
}
}
]
}

协调单个网关集群并查看日志​

重新渲染并应用单个集群的共享 Gateway、HTTPRoute 和提供方连接,并以 SSE 流返回操作日志。

POST /api/v1/model-service/admin/gateway/reconcile/stream

请求体:{ "clusterId": "cluster-a" }。需要 model-service:gateway:reconcile。

调谐网关所在集群​

为当前拥有活跃成员的每个集群安排一次完整的数据面调谐。该路由会先发现受影响的集群 ID,然后在后台运行 _reconcile_clusters()。

POST /api/v1/model-service/admin/gateway/reconcile

请求:无请求体。

响应:确认调谐已被安排执行,并列出目标集群。

{
"scheduled": true,
"clusters": [
"cluster-a",
"cluster-b"
]
}

安装或复用共享 Gateway​

安装(或复用)该集群的共享 llm-d Gateway。提供方可由用户选择(istio、gke、agentgateway、envoy-ai-gateway);若省略,则使用集群记录中固定的提供方。

默认命名空间为 lens-gateway-<cluster-id>,自定义前缀也附加同一个稳定短 ID, Gateway 与 IPP 操作始终使用一致的命名空间。Istio 使用仅作用于该 Gateway Pod 的 命名空间级 EnvoyFilter,HTTP 外部鉴权失败时拒绝访问,不注册全网格共享的鉴权提供方。 不接管或删除旧无后缀资源;旧 Gateway 占用 NodePort 时仍视为冲突, 需要选择空闲端口或主动退役旧 Gateway。操作流中的卸载只删除本注册带标签的 命名空间内资源,不删除其他命名空间、共享 GatewayClass 或部署所属的 InferencePool。

POST /api/v1/model-service/admin/gateway/install

请求体(ClusterGatewayRequest):

  • clusterId(string):目标集群 ID。
  • provider(string,可选):istio | gke | agentgateway | envoy-ai-gateway。
{ "clusterId": "1234abcd", "provider": "istio" }

响应:记录此次安装所产生的 GatewayOperation。

{
"id": "gop-9c1a2b3d4e5f",
"kind": "install-gateway",
"status": "succeeded",
"clusterId": "1234abcd",
"message": "shared Gateway ready (istio)",
"detailJson": { "provider": "istio", "namespace": "lens-gateway-1234abcd", "name": "lens-inference-gateway", "programmed": true },
"createdAt": "2026-09-24T16:20:00Z",
"finishedAt": "2026-09-24T16:20:12Z"
}

获取一个模型路由组​

按 ID 获取某一个路由组。

GET /api/v1/model-service/admin/groups/{group_id}

请求:无请求体。

响应:ModelServiceGroup 载荷(结构与列表端点相同)。

{
"id": "msg-4a8b41f3dd6c",
"name": "qwen3-8b",
"modelRef": "Qwen/Qwen3-8B",
"clusterId": "cluster-a",
"servedName": "Qwen/Qwen3-8B",
"baseModel": "Qwen/Qwen3-8B",
"selectionPolicy": "random",
"status": "active"
}

更新一个模型路由组​

更新路由组的可变字段(名称、模型引用、显示名称、描述、状态、集群、服务名称、基础模型)。

HTTP 处理函数保存配置并等待受影响集群的调谐提交。status: "disabled" 停止公开模型并 移除路由,"active" 恢复在线且正权重提供方的路由,不改变提供方状态、权重或运行中的 Pod。 调用方需要所有受影响集群的模型组管理权限,控制器收敛是异步的。

PATCH /api/v1/model-service/admin/groups/{group_id}

请求体(GroupUpdateRequest;所有字段均为可选):name、modelRef、displayName、description、status(active | disabled)、clusterId、servedName、baseModel。

{ "displayName": "Qwen3 8B (prod)", "status": "active" }

响应:成功提交调谐后的 ModelServiceGroup。502 model_publication_apply_failed 表示配置已保存但路由应用失败,请用 Reconcile 重试。越权修改返回 403。

启用或停止模型服务​

在不改变提供方状态、权重或运行中 Pod 的情况下启动或停止公开模型。

POST /api/v1/model-service/admin/groups/{group_id}/status

请求体:{ "status": "active" } 或 { "status": "disabled" }。需要模型所在集群的 model-service:group:toggle。

删除一个模型路由组​

需要模型所在集群的组管理权限,只允许移除空路由组。只要仍有关联提供方(包括离线或 权重为 0 的提供方),就返回 409 model_group_conflict。处理函数与 Gateway 调谐串行, 仅删除该模型由 Lens 管理的 HTTPRoute 和 IPP 映射,确认资源已删除后才删除记录。 模型不存在返回 404;清理失败返回 502 model_removal_failed 并保留记录供重试。 不删除其他模型、共享 Gateway 资源、部署或 Pod。

DELETE /api/v1/model-service/admin/groups/{group_id}

需要 model-service:group:remove。请求:无请求体。响应:204 No Content。

启动或停止所有提供方​

需要模型所在集群的成员管理权限。原子修改所有提供方状态,然后对受影响集群各调谐一次。 保留权重、父模型状态和 Pod。启动时要求每个部署均在其注册集群中就绪。空模型、部署未就绪 或提供方并发变更返回 409 model_member_conflict,不会部分修改状态;模型不存在返回 404。 502 model_publication_apply_failed 表示状态已保存但路由应用失败,请重试 Reconcile。

PUT /api/v1/model-service/admin/groups/{group_id}/providers/status

{ "status": "disabled" }

"active" 启动所有提供方,"disabled" 停止所有提供方。 成功返回 200,{"items": [...]} 中包含更新后的成员对象。

列出可发布的部署​

列出可以作为成员发布的就绪部署(执行),其推导出的目标地址已经填好。

GET /api/v1/model-service/admin/deployments

请求:无请求体。

响应:返回一个 items 数组,其中每个元素都是 PublishableDeployment 载荷。

{
"items": [
{
"executionId": "exec-qwen3-8b",
"name": "qwen-qwen3-8b-202609210558",
"displayName": "Qwen3 8B",
"model": "Qwen/Qwen3-8B",
"clusterId": "cluster-a",
"clusterName": "Cluster A",
"namespace": "ns",
"service": "qwen-epp",
"port": 8000,
"endpointKind": "llm-d-epp",
"modelRef": "Qwen/Qwen3-8B",
"status": "ready"
}
]
}

更新一个成员​

更新成员状态并等待集群路由调谐。disabled 将提供方离线,但不改变保存的权重或缩容 Pod。 重新上线要求该注册集群中的部署已就绪。权重 0 仍为 standby,已停止的父模型不会恢复。 允许全部提供方离线;没有在线正权重提供方时移除 HTTPRoute,否则引用所有在线正权重池。 健康探测不能撤销显式离线选择。

PATCH /api/v1/model-service/admin/members/{member_id}

请求体(MemberUpdateRequest;所有字段均为可选):status(active | disabled | unhealthy)。

{ "status": "disabled" }

响应:成功提交调谐后的 ModelServiceMember。未就绪提供方上线返回 409 model_member_conflict,缺少集群成员管理权限返回 403。 502 model_publication_apply_failed 表示状态已保存但路由应用失败,请用 Reconcile 重试。

移除一个成员​

移除一个已发布的成员。该处理函数会安排对其所在集群的调谐。

DELETE /api/v1/model-service/admin/members/{member_id}

请求:无请求体。响应:204 No Content。

探测成员后端​

探测启用模型中未禁用的成员,刷新 health_json / last_health_at, 并在 active 与 unhealthy 之间更新状态。跳过已停止模型和离线提供方, 并发健康写入不能重新启用离线提供方。没有在线正权重提供方时,HTTPRoute 缺失属于预期, 跳过 Gateway 服务检查,也不触发自动修复。

已有的零权重 InferencePool 没有 Gateway 父状态时报告 healthJson.components.inferencePool: "standby",不会仅因此将健康提供方判为异常或 触发 Gateway/IPP 修复。资源缺失、显式池拒绝、EPP 或模型服务器端点故障仍判为异常; 正权重池仍要求 Gateway 接受。

POST /api/v1/model-service/admin/gateway/probe

请求:无请求体。

响应:

{ "updated": 2 }

同步网关使用量​

从 Prometheus 读取每个集群的 llm-d EPP token 计数器,并将自上次同步以来的增量写入使用量流水账,归属到请求的 fairness_id 背后的令牌/用户(Gateway 会根据 ext_authz 的决策注入 x-llm-d-inference-fairness-id)。该操作是幂等的:计数器未变化则不会新增任何记录。

POST /api/v1/model-service/admin/gateway/usage/sync

请求:无请求体。

响应:每个集群写入的记录数。

{ "written": { "cluster-a": 3 } }

读取推理载荷处理器配置​

从集群的 ConfigMap 中读取该集群实时生效的推理载荷处理器(Inference Payload Processor)配置(Lens 本身并不存储此配置)。

GET /api/v1/model-service/admin/gateway/ipp-config?clusterId=cluster-a

响应:

{ "config": "apiVersion: llm-d.ai/v1alpha1\nkind: PayloadProcessorConfig\nplugins: []\n", "custom": false, "default": "plugins: []\n" }

保存推理载荷处理器配置​

将一份 PayloadProcessorConfig 请求体应用到集群,并升级 IPP(不涉及 Lens 存储)。空的 config 会恢复为 Helm chart 的默认配置。

POST /api/v1/model-service/admin/gateway/ipp-config

请求体(IppConfigRequest):

  • clusterId(string):目标集群 ID。
  • config(string):PayloadProcessorConfig 的内容(以 plugins: 开头;chart 会添加 apiVersion/kind)。
{ "clusterId": "cluster-a", "config": "plugins:\n- type: body-field-to-header\n" }

响应:记录此次 IPP 安装所产生的 GatewayOperation。

对集群进行预检​

在安装或调谐数据面之前,检查集群是否可达,以及是否存在活跃成员。

POST /api/v1/model-service/admin/gateway/preflight

请求体(ClusterGatewayRequest):

  • clusterId(string):目标集群 ID。
{ "clusterId": "cluster-a" }

响应:记录此次预检所产生的 GatewayOperation。

对数据面组件进行扩缩容​

将某个数据面 Deployment(Gateway 数据面、EPP 或模型服务器)扩缩容到指定的副本数。

POST /api/v1/model-service/admin/gateway/scale

请求体(ComponentScaleRequest):

  • clusterId(string)
  • namespace(string)
  • name(string)
  • replicas(integer,0–100)
  • kind(string,可选,默认值 deployment)
{ "clusterId": "cluster-a", "namespace": "ns", "name": "qwen-epp", "replicas": 2 }

响应:记录后的 GatewayOperation(detailJson 中携带 namespace、name、replicas)。

以流式方式运行一次数据面操作​

运行一次数据面操作,并以 Server-Sent Events 的形式流式输出其日志行。管理员界面在安装/卸载/调谐时使用该端点,以便日志抽屉能够实时更新。

POST /api/v1/model-service/admin/gateway/stream

请求体(GatewayStreamRequest):

  • clusterId(string)
  • op(string):install-gateway | uninstall-gateway | reconcile | scale | install-ipp | uninstall-ipp
  • provider、namespace、name、replicas、kind、gatewayName、ippChart、ippVersion(可选,依据 op 而定)
{ "clusterId": "cluster-a", "op": "reconcile" }

响应:text/event-stream;每个事件携带一行日志,最后是作为终态的 GatewayOperation 载荷。

流式输出某个组件的日志​

流式输出(kubectl logs -f)某个数据面组件的实时日志。管理员数据面拓扑图使用该端点,以便实时查看 Gateway / IPP / EPP 的输出。

POST /api/v1/model-service/admin/gateway/logs

请求体(ComponentLogsRequest):

  • clusterId(string):目标集群。
  • component(string,可选):gateway 或 ipp —— 后端会据此解析出命名空间/名称。
  • namespace / name(string,可选):当未提供 component 时,显式指定坐标。
  • kind(string,默认值 deployment)、container(string,可选)、tail(integer,默认值 200)。
{ "clusterId": "cluster-a", "component": "gateway" }

响应:text/event-stream;每个 log 事件携带一行日志,最后是一个作为终态的 complete。

内部端点(Edge ext_authz)​

这些 /api/v1/internal/model-gateway/* 路由只会被 Edge Envoy 的 ext_authz 过滤器和 llm-d EPP 调用,永远不会被 Lens 客户端直接调用;它们不携带任何 Lens 用户会话。

对网关请求进行鉴权​

校验调用方的模型令牌,将请求的公开模型名称解析为该令牌可使用的一个活跃路由组,选择一个后端成员,并返回供 Edge Envoy 附加到上游请求的路由请求头。GET /v1/models(不带模型)只会做令牌校验;其他所有 HTTP 方法都会被接受,因为 Envoy 会转发客户端原始的请求方法。

POST /api/v1/internal/model-gateway/authorize(同时支持 GET/PUT/PATCH/DELETE,以及用于适配 Envoy 按集群划分路径形式的 /authorize/{rest:path},例如 .../authorize/cluster/<id>/...)

请求:一个 JSON 请求体 {"token": "...", "model": "..."},或者原始的 OpenAI 请求体,外加 Authorization/x-api-key 以及 x-lens-model/x-gateway-base-model-name 请求头。

响应:成功时为 200,并附带 AuthorizeResult 载荷,以及供 Edge 转发到上游的 x-lens-* / x-ai-eg-model / x-llm-d-inference-fairness-id 路由请求头;失败时为 401(token_invalid)或 404(model_not_found)。

记录推理使用量​

将一次已完成推理的 token 使用情况记录到流水账中。该端点由 llm-d EPP/使用量同步流程调用;按 requestId 做幂等处理。

POST /api/v1/internal/model-gateway/record-usage

请求体(UsageRecordRequest):字段与 UsageRecord(参见“列出使用记录”)相同,以 requestId 作为键。

响应:

{ "recorded": true, "id": "usg-8c2d9e1fa410" }

列出某个指定用户可调用的模型​

返回给定用户 ID 可调用的模型名称,用于为该用户提供 /v1/models 服务。

GET /api/v1/internal/model-gateway/models?user_id=user-123

响应:一个 items 数组,其中每个元素都是 ModelServiceGroup 载荷(结构与“列出可访问的模型”相同)。