Skip to content

Conversation

@Coldaine
Copy link
Owner

@Coldaine Coldaine commented Dec 12, 2025

User description

Summary

  • Upgrade PyO3 from 0.24.1 to 0.27 with auto-initialize feature
  • Migrate API from with_gil() to attach() per PyO3 0.27 migration guide
  • Remove explicit Python::initialize() calls (handled by auto-initialize)
  • Add development tooling: .envrc for direnv, .python-version pinned to 3.12
  • Add justfile recipes: setup-moonshine, build-moonshine, verify-moonshine
  • Add verify_moonshine.rs example for isolated STT testing
  • Document PyO3/Python 3.13 instability issue in docs/issues/

Motivation

Resolves Python 3.13 free-threading compatibility issues documented in docs/issues/pyo3_instability.md. PyO3 0.24 requires Sync for #[pyclass] structs in free-threaded Python 3.13, causing build failures on modern Linux distros.

Test plan

  • cargo check -p coldvox-stt --features moonshine passes with no warnings
  • just verify-moonshine passes with Python 3.12
  • CI passes on all supported platforms

🤖 Generated with Claude Code


PR Type

Enhancement, Tests


Description

  • Upgrade PyO3 from 0.24.1 to 0.27 for Python 3.13 compatibility

  • Migrate API from with_gil() to attach() per PyO3 0.27 migration guide

  • Add development tooling: .envrc for direnv, .python-version pinned to 3.12

  • Add justfile recipes and verify_moonshine.rs example for isolated STT testing

  • Document PyO3/Python 3.13 free-threading compatibility issues


Diagram Walkthrough

flowchart LR
  A["PyO3 0.24.1"] -- "upgrade" --> B["PyO3 0.27"]
  C["with_gil API"] -- "migrate to" --> D["attach API"]
  E["Manual setup"] -- "automate with" --> F[".envrc + justfile"]
  G["No verification"] -- "add" --> H["verify_moonshine.rs example"]
  I["No documentation"] -- "add" --> J["pyo3_instability.md"]
Loading

File Walkthrough

Relevant files
Dependencies
Cargo.toml
Upgrade PyO3 dependency to 0.27                                                   

crates/coldvox-stt/Cargo.toml

  • Upgrade pyo3 dependency from 0.24.1 to 0.27
  • Keep auto-initialize feature enabled for automatic Python
    initialization
  • Update comment to reflect auto-initialize fix
+2/-2     
Enhancement
moonshine.rs
Migrate PyO3 API from with_gil to attach                                 

crates/coldvox-stt/src/plugins/moonshine.rs

  • Replace all Python::with_gil() calls with Python::attach() (4
    occurrences)
  • Affects verify_python_environment(), load_model_and_processor(),
    transcribe(), and check_moonshine_available() functions
  • Aligns with PyO3 0.27 migration guide for free-threaded Python 3.13
    support
+4/-4     
Tests
verify_moonshine.rs
Add Moonshine verification example for isolated testing   

crates/coldvox-stt/examples/verify_moonshine.rs

  • New example demonstrating isolated Moonshine STT plugin testing
  • Covers full workflow: factory creation, plugin initialization, test
    audio generation, processing, and transcription
  • Generates synthetic 440Hz sine wave audio for testing
  • Includes timing measurements and user-friendly console output with
    emoji indicators
+68/-0   
Configuration changes
.envrc
Add direnv configuration for venv automation                         

.envrc

  • New direnv configuration for automatic Python venv activation
  • Auto-creates .venv using uv if not present
  • Automatically syncs dependencies on directory entry
  • Sets PYO3_PYTHON environment variable to use venv's Python interpreter
+18/-0   
justfile
Add justfile recipes for Moonshine setup and testing         

justfile

  • Add setup-moonshine recipe to install Python dependencies via script
  • Add build-moonshine recipe to build with Moonshine feature enabled
  • Add verify-moonshine recipe to run the verification example
  • Recipes automate setup and testing workflow for Moonshine backend
+13/-0   
Documentation
pyo3_instability.md
Document PyO3 0.24 Python 3.13 compatibility issues           

docs/issues/pyo3_instability.md

  • New documentation of PyO3 0.24 instability on Python 3.13
    free-threaded builds
  • Details symptoms, findings, and impact on ColdVox Moonshine backend
  • Provides short-term workaround (pin Python to 3.12) and long-term
    recommendations
  • Explains Sync requirement changes and API deprecations in PyO3 0.24+
+27/-0   

Coldaine and others added 2 commits December 10, 2025 23:35
- Add .envrc for direnv auto-activation with uv sync
- Add .python-version pinned to 3.12 (avoids PyO3/Py3.13 issues)
- Fix duplicate .venv/ in .gitignore, add .bmad/ and .claude/
- Add justfile recipes: setup-moonshine, build-moonshine, verify-moonshine
- Add verify_moonshine.rs example for isolated STT testing
- Document PyO3 instability issue in docs/issues/
- Upgrade pyo3 from 0.24.1 to 0.27
- Remove auto-initialize feature, use explicit Python::initialize()
- Migrate API: with_gil() → attach() per PyO3 0.27 migration guide
- Resolves Python 3.13 free-threading compatibility issues

