Skip to content

feat: add reranker UI config panel to ReMeLightMemoryCard #8535

feat: add reranker UI config panel to ReMeLightMemoryCard

feat: add reranker UI config panel to ReMeLightMemoryCard #8535

Workflow file for this run

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