feat: add reranker UI config panel to ReMeLightMemoryCard #8535
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Tests | |
| on: | |
| push: | |
| branches: [main, master, dev, develop] | |
| paths: | |
| - 'src/**' | |
| - 'tests/**' | |
| - 'pyproject.toml' | |
| - 'setup.py' | |
| - '.github/workflows/tests.yml' | |
| pull_request: | |
| branches: [main, master, dev, develop] | |
| paths: | |
| - 'src/**' | |
| - 'tests/**' | |
| - 'pyproject.toml' | |
| - 'setup.py' | |
| - '.github/workflows/tests.yml' | |
| workflow_dispatch: | |
| inputs: | |
| integration_marker: | |
| description: >- | |
| Pytest marker expression for the integration tier. Leave blank | |
| to keep the event-based default (PR=p0, push/dispatch=p0+p1). | |
| Examples: "integration and p0", "integration and (p0 or p1)", | |
| "integration". | |
| required: false | |
| default: '' | |
| # Coverage data is collected on a single matrix entry per test class | |
| # (ubuntu-latest + python 3.13 for lower tracer overhead) | |
| # instead of being re-run from scratch in coverage-report. The data files | |
| # (.coverage.unit / .coverage.contract / .coverage.integration plus their | |
| # cobertura xml) are uploaded as short-lived artifacts and consumed by the | |
| # coverage-report job, which only combines and renders — it does not run | |
| # pytest itself anymore. | |
| jobs: | |
| spam-gate: | |
| name: PR Spam Gate | |
| if: github.event_name == 'pull_request' | |
| uses: ./.github/workflows/pr-spam-gate.yml | |
| with: | |
| author: ${{ github.event.pull_request.user.login }} | |
| approval-gate: | |
| name: Maintainer Approval | |
| needs: [spam-gate] | |
| if: | | |
| always() && | |
| (needs.spam-gate.result == 'skipped' || needs.spam-gate.outputs.blocked != 'true') | |
| runs-on: ubuntu-latest | |
| environment: maintainer-approved | |
| steps: | |
| - name: Approval granted | |
| run: echo "Approved by maintainer" | |
| unit-tests: | |
| name: Unit Tests - py${{ matrix.python-version }} - ${{ matrix.os }} | |
| needs: approval-gate | |
| if: | | |
| always() && | |
| needs.approval-gate.result == 'success' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.11", "3.13"] | |
| os: [ubuntu-latest] | |
| include: | |
| - os: macos-latest | |
| python-version: "3.11" | |
| - os: windows-latest | |
| python-version: "3.11" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| - name: Install dependencies | |
| shell: bash | |
| run: | | |
| # Slim install for the unit tier: unit tests neither serve the | |
| # console frontend nor require the optional heavy extras | |
| # (whisper/torch, codex, qoder). Optional-dependency tests are | |
| # guarded with pytest.importorskip and still run in the nightly | |
| # full sweep, which keeps the full extra. Keeping this job lean | |
| # cuts the Windows matrix entry from tens of minutes to a few. | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev,test]" | |
| - name: Run unit tests | |
| shell: bash | |
| env: | |
| COVERAGE_FILE: .coverage.unit | |
| run: | | |
| # Coverage is collected only on the ubuntu/py3.13 entry so the | |
| # data file can be uploaded for coverage-report. Other matrix | |
| # entries only verify cross-platform compatibility. | |
| if [ "${{ matrix.os }}" = "ubuntu-latest" ] && \ | |
| [ "${{ matrix.python-version }}" = "3.13" ]; then | |
| pytest tests/unit -v \ | |
| --cov=src/qwenpaw \ | |
| --cov-report=xml:coverage.unit.xml | |
| else | |
| pytest tests/unit -v | |
| fi | |
| - name: Upload unit coverage data | |
| if: | | |
| matrix.os == 'ubuntu-latest' && | |
| matrix.python-version == '3.13' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-data-unit | |
| path: | | |
| .coverage.unit | |
| coverage.unit.xml | |
| retention-days: 1 | |
| include-hidden-files: true | |
| contract-tests: | |
| name: Contract Tests - py${{ matrix.python-version }} - ${{ matrix.os }} | |
| needs: approval-gate | |
| if: | | |
| always() && | |
| needs.approval-gate.result == 'success' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.11", "3.13"] | |
| os: [ubuntu-latest] | |
| include: | |
| - os: macos-latest | |
| python-version: "3.11" | |
| - os: windows-latest | |
| python-version: "3.11" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js (for console build) | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: console/package-lock.json | |
| - name: Build console frontend | |
| shell: bash | |
| env: | |
| NODE_OPTIONS: "--max-old-space-size=8192" | |
| run: | | |
| cd console && npm ci && npm run build | |
| - name: Copy console build into package | |
| shell: bash | |
| run: | | |
| rm -rf src/qwenpaw/console/* | |
| mkdir -p src/qwenpaw/console | |
| cp -R console/dist/* src/qwenpaw/console/ | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| - name: Install dependencies | |
| shell: bash | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[dev,test,full]" | |
| - name: Run contract tests | |
| shell: bash | |
| env: | |
| COVERAGE_FILE: .coverage.contract | |
| run: | | |
| # Same conditional-coverage pattern as unit-tests. | |
| if [ "${{ matrix.os }}" = "ubuntu-latest" ] && \ | |
| [ "${{ matrix.python-version }}" = "3.13" ]; then | |
| pytest tests/contract -v \ | |
| --cov=src/qwenpaw \ | |
| --cov-report=xml:coverage.contract.xml \ | |
| --cov-fail-under=0 | |
| else | |
| pytest tests/contract -v | |
| fi | |
| - name: Upload contract coverage data | |
| if: | | |
| matrix.os == 'ubuntu-latest' && | |
| matrix.python-version == '3.13' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-data-contract | |
| path: | | |
| .coverage.contract | |
| coverage.contract.xml | |
| retention-days: 1 | |
| include-hidden-files: true | |
| integrated-tests: | |
| name: Integrated Tests - py${{ matrix.python-version }} - ${{ matrix.os }} | |
| needs: approval-gate | |
| if: | | |
| always() && | |
| needs.approval-gate.result == 'success' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.11", "3.13"] | |
| os: [ubuntu-latest] | |
| include: | |
| - os: macos-latest | |
| python-version: "3.11" | |
| - os: windows-latest | |
| python-version: "3.11" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node.js (for console build) | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: console/package-lock.json | |
| - name: Build console frontend | |
| shell: bash | |
| env: | |
| NODE_OPTIONS: "--max-old-space-size=8192" | |
| run: | | |
| cd console && npm ci && npm run build | |
| - name: Copy console build into package | |
| shell: bash | |
| run: | | |
| rm -rf src/qwenpaw/console/* | |
| mkdir -p src/qwenpaw/console | |
| cp -R console/dist/* src/qwenpaw/console/ | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| - name: Install dependencies | |
| shell: bash | |
| run: | | |
| python -m pip install --upgrade pip | |
| # The macOS runner ships setuptools 65.5.0, which pip's resolver | |
| # upgrades to the latest release (84.x) while backtracking the | |
| # dependency graph. setuptools >= 82 no longer ships | |
| # pkg_resources.declare_namespace, which lark-oapi's namespace | |
| # packages still call at import time, so the Feishu mock IM | |
| # integration tests crash with AttributeError on macOS. Pin | |
| # setuptools <82 here — the pin must ride in the SAME pip command | |
| # as the install: a separate `pip install "setuptools<82"` step | |
| # beforehand gets upgraded away by this resolution again | |
| # (reproduced with pip 26.2.1; see CI run 31571533395). | |
| if [ "${{ runner.os }}" = "macOS" ]; then | |
| pip install -e ".[dev,test,full]" "setuptools<82" | |
| else | |
| pip install -e ".[dev,test,full]" | |
| fi | |
| # tests/integration/browser carries the integration marker but no | |
| # priority marker, so any expression without a p0/p1 filter (a | |
| # workflow_dispatch of "integration", say) selects it and needs a real | |
| # Chromium. Same command as the e2e workflows. | |
| - name: Install Playwright browser | |
| shell: bash | |
| run: | | |
| playwright install chromium --with-deps | |
| - name: Check if integrated tests exist | |
| id: check-integrated | |
| shell: bash | |
| run: | | |
| if [ -d "tests/integration" ] && compgen -G "tests/integration/*.py" > /dev/null; then | |
| echo "has_tests=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_tests=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Determine pytest marker expression | |
| id: marker | |
| if: steps.check-integrated.outputs.has_tests == 'true' | |
| shell: bash | |
| env: | |
| DISPATCH_MARKER: ${{ inputs.integration_marker }} | |
| run: | | |
| # Manual dispatch override -> use whatever the maintainer typed. | |
| # PR gate -> p0 only (fast feedback) | |
| # push / workflow_dispatch (no input) -> p0 + p1 (full pre-merge gate) | |
| # nightly full sweep lives in full-tests-nightly.yml | |
| if [ -n "${DISPATCH_MARKER}" ]; then | |
| EXPR="${DISPATCH_MARKER}" | |
| elif [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then | |
| EXPR="integration and p0" | |
| else | |
| EXPR="integration and (p0 or p1)" | |
| fi | |
| echo "expr=$EXPR" >> "$GITHUB_OUTPUT" | |
| echo "Selected marker expression: $EXPR" | |
| - name: Run integrated tests | |
| if: steps.check-integrated.outputs.has_tests == 'true' | |
| shell: bash | |
| env: | |
| # Subprocess coverage on the ubuntu/py3.11 entry only. conftest.py | |
| # treats empty / missing as off, so the other matrix entries do | |
| # not pay the tracer overhead. Multi-platform coverage is opt-in | |
| # via full-tests-nightly.yml dispatch (coverage_platforms input). | |
| QWENPAW_INTEGRATION_COVERAGE: ${{ (matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11') && '1' || '' }} | |
| # Windows runners are 2-core and IO-bound; under xdist parallel | |
| # the default 15s HTTP timeout is too tight and intermittently | |
| # surfaces as ``httpx.ReadTimeout``. Lift the floor here. | |
| QWENPAW_INTEGRATION_HTTP_TIMEOUT: ${{ matrix.os == 'windows-latest' && '120' || '' }} | |
| run: | | |
| if [ -n "$QWENPAW_INTEGRATION_COVERAGE" ]; then | |
| # Subprocess coverage entry. Parent process must not carry | |
| # --cov, hence --no-cov here. | |
| pytest tests/integration -v --no-cov \ | |
| -n auto --dist=loadscope --timeout=300 \ | |
| -m "${{ steps.marker.outputs.expr }}" | |
| cp .integration_coverage/integration_subproc \ | |
| .coverage.integration | |
| # `coverage xml` honours fail_under and exits 2 when below; | |
| # tolerate that — the combined value is what matters. | |
| coverage xml --data-file=.coverage.integration \ | |
| -o coverage.integration.xml || [ "$?" -eq 2 ] | |
| else | |
| pytest tests/integration -v \ | |
| -n auto --dist=loadscope --timeout=300 \ | |
| -m "${{ steps.marker.outputs.expr }}" | |
| fi | |
| - name: Upload integration coverage data | |
| if: | | |
| steps.check-integrated.outputs.has_tests == 'true' && | |
| matrix.os == 'ubuntu-latest' && | |
| matrix.python-version == '3.11' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-data-integration | |
| path: | | |
| .coverage.integration | |
| coverage.integration.xml | |
| retention-days: 1 | |
| include-hidden-files: true | |
| coverage-report: | |
| name: Coverage Report | |
| needs: [approval-gate, unit-tests, contract-tests, integrated-tests] | |
| if: | | |
| always() && | |
| needs.approval-gate.result == 'success' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: 'pip' | |
| - name: Install coverage tool | |
| shell: bash | |
| run: | | |
| python -m pip install --upgrade pip | |
| # Only the coverage tool is needed for combine / report; no need | |
| # to install the full project (the per-tier .coverage data files | |
| # arrive via download-artifact below). | |
| pip install coverage | |
| - name: Download coverage data artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: coverage-data-* | |
| merge-multiple: true | |
| - name: Inspect downloaded coverage data | |
| shell: bash | |
| run: | | |
| ls -la .coverage* coverage.*.xml 2>&1 | head -30 || true | |
| - name: Combine all coverage data | |
| shell: bash | |
| run: | | |
| # Collect all coverage data files: unit, contract, and integration. | |
| coverage combine \ | |
| .coverage.unit .coverage.contract .coverage.integration | |
| # Tolerate fail-under (exit 2): combined number is what matters | |
| # and reflected in the summary regardless. | |
| coverage xml -o coverage.combined.xml || [ "$?" -eq 2 ] | |
| coverage html -d htmlcov-combined || [ "$?" -eq 2 ] | |
| echo "Combined coverage report:" | |
| coverage report --skip-covered --fail-under=0 | |
| coverage json -o coverage.combined.json || [ "$?" -eq 2 ] | |
| - name: Build coverage summary table | |
| id: cov_summary | |
| shell: bash | |
| run: | | |
| # Best-effort number per tier; missing/malformed data => "n/a". | |
| set +e | |
| # Read line-rate from cobertura xml directly to avoid the | |
| # coverage source-filter mismatch that would otherwise produce | |
| # "No data to report" on the per-tier data files. | |
| get_pct() { | |
| python3 -c 'import sys, xml.etree.ElementTree as ET; print(round(float(ET.parse(sys.argv[1]).getroot().attrib.get("line-rate", "0")) * 100))' "$1" 2>/dev/null || echo "n/a" | |
| } | |
| UNIT=$(get_pct coverage.unit.xml) | |
| CONTRACT=$(get_pct coverage.contract.xml) | |
| INTEGRATION=$(get_pct coverage.integration.xml) | |
| COMBINED=$(get_pct coverage.combined.xml) | |
| echo "UNIT=${UNIT:-n/a}" | |
| echo "CONTRACT=${CONTRACT:-n/a}" | |
| echo "INTEGRATION=${INTEGRATION:-n/a}" | |
| echo "COMBINED=${COMBINED:-n/a}" | |
| { | |
| echo "## 📊 Coverage report" | |
| echo "" | |
| echo "| Test category | Coverage |" | |
| echo "|---|---|" | |
| echo "| Unit | ${UNIT:-n/a}% |" | |
| echo "| Contract | ${CONTRACT:-n/a}% |" | |
| echo "| Integration | ${INTEGRATION:-n/a}% |" | |
| echo "| **Combined** | **${COMBINED:-n/a}%** |" | |
| echo "" | |
| echo "> Combined = \`coverage combine\` of unit + contract + integration (subprocess mode)." | |
| echo "> HTML reports under workflow artifact \`coverage-reports\` (\`htmlcov-combined\` / \`htmlcov-integration\`)." | |
| echo "> Coverage is collected only on the ubuntu/py3.13 matrix entry; other matrix entries verify cross-platform compatibility without the tracer overhead." | |
| } > coverage_summary.md | |
| cat coverage_summary.md >> "$GITHUB_STEP_SUMMARY" | |
| - name: Sticky coverage PR comment | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const body = fs.readFileSync('coverage_summary.md', 'utf8'); | |
| const marker = '<!-- qwenpaw-coverage-summary -->'; | |
| const finalBody = `${marker}\n${body}`; | |
| const comments = await github.paginate( | |
| github.rest.issues.listComments, | |
| { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }, | |
| ); | |
| const existing = comments.find( | |
| (c) => c.body && c.body.includes(marker), | |
| ); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: finalBody, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: finalBody, | |
| }); | |
| } | |
| - name: Upload coverage artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-reports | |
| path: | | |
| coverage.unit.xml | |
| coverage.contract.xml | |
| coverage.integration.xml | |
| coverage.combined.xml | |
| htmlcov-combined/ | |
| htmlcov-integration/ | |
| retention-days: 7 | |
| test-summary: | |
| name: Test Summary | |
| needs: [approval-gate, unit-tests, contract-tests, integrated-tests, coverage-report] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check test results | |
| shell: bash | |
| run: | | |
| echo "Approval gate: ${{ needs.approval-gate.result }}" | |
| echo "Unit tests: ${{ needs.unit-tests.result }}" | |
| echo "Contract tests: ${{ needs.contract-tests.result }}" | |
| echo "Integrated tests: ${{ needs.integrated-tests.result }}" | |
| if [ "${{ needs.approval-gate.result }}" != "success" ]; then | |
| echo "❌ Approval not granted" | |
| exit 1 | |
| fi | |
| if [ "${{ needs.unit-tests.result }}" = "failure" ] || \ | |
| [ "${{ needs.contract-tests.result }}" = "failure" ] || \ | |
| [ "${{ needs.integrated-tests.result }}" = "failure" ]; then | |
| echo "❌ Some tests failed" | |
| exit 1 | |
| else | |
| echo "✅ All tests passed" | |
| fi |