argus-cluster/specs/mvp/v3.8/v3.8_per_model_app_dev_plan.md

6.5 KiB
Raw Blame History

MVP v3.8变更开发计划Per-Model Serve AppTDD

目标:按 specs/mvp/v3.8/v3.8_per_model_app.md 将 v3.8 从“单 app 多模型(全量重建)”改为“每个模型一个 Ray Serve app + 独立 route_prefix”,实现增删/缩放某个模型不触发其它模型重启与重调度。

约束与结论

  • 推理端口固定:8000
  • 推理 endpoint 不做鉴权
  • model_id 前缀规则:<user_id>-<YYYYMMDDHHMM>-<suffix>
  • LLMConfig.accelerator_typeconfigs/dev.yaml 决定dev/h1: H20
  • 路由方案(本迭代固定):
    • app_name = model_key
    • route_prefix = /serve/<model_key>
    • openai_base_url = http://<host>:8000/serve/<model_key>/v1

非目标(明确不做)

  • 不提供统一 /v1 的“跨模型聚合路由”(如要,需要额外 router 层;可在后续迭代做)
  • 不改 ServingSpec 语义(输入仍为 model_id/model_source/num_replicas/gpus_per_replica/engine_kwargs

M0 - 基线回归与分支保护

目的:确保切换架构前训练/现有功能不回退。

测试

  • 本地:.venv/bin/python -m pytest 全绿coverage ≥ 90%

验收

  • 基线可用;进入 M1

M1 - API 输出与 endpoint 语义调整(单测驱动)

目的API/DB/前端都统一 per-model 的 openai_base_url 语义;避免 UI/脚本继续使用 /v1 根路径。

变更点

  • GET /api/v2/serve/models
    • 保持返回 items[],但每个 item 的 endpoint.openai_base_url 必须是 per-model base url
    • openai_base_url(列表层级字段)处理策略二选一:
      • A推荐移除该字段breaking需同步 UI/脚本)
      • B兼容保留但改为 null 或提示字符串(不再保证可用)
  • GET /api/v2/serve/models/{model_key}
    • model.endpoint.openai_base_url 改为 per-model base url

单测(先写)

  • 更新/新增 src/mvp/py/tests/test_app_serving_api.py
    • 断言 endpoint.openai_base_url 包含 /serve/<model_key>/v1
    • 断言多条 models 的 base_url 不相同(随 model_key 变化)

实现

  • 更新 src/mvp/py/argus/service/app.py
    • _serve_model_public()endpoint.openai_base_url 拼接 per-model prefix
    • 如选择移除/调整 list 层的 openai_base_url,同步实现

验收

  • API 单测通过;返回结构可被 UI/脚本消费

M2 - ServeClient 扩展delete_app+ Reconciler 改造成 per-model单测驱动

目的:核心行为变更:每次 tick 只部署/删除一个模型对应的 app不重建全量 app。

变更点(行为)

  • QUEUED
    • 对该 model_key 执行 serve.run(app, name=model_key, route_prefix=/serve/<model_key>)
    • 状态:DEPLOYING → RUNNING
  • DELETING
    • 对该 model_key 执行 serve.delete(model_key)
    • 状态:DELETED
  • 资源预检查从“全量 needed_total_gpus”改为“本次变更模型所需 GPU”

单测(先写)

  • 更新 src/mvp/py/tests/test_serving_reconciler.py
    • create Areconciler 只 apply_app(app_name=A.model_key, route_prefix=/serve/A)
    • create Breconciler 只 apply_app(app_name=B.model_key, route_prefix=/serve/B)(不再对 A 触发 apply
    • delete Breconciler 只 delete_app(B.model_key)(不触发 A
    • GPU 不足时:保持 QUEUED 且 event=SERVE_PENDING_RESOURCES

实现

  • src/mvp/py/argus/service/serve_client.py
    • 增加 delete_app(app_name: str)(封装 serve.delete
  • src/mvp/py/argus/service/serving_reconciler.py
    • 移除“全量 app apply”逻辑
    • 每个 model_key 独立部署:app_name=model_keyroute_prefix=/serve/<model_key>
    • 删除路径走 delete_app

验收

  • reconciler 单测全绿逻辑可解释events/state 正确)

M3 - WebUI Serving 页面适配 per-model base_url单测驱动

目的:用户从 UI 复制的示例命令必须可用;不再指向根 /v1

变更点

  • /ui/serving 列表:
    • “OpenAI /v1/models”按钮改为
      • A移除因为没有聚合 /v1/models
      • B保留但文案改为“OpenAI base 需进入详情页”
  • /ui/serving/{model_key} 详情页:
    • curl 示例使用 per-model openai_base_url
    • 增加一键打开:/serve/<model_key>/v1/models

单测(先写)

  • 更新/新增 src/mvp/py/tests/test_ui_serving.py
    • 断言页面包含 /serve/ 前缀
    • 断言详情页示例里包含 /serve/<model_key>/v1/chat/completions

实现

  • src/mvp/py/argus/service/ui.py 更新 Serving UI

验收

  • UI 单测全绿;页面内容与 API 语义一致

M4 - E2E 脚本更新v3.8 serving

目的:在 dev/h1 一键验证 per-model appA/B 增删不互相影响,且推理可用。

变更点

  • 更新 src/mvp/scripts/run_all_v38_serving.sh
    • /v1/modelschat/completions 均改用 per-model base_url/serve/<model_key>/v1
    • 增加“隔离验证”步骤:
      • 部署 A → 记录 A 的 serve replica actor_id/node_id
      • 部署 B → 再次记录 A 的 actor_id/node_id必须一致
      • 删除 B → 再次记录 A 的 actor_id/node_id必须一致
    • 最后删除 A

验收

  • E2E 脚本能跑通且输出明确断言(一致/不一致)

M5 - h1 端到端验证与回归

目的:确认实际 Ray Serve 行为满足“其它模型不滚动更新”的核心目标。

操作

  • 同步代码到:argus@h1:/home2/argus/infra/mvp/src/mvp
  • 重启 APIscripts/61_stop_api.sh && scripts/60_start_api.sh
  • 执行:MVP_INTERNAL_TOKEN=... scripts/run_all_v38_serving.sh

验收标准(必须满足)

  • 新增/删除 B 时A 的 LLMServer replica actor_id/node_id 不变
  • A/B 的 OpenAI endpoint 均可完成 chat/completions
  • 删除 B 后 A 仍可推理

M6 - 文档与迁移说明

目的:明确“路由语义变化”和“如何使用”。

  • 更新 src/mvp/README.md
    • 新增 per-model base_url 说明(/serve/<model_key>/v1
    • 提示不再提供聚合 /v1/models
  • 更新 specs/mvp/v3.8/v3.8_progress.md
    • 记录 per-model app 变更与验收结论

风险与缓解

  • 风险:旧 argus_llm_app 残留
    • 缓解:在 E2E/迁移步骤里增加一次 best-effort serve.delete("argus_llm_app")(可选)
  • 风险:用户仍按旧方式访问 /v1
    • 缓解UI/文档/脚本统一切换到 per-model base_url并在列表页给出明显提示