Skip to content

Commit 2370a22

Browse files
committed
batchalign3 initial.
1 parent 614a02d commit 2370a22

809 files changed

Lines changed: 130717 additions & 29215 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo/audit.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# RustSec audit configuration
2+
# Used by rustsec/audit-check@v2 in CI
3+
4+
[advisories]
5+
ignore = [
6+
# rsa crate timing sidechannel (Marvin Attack). No patch available.
7+
# Transitive dep — not exploitable in our context.
8+
"RUSTSEC-2023-0071",
9+
]
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
---
2+
name: add-command
3+
description: Scaffold a new batchalign3 CLI command end-to-end (Rust CLI + server orchestration + Python worker integration). Use when adding a new analysis/processing command.
4+
disable-model-invocation: true
5+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent
6+
---
7+
8+
# Add a New Batchalign3 Command
9+
10+
Scaffold a new CLI command through all layers. `$ARGUMENTS` should specify the command name and description (e.g., `/add-command sentiment "Sentiment analysis on utterances"`).
11+
12+
## Architecture
13+
14+
```
15+
CLI (batchalign-cli) → Server (batchalign-app) → Worker IPC → Python inference module
16+
```
17+
18+
## Step 1: Determine Command Type
19+
20+
| Type | Example | Python Worker? | Orchestration |
21+
|------|---------|---------------|---------------|
22+
| **ML inference** | morphotag, utseg, translate | Yes — needs inference module | Server extracts words → worker infers → server injects results |
23+
| **Audio processing** | transcribe, align | Yes — needs inference module | Server sends audio path → worker returns segments |
24+
| **File processing** | opensmile, avqi | Yes — uses `process` op | Worker processes entire file |
25+
| **Rust-only** | validate, normalize | No | Pure Rust, no worker needed |
26+
27+
## Step 2: Add CLI Subcommand
28+
29+
**File:** `crates/batchalign-cli/src/cli/args.rs`
30+
31+
```bash
32+
grep -n "enum Commands" crates/batchalign-cli/src/cli/args.rs
33+
```
34+
35+
Add a new variant to the `Commands` enum with clap attributes.
36+
37+
**File:** `crates/batchalign-cli/src/commands/`
38+
39+
Create a new module for the command dispatch logic. Read an existing command for the pattern:
40+
41+
```bash
42+
ls crates/batchalign-cli/src/commands/
43+
```
44+
45+
## Step 3: Add Server Orchestration (if ML inference)
46+
47+
**File:** `crates/batchalign-app/src/`
48+
49+
Create an orchestration module that:
50+
1. Parses CHAT using `batchalign-chat-ops`
51+
2. Extracts relevant data (words, audio paths, etc.)
52+
3. Calls the Python worker via `batch_infer` IPC
53+
4. Injects results back into the CHAT AST
54+
5. Serializes the modified CHAT
55+
56+
Read existing orchestrators for the pattern:
57+
58+
```bash
59+
ls crates/batchalign-app/src/*.rs
60+
```
61+
62+
## Step 4: Add Worker Types
63+
64+
**File:** `batchalign/worker/_types.py`
65+
66+
Add a new `InferTask` variant matching the Rust enum:
67+
68+
```bash
69+
grep -n "class InferTask" batchalign/worker/_types.py
70+
```
71+
72+
Add Pydantic request/response models for the new task's input/output.
73+
74+
**Rust side:** Add matching types to `batchalign-types::worker` (if it exists in the workspace).
75+
76+
## Step 5: Add Inference Module (if ML inference)
77+
78+
**File:** `batchalign/inference/<name>.py`
79+
80+
Create a new inference module following the pattern:
81+
82+
```python
83+
def load_<name>_model(lang: str) -> ModelType:
84+
"""Load the ML model. Called once at worker startup."""
85+
...
86+
87+
def batch_infer_<name>(model: ModelType, items: list[InputType]) -> list[OutputType]:
88+
"""Pure inference function. No CHAT, no domain logic."""
89+
...
90+
```
91+
92+
Read existing inference modules for the exact pattern:
93+
94+
```bash
95+
ls batchalign/inference/
96+
head -40 batchalign/inference/morphosyntax.py
97+
```
98+
99+
Key rules:
100+
- Heavy imports (torch, stanza) must be lazy
101+
- Return raw model output — no CHAT text processing
102+
- Use Pydantic models for structured I/O
103+
- Type annotations on all functions
104+
105+
## Step 6: Wire Worker Dispatch
106+
107+
**File:** `batchalign/worker/_infer.py`
108+
109+
Add a case to the `batch_infer` dispatch router:
110+
111+
```bash
112+
grep -n "def batch_infer" batchalign/worker/_infer.py
113+
```
114+
115+
**File:** `batchalign/worker/_main.py`
116+
117+
Add model loading for the new command:
118+
119+
```bash
120+
grep -n "def load_models" batchalign/worker/_main.py
121+
```
122+
123+
## Step 7: Add CHAT Operations (if needed)
124+
125+
**File:** `crates/batchalign-chat-ops/src/`
126+
127+
If the command needs to extract data from or inject results into CHAT:
128+
129+
```bash
130+
ls crates/batchalign-chat-ops/src/
131+
```
132+
133+
Follow existing extraction/injection patterns. Use the content walker for AST traversal.
134+
135+
## Step 8: Add Tests
136+
137+
```bash
138+
# Python inference test
139+
cat > batchalign/tests/test_<name>.py
140+
141+
# Rust integration test
142+
# Add to crates/batchalign-app/tests/
143+
144+
# Worker protocol test (manually)
145+
uv run python -m batchalign.worker --command <name> --lang eng
146+
# Then paste: {"op": "capabilities", "id": "test-1"}
147+
```
148+
149+
## Step 9: Verify
150+
151+
```bash
152+
# Python tests
153+
cd $REPO_ROOT && uv run pytest batchalign/tests/test_<name>.py -v
154+
155+
# Rust compile
156+
cd $REPO_ROOT && cargo check --workspace
157+
158+
# Rust tests
159+
cd $REPO_ROOT && cargo test --workspace
160+
161+
# Type check
162+
cd $REPO_ROOT && uv run mypy batchalign/inference/<name>.py batchalign/worker/
163+
```
164+
165+
## Key Files
166+
167+
| Purpose | Path |
168+
|---------|------|
169+
| CLI args definition | `crates/batchalign-cli/src/cli/args.rs` |
170+
| CLI command dispatch | `crates/batchalign-cli/src/commands/` |
171+
| Server orchestration | `crates/batchalign-app/src/` |
172+
| Worker types (Pydantic) | `batchalign/worker/_types.py` |
173+
| Worker dispatch | `batchalign/worker/_infer.py` |
174+
| Worker model loading | `batchalign/worker/_main.py` |
175+
| Inference modules | `batchalign/inference/` |
176+
| CHAT operations | `crates/batchalign-chat-ops/src/` |
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
---
2+
name: add-inference
3+
description: Add a new Python ML inference module for a new model or task. Use when adding a new ML backend (e.g., a new ASR engine, new FA model, new NLP task).
4+
disable-model-invocation: true
5+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent
6+
---
7+
8+
# Add a New Inference Module
9+
10+
Create a new Python ML inference module. `$ARGUMENTS` should describe the module (e.g., `/add-inference "wav2vec2 ASR for Mandarin"`).
11+
12+
## Architecture
13+
14+
Each inference module is a **pure inference function**: receives structured input, runs ML model, returns structured output. No CHAT parsing, no pipeline orchestration, no domain logic.
15+
16+
```
17+
Worker (_infer.py) → inference/<module>.py → ML library (torch, stanza, etc.) → structured output
18+
```
19+
20+
## Step 1: Understand the Pattern
21+
22+
Read existing inference modules to understand the conventions:
23+
24+
```bash
25+
ls batchalign/inference/
26+
head -60 batchalign/inference/morphosyntax.py
27+
head -60 batchalign/inference/fa.py
28+
```
29+
30+
Every inference module has:
31+
1. **Model loading function** — called once at worker startup
32+
2. **Inference function** — called per-batch, pure computation
33+
3. **Pydantic types** — for structured I/O at IPC boundary
34+
35+
## Step 2: Define Types
36+
37+
**File:** `batchalign/worker/_types.py`
38+
39+
Add Pydantic models for the request and response. These mirror Rust types across the IPC boundary.
40+
41+
```bash
42+
grep -n "class.*BaseModel" batchalign/worker/_types.py | head -20
43+
```
44+
45+
Rules:
46+
- Use domain types from `batchalign/inference/_domain_types.py` (AudioPath, TimestampMs, etc.)
47+
- All fields must have type annotations
48+
- No `Any` or `object` types
49+
50+
If this is a new InferTask variant, add it to the `InferTask` enum:
51+
52+
```bash
53+
grep -n "class InferTask" batchalign/worker/_types.py
54+
```
55+
56+
## Step 3: Create the Inference Module
57+
58+
**File:** `batchalign/inference/<name>.py`
59+
60+
Template:
61+
62+
```python
63+
"""<Name> inference module.
64+
65+
Receives structured input, runs ML model, returns raw output.
66+
No CHAT parsing, no text processing, no domain logic.
67+
"""
68+
69+
from __future__ import annotations
70+
71+
from typing import TYPE_CHECKING
72+
73+
if TYPE_CHECKING:
74+
pass # Import heavy ML types here
75+
76+
from batchalign.inference._domain_types import ...
77+
78+
79+
def load_<name>_model(lang: str) -> <ModelType>:
80+
"""Load the ML model for the given language.
81+
82+
Called once at worker startup. Heavy imports go here.
83+
"""
84+
import torch # Lazy import
85+
...
86+
87+
88+
def batch_infer_<name>(
89+
model: <ModelType>,
90+
items: list[<InputType>],
91+
) -> list[<OutputType>]:
92+
"""Run inference on a batch of items.
93+
94+
Pure computation — no CHAT text, no side effects.
95+
"""
96+
...
97+
```
98+
99+
Key rules:
100+
- **Lazy imports** for heavy libraries (torch, stanza, transformers) — put them inside the function
101+
- **No CHAT text** — receive extracted words/audio, return structured results
102+
- **Type annotations** on all functions and variables
103+
- **No `Any`** — use specific types, `TYPE_CHECKING` guards for expensive imports
104+
- **Pydantic models** at IPC boundaries
105+
106+
## Step 4: Wire into Worker
107+
108+
### Model loading
109+
110+
**File:** `batchalign/worker/_main.py`
111+
112+
Add model loading for the new module:
113+
114+
```bash
115+
grep -n "def load_models" batchalign/worker/_main.py
116+
```
117+
118+
### Dispatch
119+
120+
**File:** `batchalign/worker/_infer.py`
121+
122+
Add a case to route the new InferTask to your inference function:
123+
124+
```bash
125+
grep -n "def batch_infer" batchalign/worker/_infer.py
126+
```
127+
128+
## Step 5: Add to Worker Capabilities
129+
130+
**File:** `batchalign/worker/_handlers.py`
131+
132+
Ensure the new task appears in the capabilities response:
133+
134+
```bash
135+
grep -n "capabilities" batchalign/worker/_handlers.py
136+
```
137+
138+
## Step 6: Add Tests
139+
140+
```python
141+
# batchalign/tests/test_<name>.py
142+
"""Tests for <name> inference module."""
143+
144+
def test_<name>_basic():
145+
"""Test basic inference with minimal input."""
146+
...
147+
```
148+
149+
Rules:
150+
- No mocks (`unittest.mock` is banned)
151+
- Use real models or skip if not available (`pytest.mark.skipif`)
152+
- Test with minimal valid input
153+
- Verify output types match Pydantic models
154+
155+
## Step 7: Verify
156+
157+
```bash
158+
# Run the new tests
159+
uv run pytest batchalign/tests/test_<name>.py -v
160+
161+
# Type check
162+
uv run mypy batchalign/inference/<name>.py
163+
164+
# Test worker starts with new command
165+
uv run python -m batchalign.worker --command <name> --lang eng
166+
# Should print {"ready": true, ...}
167+
168+
# Full test suite
169+
uv run pytest batchalign --disable-pytest-warnings -x -q
170+
```
171+
172+
## Key Files
173+
174+
| Purpose | Path |
175+
|---------|------|
176+
| Existing inference modules | `batchalign/inference/` |
177+
| Domain type aliases | `batchalign/inference/_domain_types.py` |
178+
| Worker types (Pydantic) | `batchalign/worker/_types.py` |
179+
| Worker dispatch router | `batchalign/worker/_infer.py` |
180+
| Worker model loading | `batchalign/worker/_main.py` |
181+
| Worker capabilities | `batchalign/worker/_handlers.py` |
182+
| HK/Cantonese engines | `batchalign/inference/hk/` |

0 commit comments

Comments
 (0)