141_Spanish_Assistant/doc/notes.md

798 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [1. Executive Overview](#1-executive-overview)
- [2. System Architecture \& Data Flow](#2-system-architecture--data-flow)
- [2.1. Input Stage:](#21-input-stage)
- [2.2. Inference Stage:](#22-inference-stage)
- [2.3. Synthesis Stage:](#23-synthesis-stage)
- [2.4. Output \& Evaluation Stage:](#24-output--evaluation-stage)
- [3. Key Tech Stack \& Components](#3-key-tech-stack--components)
- [4. User Interface Layout (PySide6)](#4-user-interface-layout-pyside6)
- [5. Core Advantages of This Design](#5-core-advantages-of-this-design)
- [6. Layout Explanation](#6-layout-explanation)
- [7. Code Structure](#7-code-structure)
- [8. Key Technical Recommendations for app/modules/](#8-key-technical-recommendations-for-appmodules)
- [9. Structure Review \& Observations](#9-structure-review--observations)
- [10. Recommended .gitignore](#10-recommended-gitignore)
- [11. Tree Commands](#11-tree-commands)
- [11.1. Excluding noisy hidden folders](#111-excluding-noisy-hidden-folders)
- [11.2. Setting up Git](#112-setting-up-git)
- [11.3. Step-by-Step Commands](#113-step-by-step-commands)
- [12. Can you help me write the pyproject.toml file for uv including PySide6, mlx-whisper, and Kokoro?](#12-can-you-help-me-write-the-pyprojecttoml-file-for-uv-including-pyside6-mlx-whisper-and-kokoro)
- [13. How to Initialize \& Install using uv](#13-how-to-initialize--install-using-uv)
- [13.0.1. Note on Python Version:](#1301-note-on-python-version)
- [13.1. Errors and Fixes](#131-errors-and-fixes)
- [13.1.1. Step-by-Step Resolution Commands](#1311-step-by-step-resolution-commands)
- [13.1.2. I got the following errors uv sync](#1312-i-got-the-following-errors-uv-sync)
- [Create basic PySide6 boilerplate for main.py, main\_window.py, and the tab modules](#create--basic-pyside6-boilerplate-for-mainpy-main_windowpy-and-the-tab-modules)
# 1. Executive Overview
The project is a 100% offline, privacy-first Spanish speech practice assistant running natively on an Apple MacBook Pro M3. It creates a low-latency conversational feedback loop where you speak in Spanish, receive a generated audio response from a local LLM, and get automated feedback comparing your spoken pronunciation against the expected text.
# 2. System Architecture & Data Flow
```zsh
┌────────────────┐ ┌─────────────────────┐ ┌──────────────────────┐
│ Mic Input │ ──> │ mlx-whisper (STT) │ ──> │ PySide6 GUI │
└────────────────┘ └─────────────────────┘ └──────────┬───────────┘
┌────────────────┐ ┌─────────────────────┐ ┌──────────────────────┐
│ Mac Speakers │ <── │ Kokoro-82M (TTS) │ <── │ Gemma 4 (llama.cpp)
└────────────────┘ └─────────────────────┘ └──────────┬───────────┘
┌──────────────────────┐
│ Feedback & SQLite DB │
└──────────────────────┘
```
## 2.1. Input Stage:
Your speech is recorded through the MacBook Pro's built-in microphone array and transcribed to Spanish text using mlx-whisper.
## 2.2. Inference Stage:
Transcribed text (along with recent session history from SQLite) is sent via HTTP to a local llama.cpp server running Gemma 4.
## 2.3. Synthesis Stage:
Gemmas response is passed to Kokoro-82M, generating high-quality Spanish speech audio offline.
## 2.4. Output & Evaluation Stage:
Audio plays directly through your MacBook speakers via sounddevice. Concurrently, a feedback engine compares your spoken text against target phrases to highlight pronunciation accuracy and tracks your progress in SQLite.
# 3. Key Tech Stack & Components
|Component|Selected Technology|Role & Key Features|
|---------|-------------------|-------------------|
|Package Management|uv|"Fast Python environment setup, dependency management, and script execution (uv run main.py)."|
|GUI Framework|PySide6|Multi-tab desktop application using QtAsyncio to keep network and audio processes non-blocking.|
|LLM Server|Gemma 4 via llama.cpp|Local LLM inference server hosted on localhost:8080 with Metal GPU acceleration.|
|Speech-to-Text (STT)|mlx-whisper|"Apple Silicon-optimized Whisper engine utilizing the MLX framework for fast, accurate Spanish transcription."|
|Text-to-Speech (TTS)|Kokoro-82M|"Open-weight (Apache 2.0)| 82M parameter offline TTS engine generating 24kHz Spanish audio."|
|Feedback Engine,feedback_engine.py|"Calculates word error rates (WER), sequence similarity, and mispronounced/dropped words."|
|Data Storage|SQLite3|"Local relational database storing chat sessions| transcripts, system logs, and pronunciation scores."|
|Dev Environment|VS Code + Forgejo|Managed locally with self-hosted Git on Forgejo.|
# 4. User Interface Layout (PySide6)
The GUI consists of two main tabs:
* Tab 1: Conversación (Live Chat & Practice)
* Displays the streaming conversation transcript between you and the Assistant.
* Shows visual feedback cards after each spoken turn (e.g., Pronunciation Accuracy Score %, highlighted missing words, and accent notes).
* Tab 2: Control & Logs
* Displays system status (llama.cpp server state, latency metrics).
* Includes an embedded SQLite table viewer to inspect past conversation history and track language progress over time.
# 5. Core Advantages of This Design
* Zero Cloud Dependencies: Completely offline—no API costs, no network latency, and total privacy for your audio data.
* Hardware Optimized: Leverages your M3 Mac's Unified Memory, Metal GPU, and Neural Engine for sub-second pipeline processing.
* Clean Open-Source Licensing: Replaces proprietary components (like edge-tts) with permissive open-weight/open-source alternatives (Apache 2.0 / LGPL / MIT).
```zsh
+-------------------------------------------------------------------------------------------------------+
| SPANISH VOICE PRACTICE AI [PySide6] [ _ ] [ X ] |
+-------------------------------------------------------------------------------------------------------+
| [ CONVERSACIÓN (Practice) ] [ CONTROL & LOGS (Settings) ] |
+-------------------------------------------------------------------------------------------------------+
| |
| [TAB 1: CONVERSACIÓN] |
| |
| +-------------------------------------------------------------------------------------------------+ |
| | [CHAT DISPLAY - QTextEdit (Read-Only)] | |
| | | |
| | [User (Whisper)]: Hola Gemma, ¿cómo puedo mejorar mi acento español? | |
| | | |
| | [Assistant (Kokoro)]: ¡Hola! La mejor forma de mejorar es practicar en voz alta todos los días. | |
| | Intenta imitar mi entonación. | |
| | | |
| | [User (Whisper)]: ¿Puedes darme una frase para practica? | |
| | | |
| +-------------------------------------------------------------------------------------------------+ |
| |
| +-------------------------------------------------------------------------------------------------+ |
| | [FEEDBACK PANEL - QFrame (Visible after speaking)] | |
| | | |
| | Pronunciation Score: [ 88% ] [|||||||||| ] (Good) | |
| | -------------------------------------------------- | |
| | Target Text: "¿Puedes darme una frase para practica<font color='red'>r</font>?" | |
| | Spoken Text: "¿Puedes darme una frase para practica<font color='gray'>_</font>?" | |
| | -------------------------------------------------- | |
| | [i] Notes: Watch your word endings (verb infinitives like 'practicar'). | |
| +-------------------------------------------------------------------------------------------------+ |
| |
| +-------------------------------------------------------------------------------------------------+ |
| | [INPUT AREA] | |
| | | |
| | [ HOLD SPACE TO SPEAK / Type your prompt... ] [ RECORD (O) ] [ SEND (->) ] | |
| | | |
| +-------------------------------------------------------------------------------------------------+ |
| |
+-------------------------------------------------------------------------------------------------------+
| System Status: Assistant Speaking... [DB Session: 104] |
+-------------------------------------------------------------------------------------------------------+
```
# 6. Layout Explanation
1. Main Window Structure
* Frameless/Standard QMainWindow: A clean modern window titled "Spanish Voice Practice AI."
* QTabWidget: The core navigation anchor.
* Tab 1 (Conversación): The main interaction zone shown above.
* Tab 2 (Control & Logs): (Not pictured) Would contain database viewing, LLM parameter sliders (temperature, max tokens), and system performance logs (latency checks).
2. Chat Display (QTextEdit)
* Displays a scrolling log of the conversation.
* Uses rich text formatting to differentiate between User, Assistant, and any system alerts.
* Transcripts from mlx-whisper are inserted here automatically.
3. Feedback Panel (QFrame)
* This panel appears (or updates) dynamically after the user finishes speaking and Whisper transcribes the audio.
* It displays the results generated by the PronunciationFeedbackEngine:
* Visual Score Bar: A colored bar/meter showing accuracy.
* Diff View: Uses HTML color coding (e.g., Red for omissions, Green for correct words) to visually compare Gemma's target text against the actual spoken transcription.
4. Input Area (QHBoxLayout)
* Combined Input: Primarily focused on voice, but includes a text fallback.
* Voice Trigger: A prominent visual button (RECORD (O)) and a keyboard shortcut (e.g., holding Spacebar) to initiate recording via the STTEngine.
* Send Button: A classic arrow icon for sending text input or manually submitting a recorded chunk.
5. Status Bar (QStatusBar)
* Provides real-time feedback on what the background systems are doing (e.g., "Whisper Transcribing...", "Gemma Generating...", "Kokoro Speaking...", "Ready").
* Displays the current active SQLite database session ID.
# 7. Code Structure
1. GUI Framework Alignment: Youve migrated this project to PySide6 (as noted in your comment), but double-check that your UI imports and sub-classes across main.py, main_window.py, chat_tab.py, and control_tab.py are strictly using PySide6.QtWidgets rather than PyQt6.
2. Local macOS Optimization Opportunities: Since you are running locally on Apple Silicon (M3), using standard cloud/hybrid pipelines (edge-tts) or baseline CPU bindings might introduce unnecessary network latency or underutilize your Unified Memory.
## Tree Structure
```zsh
spanish-assistant/
├── pyproject.toml # UV dependency management (PySide6, mlx-whisper, etc.)
├── app/
│ ├── __init__.py
│ ├── main.py # PySide6 App entrypoint (QApplication)
│ ├── db.py # SQLite3 Context Manager & Repository pattern
│ ├── modules/
│ │ ├── llm_controller.py # Subprocess / HTTP client for local llama.cpp server
│ │ ├── stt_engine.py # Speech-to-Text (mlx-whisper / PyAudio capture)
│ │ └── tts_engine.py # Text-to-Speech (Local Kokoro-TTS or edge-tts fallback)
│ └── ui/
│ ├── main_window.py # Main PySide6 QMainWindow / QTabWidget
│ ├── chat_tab.py # Tab 1: Interactive Spanish audio/text interface
│ └── control_tab.py # Tab 2: Logs, DB Inspector, and local LLM parameter controls
└── data/
└── conversation_history.db # Local SQLite database storage
```
# 8. Key Technical Recommendations for app/modules/
1. Speech-to-Text (stt_engine.py)
* Apple Silicon Edge: If you are currently using standard openai-whisper or whisper.cpp Python bindings, consider using mlx-whisper. It leverages Apple's MLX framework to run Whisper directly on the M3 Neural Engine/GPU with significantly lower latency and minimal CPU impact.
2. Text-to-Speech (tts_engine.py)
* Offline Native TTS: edge-tts is simple and fast, but it requires an active internet connection to stream Microsoft's cloud endpoints.
* If you want a 100% offline local setup for Spanish voice practice, look into Kokoro-82M (e.g., via kokoro or kokoro-onnx). It supports high-quality, natural Spanish pronunciation locally on macOS without needing cloud requests.
3. Database Layer (db.py)
* Make sure your db.py exposes thread-safe connection handling (or a repository pattern) so background Qt threads (QThread) handling STT/TTS don't crash SQLite when logging conversations while the UI is rendering tables.
```Plaitext
spanish-assistant/
├── pyproject.toml # Managed with UV (PySide6, mlx-whisper, kokoro, sounddevice, etc.)
├── app/
│ ├── __init__.py
│ ├── main.py # Launches Spanish Assistant (QApplication + QtAsyncio)
│ ├── db.py # SQLite3 conversation history & progress tracker
│ ├── modules/
│ │ ├── llm_controller.py # Background process manager for llama.cpp (Gemma 4)
│ │ ├── stt_engine.py # Speech-to-Text via mlx-whisper
│ │ ├── tts_engine.py # Local 100% offline Spanish TTS via Kokoro-82M
│ │ └── feedback_engine.py # Pronunciation & WER accuracy comparison module
│ └── ui/
│ ├── main_window.py # Main QMainWindow shell
│ ├── chat_tab.py # Tab 1: Live conversation & real-time feedback card
│ └── control_tab.py # Tab 2: System logs, DB inspector, and LLM controls
└── data/
└── conversation_history.db # Session logs & speech analytics storage
```
```zsh
141_Spanish_Assistant % tree
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── modules
│ │ ├── feedback_engine.py
│ │ ├── llm_controller.py
│ │ ├── stt_engine.py
│ │ └── tts_engine.py
│ └── ui
│ ├── chat_tab.py
│ ├── control_tab.py
│ └── main_window.py
├── data
└── doc
├── images
└── notes.md
```
# 9. Structure Review & Observations
* Python Packages: You have app/__init__.py, which turns app into a package. If you plan to import modules within chat_tab.py using absolute imports like from app.modules.llm_controller import LLMController, ensure you run your entry point (main.py) from the repository root (e.g., python -m app.main). Alternatively, consider adding an empty __init__.py inside app/modules and app/ui if you run into any relative import issues down the road.
* Data Directory: Keeping data/ at the root is ideal for local database storage (like SQLite/PostgreSQL dumps), audio cache files, or user session states.
* Docs & Assets: Placing screenshots or UI reference mockups in doc/images/ and markdown notes in doc/notes.md keeps the repository root tidy.
# 10. Recommended .gitignore
```zsh
# ==========================================
# macOS System Files
# ==========================================
.DS_Store
.AppleDouble
.LSOverride
Icon?
._*
# ==========================================
# Python & Bytecode
# ==========================================
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
# ==========================================
# Virtual Environments
# ==========================================
.venv/
venv/
ENV/
env/
env.bak/
venv.bak/
# ==========================================
# Application Data & Cache
# ==========================================
# Keep the data/ directory structure, but ignore dynamic files inside it
data/*
!data/.gitkeep
# Local log files and audio caches
*.log
*.wav
*.mp3
*.ogg
# ==========================================
# Environment & Secrets
# ==========================================
.env
.env.local
*.pem
# ==========================================
# IDEs & Editors
# ==========================================
.vscode/
.idea/
*.swp
*.swo
```
# 11. Tree Commands
To tell tree to display hidden files, pass the -a (all) flag:
```zsh
tree -a
```
## 11.1. Excluding noisy hidden folders
If you run tree -a, it will also crawl into the .git directory, which prints a massive, messy wall of internal Git objects.
To see your .gitkeep and .gitignore without cluttering the output with .git/, use the -I (ignore) flag:
```zsh
tree -a -I '.git'
```
```zsh
.
├── .gitignore
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── modules
│ │ ├── feedback_engine.py
│ │ ├── llm_controller.py
│ │ ├── stt_engine.py
│ │ └── tts_engine.py
│ └── ui
│ ├── chat_tab.py
│ ├── control_tab.py
│ └── main_window.py
├── data
│ └── .gitkeep
└── doc
├── images
└── notes.md
```
```zsh
stephenlohning@Scotty 141_Spanish_Assistant % tree -a
.
├── .git
│ ├── HEAD
│ ├── config
│ ├── description
│ ├── hooks
│ │ ├── applypatch-msg.sample
│ │ ├── commit-msg.sample
│ │ ├── fsmonitor-watchman.sample
│ │ ├── post-update.sample
│ │ ├── pre-applypatch.sample
│ │ ├── pre-commit.sample
│ │ ├── pre-merge-commit.sample
│ │ ├── pre-push.sample
│ │ ├── pre-rebase.sample
│ │ ├── pre-receive.sample
│ │ ├── prepare-commit-msg.sample
│ │ ├── push-to-checkout.sample
│ │ ├── sendemail-validate.sample
│ │ └── update.sample
│ ├── info
│ │ └── exclude
│ ├── objects
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ └── tags
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── modules
│ │ ├── feedback_engine.py
│ │ ├── llm_controller.py
│ │ ├── stt_engine.py
│ │ └── tts_engine.py
│ └── ui
│ ├── chat_tab.py
│ ├── control_tab.py
│ └── main_window.py
├── data
│ └── .gitkeeper
└── doc
├── images
└── notes.md
16 directories, 29 files
stephenlohning@Scotty 141_Spanish_Assistant %
```
## 11.2. Setting up Git
* Setting __push.followTags = true__ changes default behavior so that whenever you run git push, Git automatically pushes annotated tags that point to commits reachable from the branch you are pushing.
## 11.3. Step-by-Step Commands
Run these commands in order in your terminal:
```zsh
# 1. Stage all your initial project files and .gitignore
git add .
# 2. Make your first commit (this resolves HEAD and creates your branch)
git commit -m "Initial project structure and config"
# 3. Ensure your active branch is named 'main'
git branch -M main
# 4. Now create your annotated tag on the new commit
git tag -a v0.1.0 -m "Initial project structure and config"
# 5. Push your branch and tag up to Forgejo
git push -u origin main
```
After running git commit, git branch will display * main, and your git tag command will succeed!
# 12. Can you help me write the pyproject.toml file for uv including PySide6, mlx-whisper, and Kokoro?
Here is a complete, production-ready pyproject.toml file tailored specifically for 141_Spanish_Assistant using uv.
It includes:
* PySide6 for the GUI.
* mlx-whisper for Apple Silicon GPU/Neural Engine accelerated speech-to-text.
* kokoro-onnx (and soundfile) for fast, local, 100% offline Kokoro-82M TTS.
* sounddevice and numpy for real-time audio capture and playback.
* requests to communicate with your local llama.cpp server hosting Gemma 4.
* editdistance / jiwer for pronunciation accuracy and Word Error Rate (WER) evaluations in feedback_engine.py.pyproject.toml
*
Create or overwrite pyproject.toml in the root of 141_Spanish_Assistant:
```zsh
[project]
name = "spanish-assistant"
version = "0.1.0"
description = "Local, offline Spanish speech practice assistant running on Apple Silicon"
readme = "README.md"
requires-python = ">=3.10,<3.13"
authors = [
{ name = "Stephen", email = "stephen.lohning@oxnee.com" }
]
dependencies = [
# GUI Framework
"pyside6>=6.6.0",
# Local Speech-to-Text (Apple MLX Engine)
"mlx-whisper>=0.2.0",
# Local Text-to-Speech (Kokoro 82M via ONNX Runtime)
"kokoro-onnx>=0.5.0",
"soundfile>=0.12.1",
# Audio Recording & Playback
"sounddevice>=0.4.6",
"numpy>=1.24.0,<2.0.0",
# HTTP Client for local llama.cpp server
"requests>=2.31.0",
# Pronunciation Feedback & Text Analytics
"editdistance>=0.8.0",
"jiwer>=3.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.uv]
# Ensures dependencies stay isolated and locked cleanly on macOS
dev-dependencies = [
"pytest>=8.0.0",
"black>=24.0.0",
]
```
# 13. How to Initialize & Install using uv
Run these commands in your terminal from the root directory:
```zsh
# 1. Create a Python 3.12 virtual environment managed by uv
uv venv --python 3.12
# 2. Sync and install all dependencies defined in pyproject.toml
uv sync
# 3. Test that the environment loads cleanly
uv run python -c "import PySide6, mlx_whisper, kokoro_onnx; print('All core modules imported successfully!')"
```
### 13.0.1. Note on Python Version:
* requires-python is set to <3.13 because several core MLX and ONNX bindings used on Apple Silicon run most reliably on Python 3.103.12.
```zsh
stephenlohning@Scotty 141_Spanish_Assistant % uv venv --python 3.12
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
Using CPython 3.12.12 interpreter at: /opt/homebrew/opt/python@3.12/bin/python3.12
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
stephenlohning@Scotty 141_Spanish_Assistant % uv sync
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
× No solution found when resolving dependencies:
╰─▶ Because only kokoro-onnx<=0.5.0 is available and kokoro-onnx==0.5.0 depends on numpy>=2.0.2, we can conclude that kokoro-onnx>=0.5.0 depends on numpy>=2.0.2.
And because your project depends on kokoro-onnx>=0.5.0 and numpy>=1.24.0,<2.0.0, we can conclude that your project's requirements are unsatisfiable.
stephenlohning@Scotty 141_Spanish_Assistant % source .venv/bin/activate
(141_Spanish_Assistant) stephenlohning@Scotty 141_Spanish_Assistant %
```
## 13.1. Errors and Fixes
```zsh
I got the following comments
stephenlohning@Scotty 141_Spanish_Assistant % uv venv --python 3.12
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
Using CPython 3.12.12 interpreter at: /opt/homebrew/opt/python@3.12/bin/python3.12
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
stephenlohning@Scotty 141_Spanish_Assistant % uv sync
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
× No solution found when resolving dependencies:
╰─▶ Because only kokoro-onnx<=0.5.0 is available and kokoro-onnx==0.5.0 depends on numpy>=2.0.2, we can conclude that kokoro-onnx>=0.5.0 depends on numpy>=2.0.2.
And because your project depends on kokoro-onnx>=0.5.0 and numpy>=1.24.0,<2.0.0, we can conclude that your project's requirements are unsatisfiable.
stephenlohning@Scotty 141_Spanish_Assistant % source .venv/bin/activate
(141_Spanish_Assistant) stephenlohning@Scotty 141_Spanish_Assistant % uv run python -c "import PySide6, mlx_whisper, kokoro_onnx; print('All core modules imported successfully!')"
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
× No solution found when resolving dependencies:
╰─▶ Because only kokoro-onnx<=0.5.0 is available and kokoro-onnx==0.5.0 depends on numpy>=2.0.2, we can conclude that kokoro-onnx>=0.5.0 depends on numpy>=2.0.2.
And because your project depends on kokoro-onnx>=0.5.0 and numpy>=1.24.0,<2.0.0, we can conclude that your project's requirements are unsatisfiable.
(141_Spanish_Assistant) stephenlohning@Scotty 141_Spanish_Assistant %
```
This is a dependency conflict between kokoro-onnx and numpy, combined with an updated syntax rule in modern uv.
Here is why it failed and how to fix it:
1. numpy Conflict: kokoro-onnx>=0.5.0 explicitly requires numpy>=2.0.2. The pyproject.toml constrained numpy to <2.0.0, creating an impossible set of rules for uv to resolve.
2. uv Deprecation Warning: In recent uv versions, dev dependencies belong under [dependency-groups] rather than [tool.uv.dev-dependencies].
The Fixed pyproject.toml
Replace your root pyproject.toml with this updated version:
```zsh
[project]
name = "spanish-assistant"
version = "0.1.0"
description = "Local, offline Spanish speech practice assistant running on Apple Silicon"
readme = "README.md"
requires-python = ">=3.10,<3.13"
authors = [
{ name = "Stephen", email = "stephen.lohning@oxnee.com" }
]
dependencies = [
# GUI Framework
"pyside6>=6.6.0",
# Local Speech-to-Text (Apple MLX Engine)
"mlx-whisper>=0.2.0",
# Local Text-to-Speech (Kokoro 82M via ONNX Runtime)
"kokoro-onnx>=0.5.0",
"soundfile>=0.12.1",
# Audio Recording & Playback (Compatible with NumPy 2.x)
"sounddevice>=0.4.6",
"numpy>=2.0.2",
# HTTP Client for local llama.cpp server
"requests>=2.31.0",
# Pronunciation Feedback & Text Analytics
"editdistance>=0.8.0",
"jiwer>=3.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
# Updated syntax for dev dependencies in modern uv
[dependency-groups]
dev = [
"pytest>=8.0.0",
"black>=24.0.0",
]
```
### 13.1.1. Step-by-Step Resolution Commands
Run these steps in your terminal to clear out the old resolution state and sync cleanly:
```zsh
# 1. Sync dependencies with the updated pyproject.toml
uv sync
# 2. Test the core module imports
uv run python -c "import PySide6, mlx_whisper, kokoro_onnx; print('All core modules imported successfully!')"
```
Once uv sync finishes, you will see All core modules imported successfully! and your .venv will be ready for development.
### 13.1.2. I got the following errors uv sync
```zsh
stephenlohning@Scotty 141_Spanish_Assistant % uv sync
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
× No solution found when resolving dependencies:
╰─▶ Because only kokoro-onnx<=0.5.0 is available and kokoro-onnx==0.5.0 depends on numpy>=2.0.2, we can conclude that kokoro-onnx>=0.5.0 depends on numpy>=2.0.2.
And because your project depends on kokoro-onnx>=0.5.0 and numpy>=1.24.0,<2.0.0, we can conclude that your project's requirements are unsatisfiable.
stephenlohning@Scotty 141_Spanish_Assistant % source .venv/bin/activate
(141_Spanish_Assistant) stephenlohning@Scotty 141_Spanish_Assistant % uv run python -c "import PySide6, mlx_whisper, kokoro_onnx; print('All core modules imported successfully!')"
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
× No solution found when resolving dependencies:
╰─▶ Because only kokoro-onnx<=0.5.0 is available and kokoro-onnx==0.5.0 depends on numpy>=2.0.2, we can conclude that kokoro-onnx>=0.5.0 depends on numpy>=2.0.2.
And because your project depends on kokoro-onnx>=0.5.0 and numpy>=1.24.0,<2.0.0, we can conclude that your project's requirements are unsatisfiable.
(141_Spanish_Assistant) stephenlohning@Scotty 141_Spanish_Assistant % uv sync
Resolved 109 packages in 1.52s
× Failed to build `spanish-assistant @ file:///Users/stephenlohning/Documents/01_Projects/141_Spanish_Assistant`
├─▶ The build backend returned an error
╰─▶ Call to `hatchling.build.build_editable` failed (exit status: 1)
[stderr]
Traceback (most recent call last):
File "<string>", line 11, in <module>
File "/Users/stephenlohning/.cache/uv/builds-v0/.tmpOvMudK/lib/python3.12/site-packages/hatchling/build.py", line 83, in build_editable
return os.path.basename(next(builder.build(directory=wheel_directory, versions=["editable"])))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/stephenlohning/.cache/uv/builds-v0/.tmpOvMudK/lib/python3.12/site-packages/hatchling/builders/plugin/interface.py", line 92, in build
self.metadata.validate_fields()
File "/Users/stephenlohning/.cache/uv/builds-v0/.tmpOvMudK/lib/python3.12/site-packages/hatchling/metadata/core.py", line 266, in validate_fields
self.core.validate_fields()
File "/Users/stephenlohning/.cache/uv/builds-v0/.tmpOvMudK/lib/python3.12/site-packages/hatchling/metadata/core.py", line 1437, in validate_fields
getattr(self, attribute)
File "/Users/stephenlohning/.cache/uv/builds-v0/.tmpOvMudK/lib/python3.12/site-packages/hatchling/metadata/core.py", line 533, in readme
raise OSError(message)
OSError: Readme file does not exist: README.md
hint: Build failures usually indicate a problem with the package or the build environment%
(141_Spanish_Assistant) stephenlohning@Scotty 141_Spanish_Assistant %
```
There were two independent issues happening in your terminal output:
Resolution Failure (Fixed): The first run failed because of the numpy constraint conflict.
Build Failure (README.md Missing): The second run succeeded in resolving all 109 packages, but failed at the build step because pyproject.toml declared readme = "README.md", but no README.md file existed in your project root yet.
Additionally, because your Python code lives inside an app/ folder rather than a src/ or spanish_assistant/ package folder, Hatchling (the build backend) needs a small configuration hint to know what to include in editable mode (uv sync).
Step 1: Create a placeholder README.md
Run this in your terminal from the project root:
``zsh
touch README.md
echo "# 141 Spanish Assistant" > README.md
```
Step 2: Use the Updated pyproject.tomlUpdate your root pyproject.toml with this version. It configures [tool.hatch.build.targets.wheel] so Hatchling cleanly packages the app/ directory without throwing editable build errors:
```zsh
[project]
name = "spanish-assistant"
version = "0.1.0"
description = "Local, offline Spanish speech practice assistant running on Apple Silicon"
readme = "README.md"
requires-python = ">=3.10,<3.13"
authors = [
{ name = "Stephen", email = "stephen.lohning@oxnee.com" }
]
dependencies = [
# GUI Framework
"pyside6>=6.6.0",
# Local Speech-to-Text (Apple MLX Engine)
"mlx-whisper>=0.2.0",
# Local Text-to-Speech (Kokoro 82M via ONNX Runtime)
"kokoro-onnx>=0.5.0",
"soundfile>=0.12.1",
# Audio Recording & Playback
"sounddevice>=0.4.6",
"numpy>=2.0.2",
# HTTP Client for local llama.cpp server
"requests>=2.31.0",
# Pronunciation Feedback & Text Analytics
"editdistance>=0.8.0",
"jiwer>=3.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
# Configures Hatchling to recognise the app/ directory
[tool.hatch.build.targets.wheel]
packages = ["app"]
[dependency-groups]
dev = [
"pytest>=8.0.0",
"black>=24.0.0",
]
```
Step 3: Run uv sync and Test
Now sync and verify:
```zsh
uv sync
```
```zsh
uv run python -c "import PySide6, mlx_whisper, kokoro_onnx; print('All core modules imported successfully!')"
```
You should see All core modules imported successfully!, with zero warnings or errors. Which we did
git tag -a v0.1.1 -m "uv set all modules loaded "
# Create basic PySide6 boilerplate for main.py, main_window.py, and the tab modules
Here is a complete set of boilerplate code tailored specifically for 141_Spanish_Assistant.
It uses PySide6.QtAsyncio for non-blocking execution (allowing STT, TTS, and LLM calls to run seamlessly in the background without freezing the GUI).
1. Entry Point: app/main.py
This file initializes the PySide6 application, creates the MainWindow, and delegates execution to QtAsyncio.