voice: real-time Deepgram WebSocket streaming STT (Voice 6.5->8.0) #10
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: Operon CI | |
| on: | |
| push: | |
| branches: ["main", "master", "develop"] | |
| pull_request: | |
| branches: ["main", "master", "develop"] | |
| jobs: | |
| test: | |
| name: Python ${{ matrix.python-version }} / ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| # Hard cap so a hanging test can never run for hours. | |
| timeout-minutes: 20 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.9", "3.10", "3.11", "3.12"] | |
| os: ["ubuntu-latest", "macos-latest"] | |
| # macOS runners bill at 10x — run the full Python matrix on cheap Ubuntu, | |
| # and only one macOS job for cross-platform signal. Keeps Actions minutes low. | |
| exclude: | |
| - { os: macos-latest, python-version: "3.9" } | |
| - { os: macos-latest, python-version: "3.10" } | |
| - { os: macos-latest, python-version: "3.11" } | |
| env: | |
| OPERON_SKIP_WIZARD: "1" | |
| OPERON_NO_GIT: "1" | |
| CI: "1" | |
| # Defensive: force UTF-8 I/O at the Python level (safe on macOS + Linux; | |
| # avoids system-locale assumptions like C.UTF-8 that don't exist on macOS). | |
| PYTHONIOENCODING: "utf-8" | |
| PYTHONUTF8: "1" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Configure git identity (some tests touch real git) | |
| run: | | |
| git config --global user.email "ci@operon.dev" | |
| git config --global user.name "Operon CI" | |
| git config --global init.defaultBranch main | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: "pip" | |
| - name: Install core dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| - name: Install test dependencies | |
| run: | | |
| pip install pytest pytest-asyncio pytest-cov pytest-timeout | |
| pip install pypdf reportlab | |
| # Data-analysis tools/tests need these (pip-installable, no hardware): | |
| pip install pandas numpy matplotlib seaborn openpyxl aiohttp | |
| - name: Install package in editable mode | |
| run: pip install -e . || true | |
| - name: Lint (ruff) | |
| run: | | |
| pip install ruff | |
| ruff check . --select E,F,W --ignore E501,F401,F811 || true | |
| - name: Run unit tests (test_all.py) | |
| # Network-flaky / optional-dep paths must not fail the build. | |
| run: python test_all.py || echo "test_all.py reported failures (non-gating)" | |
| - name: Run integration tests (tests/) | |
| # Exclude tests that require real hardware (mic, screen, keyboard) or | |
| # heavy optional deps (whisper/torch, lancedb) the runner doesn't have. | |
| # A per-test timeout kills anything that tries to block on a device. | |
| run: | | |
| set -o pipefail | |
| pytest tests/ -q -rfE -p no:cacheprovider \ | |
| --timeout=120 --timeout-method=thread \ | |
| --ignore=tests/test_browser.py \ | |
| --ignore=tests/test_browser_supervisor.py \ | |
| --ignore=tests/test_computer_use.py \ | |
| --ignore=tests/test_voice_pipeline.py \ | |
| --ignore=tests/test_visual_grounding.py \ | |
| --ignore=tests/test_vector_memory.py \ | |
| --cov=core --cov=tools \ | |
| --cov-report=xml:coverage.xml \ | |
| 2>&1 | tee /tmp/pytest_out.txt | |
| - name: Surface failures in run summary | |
| if: failure() | |
| run: | | |
| echo "### ❌ Failing tests on ${{ matrix.os }} / py${{ matrix.python-version }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo '```' >> "$GITHUB_STEP_SUMMARY" | |
| grep -E "^FAILED|^ERROR" /tmp/pytest_out.txt >> "$GITHUB_STEP_SUMMARY" || echo "(no FAILED/ERROR lines captured)" >> "$GITHUB_STEP_SUMMARY" | |
| echo '```' >> "$GITHUB_STEP_SUMMARY" | |
| echo "#### Tracebacks (first 120 lines)" >> "$GITHUB_STEP_SUMMARY" | |
| echo '```' >> "$GITHUB_STEP_SUMMARY" | |
| grep -A4 -E "Error|assert|Exception" /tmp/pytest_out.txt | head -120 >> "$GITHUB_STEP_SUMMARY" || true | |
| echo '```' >> "$GITHUB_STEP_SUMMARY" | |
| - name: Upload coverage report | |
| uses: codecov/codecov-action@v4 | |
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' | |
| with: | |
| files: coverage.xml | |
| fail_ci_if_error: false | |
| browser-tests: | |
| name: Browser Tests (Playwright) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' | |
| timeout-minutes: 15 | |
| env: | |
| OPERON_SKIP_WIZARD: "1" | |
| OPERON_NO_GIT: "1" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: "pip" | |
| - name: Install | |
| run: | | |
| pip install -r requirements.txt | |
| pip install playwright pytest pytest-asyncio pytest-timeout | |
| pip install -e . || true | |
| playwright install --with-deps chromium | |
| - name: Run browser tests | |
| run: pytest tests/test_browser.py -q --timeout=180 || true | |
| type-check: | |
| name: Type Check (mypy) | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install | |
| run: | | |
| pip install mypy types-requests | |
| pip install -r requirements.txt || true | |
| pip install -e . || true | |
| - name: mypy | |
| run: | | |
| mypy core/router.py core/reflection.py core/parallel_executor.py \ | |
| tools/github_ops.py --ignore-missing-imports || true | |
| summary: | |
| name: Test Summary | |
| runs-on: ubuntu-latest | |
| needs: [test] | |
| if: always() | |
| steps: | |
| - name: Print summary | |
| run: | | |
| echo "## Operon CI" | |
| echo "- Python: 3.9 – 3.12 · OS: ubuntu + macos" | |
| echo "- Hardware/heavy-dep suites (browser, computer_use, voice, visual_grounding)" | |
| echo " run locally with the full install, not in headless CI." | |
| echo "- Per-test timeout: 120s · Job cap: 20 min" |