Addresses: docs/issues/pyo3_instability.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copilot AI review requested due to automatic review settings December 12, 2025 08:16
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@qodo-code-review
Copy link

qodo-code-review bot commented Dec 12, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No audit logs: The new verification example performs plugin initialization, processing, and transcription
without emitting structured audit logs containing user/action/timestamp/outcome context.

Referred Code
println!("🚀 Starting Moonshine Verification...");

// 2. Create Plugin Factory
// This will check requirement (Python deps)
println!("🔍 Checking requirements...");
let factory = MoonshinePluginFactory::new();
factory.check_requirements()?;

// 3. Create Plugin Instance
println!("📦 Creating plugin instance...");
let mut plugin = factory.create()?;

// 4. Initialize (loads model)
println!("⏳ Initializing model (this uses PyO3 and might take a moment)...");
let start = Instant::now();
plugin.initialize(TranscriptionConfig::default()).await?;
println!("✅ Model loaded in {:.2?}", start.elapsed());

// 5. Generate Test Audio (1s of 440Hz sine wave @ 16kHz)
println!("🎵 Generating test audio...");
let sample_rate = 16000;


 ... (clipped 36 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Minimal error context: The example propagates errors with '?' and prints progress but does not add
contextual error messages or handle edge cases like missing Python deps at runtime or
empty transcription results beyond a simple notice.

Referred Code
println!("🔍 Checking requirements...");
let factory = MoonshinePluginFactory::new();
factory.check_requirements()?;

// 3. Create Plugin Instance
println!("📦 Creating plugin instance...");
let mut plugin = factory.create()?;

// 4. Initialize (loads model)
println!("⏳ Initializing model (this uses PyO3 and might take a moment)...");
let start = Instant::now();
plugin.initialize(TranscriptionConfig::default()).await?;
println!("✅ Model loaded in {:.2?}", start.elapsed());

// 5. Generate Test Audio (1s of 440Hz sine wave @ 16kHz)
println!("🎵 Generating test audio...");
let sample_rate = 16000;
let duration_secs = 2;
let spec = hound::WavSpec {
    channels: 1,
    sample_rate,


 ... (clipped 29 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-free-for-open-source-projects

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Integer overflow risk

Description: Potential integer overflow when calculating sine wave samples: (0..sample_rate *
duration_secs) multiplies two u32 values without overflow checking, which could panic or
produce incorrect audio data with large duration values.
verify_moonshine.rs [42-44]

Referred Code
let sample = (t * 440.0 * 2.0 * std::f32::consts::PI).sin();
let amplitude = i16::MAX as f32 * 0.5;
samples.push((sample * amplitude) as i16);
Command injection via direnv

Description: Command injection vulnerability: uv venv .venv executes without validating the current
directory path, allowing malicious directory names with shell metacharacters to execute
arbitrary commands when direnv auto-loads.
.envrc [8-8]

Referred Code
    uv venv .venv
fi
Path injection in PYO3_PYTHON

Description: Path injection vulnerability: PYO3_PYTHON environment variable is set using unsanitized
$(pwd) output, which could be exploited if the working directory path contains malicious
content or is controlled by an attacker.
.envrc [18-18]

Referred Code
export PYO3_PYTHON="$(pwd)/.venv/bin/python"
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Dec 12, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

Category Suggestion                                                                                                                                    Impact
Possible issue
Use direnv variable for robust path
Suggestion Impact:The suggestion prompted a change to make the path more robust, though the implementation used ${PWD} instead of ${DIRENV_ROOT}. Both approaches address the issue of using a more reliable variable than $(pwd), with ${PWD} being a standard shell variable that's always set to the current directory.

code diff:

-export PYO3_PYTHON="$(pwd)/.venv/bin/python"
+export PYO3_PYTHON="${PWD}/.venv/bin/python"

Replace $(pwd) with the more robust $DIRENV_ROOT variable in .envrc to ensure
the PYO3_PYTHON path is always resolved correctly from the project root.

.envrc [18]

-export PYO3_PYTHON="$(pwd)/.venv/bin/python"
+export PYO3_PYTHON="${DIRENV_ROOT}/.venv/bin/python"

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a common pitfall with direnv and pwd, proposing a more robust solution using the idiomatic $DIRENV_ROOT variable, which improves the reliability of the development environment setup.

Medium
  • Update

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Dec 12, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

Category Suggestion                                                                                                                                    Impact
Possible issue
Handle intermediate transcription results properly

Update the audio processing loop in verify_moonshine.rs to handle the Option
returned by plugin.process_audio. This ensures that any intermediate
transcription results are captured and displayed.

crates/coldvox-stt/examples/verify_moonshine.rs [47-65]

 // 6. Process Audio
 println!("🗣️ Processing audio ({} samples)...", samples.len());
 let process_start = Instant::now();
 
 // Feed audio in chunks
 let chunk_size = 4000;
 for chunk in samples.chunks(chunk_size) {
-    plugin.process_audio(chunk).await?;
+    if let Some(event) = plugin.process_audio(chunk).await? {
+        println!("🎤 Intermediate Transcription: {:?}", event);
+    }
 }
 
 // 7. Finalize (Trigger transcription)
 println!("📝 Finalizing and transcribing...");
 if let Some(event) = plugin.finalize().await? {
     println!("🎉 Transcription Result: {:?}", event);
 } else {
-    println!("⚠️ No transcription result returned.");
+    println!("⚠️ No final transcription result returned.");
 }
 
 println!("⏱️ Total processing time: {:.2?}", process_start.elapsed());
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that the example code is ignoring potential intermediate results from plugin.process_audio, which is a logical flaw in a verification script. Handling these results makes the example more complete and useful for testing.

Medium
General
Use idiomatic direnv environment setup
Suggestion Impact:The commit partially implemented the suggestion by replacing $(pwd) with a variable reference, though it used ${PWD} instead of the suggested $DIRENV_ROOT. The layout_python recommendation was not implemented.

code diff:

-export PYO3_PYTHON="$(pwd)/.venv/bin/python"
+export PYO3_PYTHON="${PWD}/.venv/bin/python"

Refactor the .envrc script to use the idiomatic direnv function layout_python
for activating the virtual environment and replace $(pwd) with the more robust
$DIRENV_ROOT variable.

.envrc [11-18]

-# Activate the venv
-source .venv/bin/activate
+# Activate the venv using direnv's standard library
+layout_python .venv/bin/python
 
 # Ensure dependencies are synced
 uv sync --quiet
 
 # Set PYO3 to use this venv's Python
-export PYO3_PYTHON="$(pwd)/.venv/bin/python"
+export PYO3_PYTHON="$DIRENV_ROOT/.venv/bin/python"

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that using layout_python and $DIRENV_ROOT is the idiomatic and more robust way to configure a Python environment with direnv, improving the script's quality and adherence to best practices.

Low
  • More

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades PyO3 from version 0.24.1 to 0.27 to resolve Python 3.13 compatibility issues, specifically around free-threading support. The upgrade includes migrating all GIL interaction code from the deprecated Python::with_gil() API to the new Python::attach() API, and adds development tooling to streamline Moonshine STT testing.

Key Changes:

  • PyO3 upgraded to 0.27 with auto-initialize feature for Python 3.13 compatibility
  • Migrated all Python GIL interactions from with_gil() to attach() in moonshine.rs
  • Added development infrastructure: justfile recipes, verification example, .envrc for direnv, and comprehensive documentation

Reviewed changes

Copilot reviewed 6 out of 8 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
crates/coldvox-stt/Cargo.toml Updated PyO3 dependency from 0.24.1 to 0.27 with auto-initialize feature
Cargo.lock Dependency lock file updates for PyO3 0.27 and transitive dependencies
crates/coldvox-stt/src/plugins/moonshine.rs Migrated 4 instances of Python::with_gil() to Python::attach()
crates/coldvox-stt/examples/verify_moonshine.rs New example for isolated Moonshine STT testing with synthetic audio
justfile Added 3 recipes for Moonshine setup, build, and verification
docs/issues/pyo3_instability.md Documented PyO3 Python 3.13 compatibility issues and migration rationale
.envrc Added direnv configuration for automatic venv activation and dependency sync
.gitignore Added AI agent framework directories (.bmad/, .claude/) to ignore list

1. **Free-Threading (3.13t)**: Python 3.13 supports experimental free-threading. PyO3 0.24 requires `Sync` implementation for all `#[pyclass]` types to support this.
2. **API Churn**: `Python::with_gil` is conceptually deprecated in favor of `Python::attach` in free-threaded contexts, though 0.24 still supports it.
3. **Build Tooling**: Attempting to build against Python 3.13 with older versions (or mismatched feature flags) fails.
4. **Current Config**: `coldvox-stt` uses `pyo3 = "0.24.1"`.
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line states "Current Config: coldvox-stt uses pyo3 = "0.24.1"" but the PR updates it to 0.27. This should be updated to reflect the new configuration, or reframed as "Previous Config" if documenting the historical issue.

Suggested change
4. **Current Config**: `coldvox-stt` uses `pyo3 = "0.24.1"`.
4. **Previous Config**: `coldvox-stt` used `pyo3 = "0.24.1"`.

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +37
let spec = hound::WavSpec {
channels: 1,
sample_rate,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec variable is declared but never used. Consider removing it or using it to write a WAV file for debugging purposes.

Copilot uses AI. Check for mistakes.
// 5. Generate Test Audio (1s of 440Hz sine wave @ 16kHz)
println!("🎵 Generating test audio...");
let sample_rate = 16000;
let duration_secs = 2;
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "1s of 440Hz sine wave" but the code sets duration_secs = 2, which generates 2 seconds of audio, not 1 second.

Suggested change
let duration_secs = 2;
let duration_secs = 1;

Copilot uses AI. Check for mistakes.
source .venv/bin/activate

# Ensure dependencies are synced
uv sync --quiet
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "Ensure dependencies are synced" but uv sync --quiet could fail silently. Consider removing the --quiet flag or adding error handling to ensure failures are visible to the developer.

Suggested change
uv sync --quiet
uv sync

Copilot uses AI. Check for mistakes.
mic-probe duration="30":
cd crates/app && cargo run --bin mic_probe -- --duration {{duration}}

# Install Moonshine Python dependencies (transformers, torch, librosa via uv)
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "Install Moonshine Python dependencies (transformers, torch, librosa via uv)" but this recipe doesn't install dependencies directly - it delegates to a shell script. Consider updating to "Run Moonshine dependency installation script" or similar to better reflect what this recipe does.

Suggested change
# Install Moonshine Python dependencies (transformers, torch, librosa via uv)
# Run Moonshine dependency installation script (installs transformers, torch, librosa via uv)

Copilot uses AI. Check for mistakes.

# Moonshine STT via PyO3/HuggingFace
pyo3 = { version = "0.24.1", optional = true, features = ["auto-initialize"] }
# Moonshine STT via PyO3/HuggingFace - Fixed auto-initialize
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Fixed auto-initialize" is unclear. The feature was already present in 0.24.1, so this isn't fixing it. Consider clarifying what was fixed or just stating "Updated to 0.27 with auto-initialize".

Suggested change
# Moonshine STT via PyO3/HuggingFace - Fixed auto-initialize
# Moonshine STT via PyO3/HuggingFace - Updated to 0.27 with auto-initialize

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +19
# Issue: PyO3 0.24 Instability on Python 3.13 (Moonshine Backend)

**Status**: DRAFT (Local)
**Created**: 2025-12-10
**Priority**: High (Blocks stable build on modern Linux distros)

## Problem
PyO3 0.24 introduces breaking changes and strict requirements for Python 3.13 compatibility, specifically regarding free-threaded builds (GIL removal). This impacts the `moonshine` STT plugin in ColdVox.

## Symptoms
- Build errors on systems with Python 3.13 default (e.g., Arch, Fedora Rawhide).
- Potential runtime panics if `#[pyclass]` structs do not implement `Sync`.
- API deprecations/renames (`Python::with_gil` semantics shifting).

## Findings from Research
1. **Free-Threading (3.13t)**: Python 3.13 supports experimental free-threading. PyO3 0.24 requires `Sync` implementation for all `#[pyclass]` types to support this.
2. **API Churn**: `Python::with_gil` is conceptually deprecated in favor of `Python::attach` in free-threaded contexts, though 0.24 still supports it.
3. **Build Tooling**: Attempting to build against Python 3.13 with older versions (or mismatched feature flags) fails.
4. **Current Config**: `coldvox-stt` uses `pyo3 = "0.24.1"`.
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states "PyO3 0.24 introduces breaking changes" but this PR is upgrading to PyO3 0.27, not documenting issues with 0.24. The title also references 0.24. Since the code has moved to 0.27, this documentation should either be updated to reflect that 0.27 is now being used, or the issue should be reframed to explain the historical context of why the upgrade was necessary.

Suggested change
# Issue: PyO3 0.24 Instability on Python 3.13 (Moonshine Backend)
**Status**: DRAFT (Local)
**Created**: 2025-12-10
**Priority**: High (Blocks stable build on modern Linux distros)
## Problem
PyO3 0.24 introduces breaking changes and strict requirements for Python 3.13 compatibility, specifically regarding free-threaded builds (GIL removal). This impacts the `moonshine` STT plugin in ColdVox.
## Symptoms
- Build errors on systems with Python 3.13 default (e.g., Arch, Fedora Rawhide).
- Potential runtime panics if `#[pyclass]` structs do not implement `Sync`.
- API deprecations/renames (`Python::with_gil` semantics shifting).
## Findings from Research
1. **Free-Threading (3.13t)**: Python 3.13 supports experimental free-threading. PyO3 0.24 requires `Sync` implementation for all `#[pyclass]` types to support this.
2. **API Churn**: `Python::with_gil` is conceptually deprecated in favor of `Python::attach` in free-threaded contexts, though 0.24 still supports it.
3. **Build Tooling**: Attempting to build against Python 3.13 with older versions (or mismatched feature flags) fails.
4. **Current Config**: `coldvox-stt` uses `pyo3 = "0.24.1"`.
# Issue: PyO3 0.27 Instability on Python 3.13 (Moonshine Backend)
**Status**: DRAFT (Local)
**Created**: 2025-12-10
**Priority**: High (Blocks stable build on modern Linux distros)
## Problem
PyO3 0.27 introduces breaking changes and strict requirements for Python 3.13 compatibility, specifically regarding free-threaded builds (GIL removal). This impacts the `moonshine` STT plugin in ColdVox.
> **Note:** This document was originally written for PyO3 0.24, but has been updated to reflect the upgrade to 0.27. The upgrade was necessary due to ongoing Python 3.13 compatibility issues and continued API changes in PyO3.
## Symptoms
- Build errors on systems with Python 3.13 default (e.g., Arch, Fedora Rawhide).
- Potential runtime panics if `#[pyclass]` structs do not implement `Sync`.
- API deprecations/renames (`Python::with_gil` semantics shifting).
## Findings from Research
1. **Free-Threading (3.13t)**: Python 3.13 supports experimental free-threading. PyO3 0.27 requires `Sync` implementation for all `#[pyclass]` types to support this.
2. **API Churn**: `Python::with_gil` is conceptually deprecated in favor of `Python::attach` in free-threaded contexts, though 0.27 still supports it.
3. **Build Tooling**: Attempting to build against Python 3.13 with older versions (or mismatched feature flags) fails.
4. **Current Config**: `coldvox-stt` uses `pyo3 = "0.27.x"`.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,27 @@
# Issue: PyO3 0.24 Instability on Python 3.13 (Moonshine Backend)
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The title references "PyO3 0.24 Instability" but the PR upgrades to PyO3 0.27. Consider updating the title to "PyO3 0.27 Upgrade for Python 3.13 Compatibility" or similar to better reflect the resolution rather than the problem.

Suggested change
# Issue: PyO3 0.24 Instability on Python 3.13 (Moonshine Backend)
# Issue: PyO3 0.27 Upgrade for Python 3.13 Compatibility (Moonshine Backend)

Copilot uses AI. Check for mistakes.
- Add required frontmatter to docs/issues/pyo3_instability.md
- Fix .envrc to use ${PWD} instead of $(pwd) for robustness
- Run cargo fmt to fix formatting issues in moonshine plugin files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link

@kiloconnect kiloconnect bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Critical Issue Found

I found a critical compilation error in the new verify_moonshine.rs example that prevents it from building.

Issue: Missing tokio dependency

The example uses #[tokio::main] but doesn't import or depend on tokio. This will cause a compilation failure.

Files affected:

  • crates/coldvox-stt/examples/verify_moonshine.rs

Location: Line 1 (import section)

Fix: Add tokio to the dev-dependencies section of crates/coldvox-stt/Cargo.toml:

[dev-dependencies]
tokio = { version = "1.35", features = ["rt-multi-thread", "macros"] }
serial_test = "3.2"
hound = "3.5"

Without this fix, cargo run -p coldvox-stt --example verify_moonshine will fail with a compilation error.


Additional Minor Issues

I've reviewed the changes and identified several minor issues that should be addressed:

1. Outdated comments in moonshine.rs

Files affected: crates/coldvox-stt/src/plugins/moonshine.rs

The comments on lines 80 and 90 still reference Python::with_gil() but the code now uses Python::attach(). Update these comments to reflect the current API:

/// SAFETY: `Py<PyAny>` is `Send` but requires Python GIL for all access.
/// All methods that access `cached_model` must use `Python::attach()`.
#[cfg(feature = "moonshine")]
cached_model: Option<Py<PyAny>>,

/// SAFETY: `Py<PyAny>` is `Send` but requires Python GIL for all access.
/// All methods that access `cached_processor` must use `Python::attach()`.
#[cfg(feature = "moonshine")]
cached_processor: Option<Py<PyAny>>,

2. Improve error visibility in .envrc

Files affected: .envrc

The --quiet flag on uv sync could hide important errors during development. Consider removing it or adding explicit error handling:

# Ensure dependencies are synced
uv sync || {
    echo "Error: Failed to sync dependencies"
    exit 1
}

3. Remove unused variable in verify_moonshine.rs

Files affected: crates/coldvox-stt/examples/verify_moonshine.rs

The spec variable is declared but never used (line 32-37). Either use it or remove it:

// Remove this if not needed:
let spec = hound::WavSpec {
    channels: 1,
    sample_rate,
    bits_per_sample: 16,
    sample_format: hound::SampleFormat::Int,
};

4. Clarify duration in verify_moonshine.rs

Files affected: crates/coldvox-stt/examples/verify_moonshine.rs

The comment says "1s of 440Hz sine wave" but duration_secs = 2. Either update the comment or the variable:

// Generate Test Audio (2s of 440Hz sine wave @ 16kHz)
let duration_secs = 2;

5. Update documentation accuracy

Files affected: docs/issues/pyo3_instability.md

The documentation still references PyO3 0.24 but the upgrade is to 0.27. Consider updating to:

# Issue: PyO3 0.27 Upgrade for Python 3.13 Compatibility (Moonshine Backend)

**Status**: RESOLVED
**Created**: 2025-12-10
**Resolved**: 2025-12-12
**Priority**: High (Blocks stable build on modern Linux distros)

## Problem
PyO3 0.24 introduced breaking changes and strict requirements for Python 3.13 compatibility. This PR upgrades to PyO3 0.27 to resolve these issues and ensure forward compatibility with Python 3.13 free-threading support.

Summary

Critical: 1 issue (missing tokio dependency)
Minor: 5 issues (comments, documentation, minor code improvements)

The PR addresses the core requirement of upgrading PyO3 for Python 3.13 compatibility, and the API migration from with_gil() to attach() appears correct. However, the verify_moonshine.rs example needs the tokio dependency added to compile successfully.

Recommendation: Address the critical tokio dependency issue before merge. The minor issues can be addressed in a follow-up or as part of this PR based on team preference.

The example uses moonshine-specific types that aren't available when
building without the moonshine feature, causing clippy to fail in CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The test was only clearing the `CI` variable but not other CI indicators
like `GITHUB_ACTIONS`, `GITLAB_CI`, etc. This caused the test to fail in
GitHub Actions where `GITHUB_ACTIONS=true` is set.

Now clears all CI-related variables consistent with other tests like
`test_detect_function_basic`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@qodo-code-review
Copy link

qodo-code-review bot commented Dec 12, 2025

CI Feedback 🧐

(Feedback updated until commit c27f96b)

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Whisper E2E (Optional)

Failed stage: Run Whisper Golden Master test [❌]

Failure summary:

The action failed during the installation of Python dependencies. Specifically:
- The pip install
faster-whisper command failed when trying to install the av package (version 12.3.0)
- The av
package requires FFmpeg development libraries to build from source
- The build process failed
because pkg-config could not find the required FFmpeg libraries: libavformat, libavcodec,
libavdevice, libavutil, libavfilter, libswscale, and libswresample
- These libraries need to be
installed on the system (e.g., via apt-get install libavformat-dev libavcodec-dev libavdevice-dev
libavutil-dev libavfilter-dev libswscale-dev libswresample-dev on Ubuntu/Debian) before the Python
package can be built

Relevant error logs:
1:  Runner name: 'laptop-extra'
2:  Runner group name: 'Default'
...

141:  targets: 
142:  components: 
143:  ##[endgroup]
144:  ##[group]Run : set $CARGO_HOME
145:  �[36;1m: set $CARGO_HOME�[0m
146:  �[36;1mecho CARGO_HOME=${CARGO_HOME:-"$HOME/.cargo"} >> $GITHUB_ENV�[0m
147:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
148:  env:
149:  RUSTFLAGS: -D warnings
150:  CARGO_TERM_COLOR: always
151:  WHISPER_MODEL_SIZE: tiny
152:  ##[endgroup]
153:  ##[group]Run : install rustup if needed
154:  �[36;1m: install rustup if needed�[0m
155:  �[36;1mif ! command -v rustup &>/dev/null; then�[0m
156:  �[36;1m  curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused --location --silent --show-error --fail https://sh.rustup.rs | sh -s -- --default-toolchain none -y�[0m
157:  �[36;1m  echo "$CARGO_HOME/bin" >> $GITHUB_PATH�[0m
...

159:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
160:  env:
161:  RUSTFLAGS: -D warnings
162:  CARGO_TERM_COLOR: always
163:  WHISPER_MODEL_SIZE: tiny
164:  CARGO_HOME: /home/coldaine/.cargo
165:  ##[endgroup]
166:  info: downloading installer
167:  warn: It looks like you have an existing installation of Rust at:
168:  warn: /usr/bin
169:  warn: It is recommended that rustup be the primary Rust installation.
170:  warn: Otherwise you may have confusion unless you are careful with your PATH.
171:  warn: If you are sure that you want both rustup and your already installed Rust
172:  warn: then please reply `y' or `yes' or set RUSTUP_INIT_SKIP_PATH_CHECK to yes
173:  warn: or pass `-y' to ignore all ignorable checks.
174:  error: cannot install while Rust is installed
175:  warn: continuing (because the -y flag is set and the error is ignorable)
176:  warn: It looks like you have an existing rustup settings file at:
...

259:  �[36;1m    touch "/home/coldaine/actions-runner/_work/_temp"/.implicit_cargo_registries_crates_io_protocol || true�[0m
260:  �[36;1m    echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse >> $GITHUB_ENV�[0m
261:  �[36;1m  elif rustc +stable --version --verbose | grep -q '^release: 1\.6[67]\.'; then�[0m
262:  �[36;1m    touch "/home/coldaine/actions-runner/_work/_temp"/.implicit_cargo_registries_crates_io_protocol || true�[0m
263:  �[36;1m    echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=git >> $GITHUB_ENV�[0m
264:  �[36;1m  fi�[0m
265:  �[36;1mfi�[0m
266:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
267:  env:
268:  RUSTFLAGS: -D warnings
269:  CARGO_TERM_COLOR: always
270:  WHISPER_MODEL_SIZE: tiny
271:  CARGO_HOME: /home/coldaine/.cargo
272:  CARGO_INCREMENTAL: 0
273:  ##[endgroup]
274:  ##[group]Run : work around spurious network errors in curl 8.0
275:  �[36;1m: work around spurious network errors in curl 8.0�[0m
276:  �[36;1m# https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/timeout.20investigation�[0m
...

368:  - /home/coldaine/actions-runner/_work/ColdVox/ColdVox/crates/coldvox-text-injection/Cargo.toml
369:  - /home/coldaine/actions-runner/_work/ColdVox/ColdVox/crates/coldvox-vad-silero/Cargo.toml
370:  - /home/coldaine/actions-runner/_work/ColdVox/ColdVox/crates/coldvox-vad/Cargo.toml
371:  ##[endgroup]
372:  ... Restoring cache ...
373:  No cache found.
374:  ##[group]Run bash scripts/ci/setup-whisper-cache.sh
375:  �[36;1mbash scripts/ci/setup-whisper-cache.sh�[0m
376:  shell: /usr/bin/bash -e {0}
377:  env:
378:  RUSTFLAGS: -D warnings
379:  CARGO_TERM_COLOR: always
380:  WHISPER_MODEL_SIZE: tiny
381:  CARGO_HOME: /home/coldaine/.cargo
382:  CARGO_INCREMENTAL: 0
383:  CACHE_ON_FAILURE: false
384:  ##[endgroup]
...

409:  Model Path: /home/coldaine/actions-runner/_work/ColdVox/ColdVox/vendor/whisper/model/tiny
410:  Model Size: tiny
411:  Model Identifier: tiny
412:  ✅ Whisper setup complete.
413:  ##[group]Run pip install faster-whisper
414:  �[36;1mpip install faster-whisper�[0m
415:  �[36;1mexport PYTHONPATH=$(python3 -c "import site; print(site.getsitepackages()[0])")�[0m
416:  �[36;1mcargo test -p coldvox-app --test golden_master -- --nocapture�[0m
417:  shell: /usr/bin/bash -e {0}
418:  env:
419:  RUSTFLAGS: -D warnings
420:  CARGO_TERM_COLOR: always
421:  WHISPER_MODEL_SIZE: tiny
422:  CARGO_HOME: /home/coldaine/.cargo
423:  CARGO_INCREMENTAL: 0
424:  CACHE_ON_FAILURE: false
425:  WHISPER_MODEL_PATH: /home/coldaine/actions-runner/_work/ColdVox/ColdVox/vendor/whisper/model/tiny
...

432:  Collecting huggingface-hub>=0.21 (from faster-whisper)
433:  Downloading huggingface_hub-1.2.3-py3-none-any.whl.metadata (13 kB)
434:  Collecting tokenizers<1,>=0.13 (from faster-whisper)
435:  Using cached tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.8 kB)
436:  INFO: pip is looking at multiple versions of faster-whisper to determine which version is compatible with other requirements. This could take a while.
437:  Collecting faster-whisper
438:  Using cached faster_whisper-1.2.0-py3-none-any.whl.metadata (16 kB)
439:  Using cached faster_whisper-1.1.1-py3-none-any.whl.metadata (16 kB)
440:  Using cached faster_whisper-1.1.0-py3-none-any.whl.metadata (16 kB)
441:  Using cached faster_whisper-1.0.3-py3-none-any.whl.metadata (15 kB)
442:  Collecting av<13,>=11.0 (from faster-whisper)
443:  Using cached av-12.3.0.tar.gz (3.8 MB)
444:  Installing build dependencies: started
445:  Installing build dependencies: finished with status 'done'
446:  Getting requirements to build wheel: started
447:  Getting requirements to build wheel: finished with status 'error'
448:  error: subprocess-exited-with-error
449:  × Getting requirements to build wheel did not run successfully.
450:  │ exit code: 1
451:  ╰─> [11 lines of output]
452:  Package libavformat was not found in the pkg-config search path.
453:  Perhaps you should add the directory containing `libavformat.pc'
454:  to the PKG_CONFIG_PATH environment variable
455:  Package 'libavformat' not found
456:  Package 'libavcodec' not found
457:  Package 'libavdevice' not found
458:  Package 'libavutil' not found
459:  Package 'libavfilter' not found
460:  Package 'libswscale' not found
461:  Package 'libswresample' not found
462:  pkg-config could not find libraries ['avformat', 'avcodec', 'avdevice', 'avutil', 'avfilter', 'swscale', 'swresample']
463:  [end of output]
464:  note: This error originates from a subprocess, and is likely not a problem with pip.
465:  error: subprocess-exited-with-error
466:  × Getting requirements to build wheel did not run successfully.
467:  │ exit code: 1
468:  ╰─> See above for output.
469:  note: This error originates from a subprocess, and is likely not a problem with pip.
470:  ##[error]Process completed with exit code 1.
471:  Post job cleanup.

Copy link

@kiloconnect kiloconnect bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No Critical Issues Found

I've reviewed PR #294 and found no critical issues. The PyO3 upgrade from 0.24.1 to 0.27 and API migration from with_gil() to attach() appear to be implemented correctly.

Note: The previous review incorrectly flagged a missing tokio dependency. The verify_moonshine.rs example compiles successfully because tokio is already present in the [dev-dependencies] section of Cargo.toml.

Minor Issues Found

I identified 4 minor issues that should be addressed:

Issue Location Severity
Unused variable in example verify_moonshine.rs:32-37 SUGGESTION
Comment duration mismatch verify_moonshine.rs:39 SUGGESTION
Documentation needs update pyo3_instability.md:1-10 SUGGESTION
Error visibility in .envrc .envrc:16 SUGGESTION

Key Changes Reviewed

PyO3 Upgrade: Correctly upgraded from 0.24.1 to 0.27 with auto-initialize feature
API Migration: All 4 instances of Python::with_gil() properly migrated to Python::attach()
Dependency Updates: Cargo.lock updated correctly for PyO3 0.27 and transitive dependencies
Development Tools: Added .envrc, justfile recipes, and verification example
Documentation: Comprehensive documentation of PyO3/Python 3.13 issues

Recommendation: These changes address the core requirement of upgrading PyO3 for Python 3.13 compatibility. The API migration is correct and the new development tooling will be helpful for testing. Address the minor issues at your discretion.

Comment on lines +32 to +37
let spec = hound::WavSpec {
channels: 1,
sample_rate,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 SUGGESTION: Remove unused variable

The spec variable is declared but never used (lines 32-37). Either use it or remove it:

// Remove this if not needed:
let spec = hound::WavSpec {
    channels: 1,
    sample_rate,
    bits_per_sample: 16,
    sample_format: hound::SampleFormat::Int,
};

Comment on lines +39 to +40
// We create a buffer of samples
let mut samples = Vec::new();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 SUGGESTION: Fix comment accuracy

The comment says "1s of 440Hz sine wave" but duration_secs = 2. Either update the comment or the variable:

// Generate Test Audio (2s of 440Hz sine wave @ 16kHz)
let duration_secs = 2;

Comment on lines +1 to +36
---
doc_type: troubleshooting
subsystem: stt
version: 1.0.0
status: draft
owners: Documentation Working Group
last_reviewed: 2025-12-12
---

# Issue: PyO3 0.24 Instability on Python 3.13 (Moonshine Backend)

**Status**: DRAFT (Local)
**Created**: 2025-12-10
**Priority**: High (Blocks stable build on modern Linux distros)

## Problem
PyO3 0.24 introduces breaking changes and strict requirements for Python 3.13 compatibility, specifically regarding free-threaded builds (GIL removal). This impacts the `moonshine` STT plugin in ColdVox.

## Symptoms
- Build errors on systems with Python 3.13 default (e.g., Arch, Fedora Rawhide).
- Potential runtime panics if `#[pyclass]` structs do not implement `Sync`.
- API deprecations/renames (`Python::with_gil` semantics shifting).

## Findings from Research
1. **Free-Threading (3.13t)**: Python 3.13 supports experimental free-threading. PyO3 0.24 requires `Sync` implementation for all `#[pyclass]` types to support this.
2. **API Churn**: `Python::with_gil` is conceptually deprecated in favor of `Python::attach` in free-threaded contexts, though 0.24 still supports it.
3. **Build Tooling**: Attempting to build against Python 3.13 with older versions (or mismatched feature flags) fails.
4. **Current Config**: `coldvox-stt` uses `pyo3 = "0.24.1"`.

## Impact on ColdVox
`moonshine.rs` uses `Python::with_gil` extensively. If the system Python is 3.13, the build may produce unstable binaries or fail link checks because our `MoonshinePlugin` struct holds `Py<PyAny>` fields that might need `Sync` guards in the new model.

## Recommendation
1. **Short Term**: Pin Python to 3.12 for stability via `.python-version` or `pyenv`.
2. **Code Change**: Audit all `Py<T>` usage in `moonshine.rs` for `Sync` compliance.
3. **Configuration**: Consider enabling `abi3-py313` feature in `Cargo.toml` or setting `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1`.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 SUGGESTION: Update documentation to reflect current state

The documentation still references PyO3 0.24 but the upgrade is to 0.27. Consider updating to:

# Issue: PyO3 0.27 Upgrade for Python 3.13 Compatibility (Moonshine Backend)

**Status**: RESOLVED  
**Created**: 2025-12-10  
**Resolved**: 2025-12-12  
**Priority**: High (Blocks stable build on modern Linux distros)

## Problem
PyO3 0.24 introduced breaking changes and strict requirements for Python 3.13 compatibility. This PR upgrades to PyO3 0.27 to resolve these issues and ensure forward compatibility with Python 3.13 free-threading support.

Comment on lines +16 to +17

# Set PYO3 to use this venv's Python
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 SUGGESTION: Improve error visibility

The --quiet flag on uv sync could hide important errors during development. Consider removing it or adding explicit error handling:

# Ensure dependencies are synced
uv sync || {
    echo "Error: Failed to sync dependencies"
    exit 1
}

@Coldaine
Copy link
Owner Author

Closing as superseded - the PyO3 0.27 upgrade was merged as part of PR #296 (feat: upgrade PyO3 0.27, stabilize CI, and fix real injection tests).

@Coldaine Coldaine closed this Dec 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants