11# Architecture Overview
22
3- Pragmatic layered architecture: CLI → Core → Adapters.
4-
53## Layered Architecture
64
75```
@@ -22,177 +20,20 @@ Adapters Layer (I/O)
2220
2321## Dependency Rule
2422
25- ** Unidirectional flow:** CLI → Core → Adapters
23+ Unidirectional flow: CLI → Core → Adapters
2624
2725- CLI imports Core and Adapters
28- - Core imports nothing (pure logic)
29- - Adapters import nothing (isolated I/O)
30-
31- Benefits: Clean separation, easy testing, framework independence.
32-
33- ## Layer Responsibilities
34-
35- ### CLI Layer
36-
37- User interaction and terminal UI.
38-
39- ** Responsibilities:**
40- - Parse arguments (Typer)
41- - Display output (Rich)
42- - Orchestrate workflows (calls adapters)
43- - Bridge sync CLI to async backend
44-
45- ** Files:** ` cli/app.py ` , ` cli/commands/record.py ` , ` cli/commands/config.py `
46-
47- ### Core Layer
48-
49- Domain models only.
50-
51- ** Responsibilities:**
52- - Define TranscriptionStyle enum
53-
54- ** Files:** ` core/styles.py `
55-
56- ** Note:** Currently minimal. Orchestration happens in CLI layer (pragmatic choice for small app).
57-
58- ### Adapters Layer
59-
60- All external I/O.
61-
62- ** Responsibilities:**
63- - Audio recording (sounddevice)
64- - WAV file I/O (scipy)
65- - Whisper API (AsyncOpenAI)
66- - LLM formatting (PydanticAI)
67- - Clipboard (pyperclip)
68-
69- ** Files:** ` adapters/audio/recorder.py ` , ` adapters/whisper/client.py ` , ` adapters/llm/formatter.py ` , ` adapters/clipboard/manager.py `
26+ - Core imports nothing
27+ - Adapters import nothing
7028
7129## Data Flow
7230
73- ### Recording Workflow
74-
75- ```
76- User runs: shh --style casual --translate English
77-
78- 1. CLI Layer (app.py)
79- ├─ Parse args (Typer)
80- ├─ Validate API key exists
81- └─ Call asyncio.run(record_command(...))
82-
83- 2. CLI Layer (record.py)
84- ├─ Create AudioRecorder
85- ├─ Display progress (Rich Live)
86- └─ Wait for Enter or max duration
87-
88- 3. Adapters Layer (audio/recorder.py)
89- └─ Record audio chunks (sounddevice)
90-
91- 4. Adapters Layer (audio/processor.py)
92- └─ Save to WAV file (scipy)
93-
94- 5. Adapters Layer (whisper/client.py)
95- └─ Transcribe audio (OpenAI Whisper API)
96-
97- 6. Adapters Layer (llm/formatter.py)
98- ├─ Translate to English (PydanticAI + GPT)
99- └─ Format with casual style (PydanticAI + GPT)
100-
101- 7. Adapters Layer (clipboard/manager.py)
102- └─ Copy to clipboard (pyperclip)
103-
104- 8. CLI Layer (record.py)
105- ├─ Display result (Rich Panel)
106- └─ Clean up temp WAV file
10731```
108-
109- ### Configuration Workflow
110-
111- ```
112- User runs: shh config set default_style casual
113-
114- 1. CLI Layer (config.py)
115- ├─ Parse args (key, value)
116- ├─ Validate key exists
117- └─ Validate value is valid enum
118-
119- 2. Config (settings.py)
120- ├─ Load existing config from JSON
121- ├─ Update setting with validation
122- └─ Save to platform-specific path
123- ```
124-
125- ## Configuration Architecture
126-
127- ```
128- ┌─────────────────────────────────────────────┐
129- │ Configuration Priority │
130- │ 1. CLI Flags (--style casual) │
131- │ 2. Environment Vars (SHH_DEFAULT_STYLE) │
132- │ 3. Config File (config.json) │
133- │ 4. Defaults (hardcoded) │
134- └─────────────────────────────────────────────┘
135- │
136- ↓
137- ┌─────────────────────────────────────────────┐
138- │ pydantic-settings (Settings class) │
139- │ • Type validation │
140- │ • Environment variable parsing │
141- │ • Default values │
142- └─────────────────────────────────────────────┘
143- │
144- ↓
145- ┌─────────────────────────────────────────────┐
146- │ Platform-specific Storage │
147- │ macOS: ~/Library/Application Support/ │
148- │ Linux: ~/.config/shh/ │
149- │ Windows: %APPDATA%\shh\ │
150- └─────────────────────────────────────────────┘
151- ```
152-
153- ## Async Architecture
154-
155- All I/O operations use async/await for non-blocking execution and responsive UX.
156-
157- ** Key patterns:**
158- - Async context managers for resource cleanup
159- - Thread pool executors for blocking I/O (stdin)
160- - Task cancellation for graceful shutdowns
161-
162- ``` python
163- async with AudioRecorder() as recorder:
164- await asyncio.sleep(duration)
165- # Cleanup happens automatically
32+ 1. CLI (app.py) - Parse args, validate API key
33+ 2. CLI (record.py) - Display progress, wait for Enter
34+ 3. Adapters (audio) - Record and save WAV
35+ 4. Adapters (whisper) - Transcribe via OpenAI
36+ 5. Adapters (llm) - Translate/format via PydanticAI
37+ 6. Adapters (clipboard) - Copy result
38+ 7. CLI (record.py) - Display result, cleanup
16639```
167-
168- ## Type Safety
169-
170- Full ` mypy --strict ` compliance with type hints on all functions.
171-
172- ** Stack:**
173- - Pydantic models for structured data
174- - Enums for finite choices (TranscriptionStyle)
175- - No ` Any ` without justification
176-
177- ## Error Handling
178-
179- Fail fast with clear messages. Adapters translate external errors to domain exceptions. Resources cleaned up in ` try/finally ` blocks.
180-
181- ``` python
182- try :
183- result = await transcribe_audio(wav_path, api_key)
184- finally :
185- wav_path.unlink(missing_ok = True ) # Always clean up
186- ```
187-
188- ## Testing Strategy
189-
190- Unit tests for core logic, integration tests with mocked APIs. No E2E tests in CI (avoid real API calls).
191-
192- Target: 80%+ code coverage.
193-
194- ## Next Steps
195-
196- - [ Design Decisions] ( design-decisions.md )
197- - [ Testing Architecture] ( testing.md )
198- - [ API Reference] ( ../api-reference/core.md )
0 commit comments