Skip to content

fix(python): support Python 3.14 (daemon pool WorkerContext, wake litert bridge, requires-python ceiling, 3.14 CI lane) - #53

Open
spfcraze wants to merge 4 commits into
mainfrom
fix/python-3.14-support
Open

fix(python): support Python 3.14 (daemon pool WorkerContext, wake litert bridge, requires-python ceiling, 3.14 CI lane)#53
spfcraze wants to merge 4 commits into
mainfrom
fix/python-3.14-support

Conversation

@spfcraze

@spfcraze spfcraze commented Aug 10, 2026

Copy link
Copy Markdown
Owner

fix(python): support Python 3.14 — daemon pool WorkerContext, wake-word litert bridge, raise requires-python ceiling

Summary

This PR makes hermes-agent run on Python 3.14 (verified against 3.14.4) while
remaining fully backward-compatible with 3.11/3.12/3.13. It contains exactly
three changes, each independently verified:

  1. tools/daemon_pool.py — the only code-level 3.14 blocker: CPython 3.14
    changed the concurrent.futures.thread._worker signature from
    (executor_reference, work_queue, initializer, initargs) to
    (executor_reference, ctx, work_queue) with a WorkerContext object.
    DaemonThreadPoolExecutor._adjust_thread_count mirrored the old 4-arg
    call and crashes on 3.14 with AttributeError: '_initializer' (the base
    class no longer sets those attributes). This is load-bearing: the delegate
    timeout executor (tools/delegate_tool.py:2802) uses this pool, so
    subagent delegation, heartbeat, and orchestration all break on 3.14
    without it.
  2. pyproject.toml [wake] extra + [tool.uv] overridetflite-runtime
    (pulled by openwakeword on Linux) ships only cp38–cp311 wheels and
    cannot install on Python 3.12+. ai-edge-litert is the cross-platform
    successor with cp314 wheels; the existing bridge in
    tools/wake_word.py (ensure_tflite_runtime aliases
    tflite_runtime.interpreterai_edge_litert.interpreter) now covers
    Linux too. The marker-gated override excludes tflite-runtime from
    resolution on 3.12+.
  3. requires-python ceiling — raised from <3.14 to <3.15. The cap's
    stated reason ("Rust-backed transitives have no cp314 wheel yet") is stale:
    pydantic-core 2.46.4, numpy 2.4.3, uvloop, watchfiles, uvicorn all ship
    cp314 wheels; the full CI-shaped install succeeds on 3.14.4.

Verification (all measured, no claims without runs)

daemon_pool fix — sabotage-verified

Scenario Result
New regression tests on 3.14 without the fix (worktree at origin/main) 5/5 FAILAttributeError: '_initializer'
Same tests on 3.14 with the fix 5/5 PASS (0.78s)
Same tests on 3.12 with the fix 5/5 PASS (0.79s) — backward compatible
test_delegate.py on 3.14, unfixed tree 41 failed / 64 passed (delegation broken)
test_delegate.py on 3.14, fixed tree 63/63 PASS
test_compression_concurrent_fork.py + test_async_delegation.py, fixed 62/62 PASS

The two new regression tests:

  • test_worker_context_signature_compat — spawns the pool with an
    initializer/initargs and asserts workers receive the initialized
    thread-local (the exact 3.14 WorkerContext path).
  • test_no_initializer_spawns_and_reuses — pool without initializer still
    spawns and reuses workers on 3.14.

Both run in a subprocess so a failure cannot wedge the test runner's own pool.

wake-word / tflite fix — verified

Scenario Result
uv sync --extra wake --extra dev on 3.14.4 exit 0 — tflite-runtime excluded, ai-edge-litert 2.1.6 installed
import tflite_runtime after sync Not installed (correct — conflict works)
ensure_tflite_runtime() bridge True — resolves via ai_edge_litert.interpreter
tests/tools/test_wake_word.py on 3.14 26 passed / 3 skipped
Same on 3.12 26 passed / 3 skipped — backward compatible
uv.lock marker tflite-runtime ... marker = "python_full_version < '3.12'"

Full dependency audit (Linux x86-64, 2026-08-09)

  • 92 top-level deps audited: 91 resolve with cp314 wheels, 0 source-build-only.
  • The one previous failure (tflite-runtime) is fixed by this PR.
  • Affected extras verified on 3.14: anthropic, messaging (telegram/discord/
    slack/aiohttp), matrix (mautrix+asyncpg+olm), teams, homeassistant, voice
    (faster-whisper/onnxruntime/sherpa), mcp, modal, elevenlabs, boto3,
    google/azure, feishu, youtube-transcript-api, web, acp.

