name: Model E2E acceptance (PR / PyPI) # Separate from release workflows: this workflow cannot upload packages. # Only maintainers dispatch reviewed SHAs; never execute pull_request_target code. on: workflow_dispatch: inputs: mode: description: '验收对象:pr 为候选 wheels,pypi 为公开安装' type: choice options: [pypi, pr] default: pypi version: description: 'PyPI 的 ktransformers 版本;latest 按用户默认安装' type: string default: latest pr_repository: description: 'PR 所属四仓之一' type: choice options: [kvcache-ai/ktransformers, kvcache-ai/sglang, kvcache-ai/transformers, kvcache-ai/accelerate] default: kvcache-ai/ktransformers pr_number: description: 'PR 编号(pr 模式必填)' type: string default: '' pr_sha: description: '已审阅批准的完整 PR head SHA(pr 模式必填)' type: string default: '' build_run_id: description: '可信 main 构建流程的 run ID,包含 model-e2e-candidate artifact' type: string default: '' workflow_call: inputs: mode: type: string default: pypi version: type: string default: latest pr_repository: type: string default: kvcache-ai/ktransformers pr_number: type: string default: '' pr_sha: type: string default: '' build_run_id: type: string default: '' permissions: contents: read # Deliberately no shared Actions concurrency group: GitHub may replace an # existing pending run. The runner queue + host-wide flock preserve waiting jobs. jobs: prepare: if: github.repository == 'kvcache-ai/ktransformers' && github.ref == 'refs/heads/main' runs-on: ubuntu-24.04 timeout-minutes: 20 permissions: contents: read actions: read steps: - uses: actions/checkout@v4 with: persist-credentials: false - uses: actions/setup-python@v5 with: python-version: '3.12' - name: Freeze the approved test identity (no PR code execution) env: GH_TOKEN: ${{ github.token }} ENABLED: ${{ vars.KT_MODEL_E2E_ENABLED }} MODE: ${{ inputs.mode }} VERSION: ${{ inputs.version }} PR_REPOSITORY: ${{ inputs.pr_repository }} PR_NUMBER: ${{ inputs.pr_number }} PR_SHA: ${{ inputs.pr_sha }} BUILD_RUN_ID: ${{ inputs.build_run_id }} BUILD_WORKFLOW: ${{ vars.KT_MODEL_E2E_BUILD_WORKFLOW }} run: | python .github/model-e2e/prepare.py mkdir input mv request.json input/request.json - name: Download final candidates from the approved build if: inputs.mode == 'pr' uses: actions/download-artifact@v4 with: name: model-e2e-candidate path: input/candidate run-id: ${{ inputs.build_run_id }} github-token: ${{ github.token }} - name: Check wheel hashes and source lock if: inputs.mode == 'pr' run: | python - <<'PY' import json, sys sys.path.insert(0, '.github/model-e2e') from contracts import verify_candidate verify_candidate('input/candidate', json.load(open('input/request.json'))) PY - uses: actions/upload-artifact@v4 with: name: model-e2e-input-${{ github.run_id }}-${{ github.run_attempt }} path: input/ if-no-files-found: error retention-days: 7 gpu: needs: prepare # An administrator must provision the no-credentials runner account and # protected environment before assigning this new label. Existing release # runners must NOT receive the label without the documented isolation audit. environment: qj5090-model-e2e runs-on: [self-hosted, linux, x64, gpu, kt-model-e2e] timeout-minutes: 1100 permissions: contents: read actions: read steps: - uses: actions/checkout@v4 with: persist-credentials: false path: harness - uses: actions/download-artifact@v4 with: name: model-e2e-input-${{ github.run_id }}-${{ github.run_attempt }} path: input - name: Queue, install fresh packages, and run the three model tests env: HOST_CONFIG: ${{ vars.KT_MODEL_E2E_HOST_CONFIG || '/etc/ktransformers/model-e2e/qj5090.json' }} CONTROLLER_PYTHON: ${{ vars.KT_MODEL_E2E_PYTHON || 'python3.12' }} run: | set -euo pipefail test "$("$CONTROLLER_PYTHON" -c 'import sys; print(sys.version_info >= (3, 11))')" = True test_root="$(mktemp -d "$RUNNER_TEMP/kt-model-e2e.XXXXXXXX")" echo "E2E_OUTPUT=$test_root/output" >> "$GITHUB_ENV" "$CONTROLLER_PYTHON" harness/.github/model-e2e/host.py \ --config "$HOST_CONFIG" --input "$GITHUB_WORKSPACE/input" \ --output "$test_root/output" - name: Retain evidence only (not model weights, adapters, or the venv) if: always() && env.E2E_OUTPUT != '' uses: actions/upload-artifact@v4 with: name: model-e2e-evidence-${{ github.run_id }}-${{ github.run_attempt }} path: ${{ env.E2E_OUTPUT }}/evidence/ if-no-files-found: warn retention-days: 30 report: if: always() needs: [prepare, gpu] runs-on: ubuntu-24.04 timeout-minutes: 10 permissions: contents: read actions: read statuses: write steps: - uses: actions/checkout@v4 with: persist-credentials: false - uses: actions/download-artifact@v4 if: needs.prepare.result == 'success' with: name: model-e2e-input-${{ github.run_id }}-${{ github.run_attempt }} path: input - uses: actions/download-artifact@v4 if: needs.gpu.result == 'success' || needs.gpu.result == 'failure' continue-on-error: true with: name: model-e2e-evidence-${{ github.run_id }}-${{ github.run_attempt }} path: evidence - name: Summarize and report to the approved KT PR SHA env: GH_TOKEN: ${{ github.token }} GPU_JOB_RESULT: ${{ needs.gpu.result }} run: python3 .github/model-e2e/report.py