Test suite (CI-shaped surface, 3.14.4)

  • Collection (without tests/e2e/tests/integration — the CI -m 'not integration' surface): 29,577 tests, 0 collection errors on 3.14.
  • Clean sequential run with isolated --basetemp: exit 0, no error
    cascade, residual failures are the daemon-pool delegation cluster (fixed
    here) plus pre-existing parallel-run artifacts reproducible on 3.12.
  • Methodology note: earlier full-suite runs showing 23k "errors" were
    contaminated by concurrent pytest runs sharing /tmp/pytest-of-rat
    basetemp (tmp_path fixture races) — not 3.14 breakage. Every affected
    test passes standalone or with this PR's fix.

Why the changes are safe / backward compatible

  • daemon_pool.py: the hasattr(self, "_create_worker_context") branch
    selects the 3.14 API only when the base class provides it; 3.8–3.13 keeps
    the exact original positional call. Verified on 3.12 (5/5 + delegate
    suite + wake suite all green).
  • Wake extra: ai-edge-litert was already the macOS dependency; this extends
    it to Linux where the same bridge already existed. tflite-runtime remains
    available on <3.12 for the legacy path.
  • requires-python <3.15: purely widens the accepted range; existing
    installs on 3.11–3.13 are unaffected.

Files changed

  • tools/daemon_pool.py — WorkerContext-compatible worker spawn (+19/−6)
  • tests/tools/test_daemon_pool.py — 2 new regression tests (+67)
  • pyproject.toml — wake extra litert for all platforms, tflite override,
    requires-python ceiling (+24/−9)
  • uv.lock — regenerated marker matrix for 3.14

Test evidence artifacts

Full logs from every run above are available in the py314 mission workspace:
/home/rat/py314/notes/ (hunter-dep-audit.md, benefits-measurement.md,
monitor-validation.md, PLAN.md status history).

@spfcraze
spfcraze force-pushed the fix/python-3.14-support branch from 753616d to 39a2268 Compare August 10, 2026 20:15
@spfcraze spfcraze changed the title fix(python): support Python 3.14 (daemon pool WorkerContext, wake-word litert bridge, raise requires-python ceiling) fix(python): support Python 3.14 (daemon pool WorkerContext, wake litert bridge, requires-python ceiling, 3.14 CI lane) Aug 10, 2026
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

૮ >ﻌ< ა ci review

running on 39a2268 — ci: add Python 3.14 test lane exercising the 3.14 compatibil


waiting for more jobs to start…

⚠️ Action required

CI-sensitive file review · View job

This PR changes CI-sensitive files (eslint config, workflow YAMLs, or composite actions). These influence what the js-autofix job executes and pushes to main.

Sensitive files changed:

How to fix:

Add the ci-reviewed label after verifying:

  • no new eslint rules with custom fix functions that write outside linted paths,
  • no workflow changes that widen permissions or remove guards,
  • no composite action changes that alter what gets executed.

⚠️ Warnings

OSV vulnerability scan · View job

2 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.

…14 runtime import smoke (AI-review points 1,3)

Addresses AI-review observations on PR NousResearch#83433:

1. daemon_pool: the 3.14 worker-signature branch was selected by
   hasattr(self, '_create_worker_context') alone, which a CPython fork
   could expose without the new _worker(...ctx, work_queue) contract.
   Now gated on BOTH sys.version_info >= (3,14) AND the attribute.
   Regression test verifies the branch selector + the source gate.

3. CI: the 3.14 lane ran only four daemon-pool-adjacent test files,
   leaving runtime import regressions (gateway/agent/tools) uncovered on
   3.14. Added a runtime import smoke importing the core persistent/
   agent/gateway/tool modules so a 3.14 import break surfaces in CI.

Point 2 (uv.lock exclude-newer) verified: all 56 after-window packages
are covered by exclude-newer-package exceptions; uv lock --check passes
clean (249 pkgs). No change needed.
Point 4 (wake_word backend) verified: ensure_tflite_runtime imports the
real tflite_runtime first and falls back to ai_edge_litert only if absent
(sys.modules.setdefault) — deterministic, no conflict. No change needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant