batchalign 0.10.0__cp310-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- batchalign/BUILD.bazel +376 -0
- batchalign/README.md +140 -0
- batchalign/__init__.py +233 -0
- batchalign/__main__.py +6 -0
- batchalign/_core/BUILD.bazel +84 -0
- batchalign/_core/__init__.py +56 -0
- batchalign/_core/_core.pyd +0 -0
- batchalign/_core/_proto_generated.py +1099 -0
- batchalign/_core/proto.py +186 -0
- batchalign/_pytest_runner.py +29 -0
- batchalign/api.py +890 -0
- batchalign/backends/__init__.py +106 -0
- batchalign/backends/_qwen_lang.py +31 -0
- batchalign/backends/ai/__init__.py +7 -0
- batchalign/backends/ai/dspy.py +450 -0
- batchalign/backends/asr/__init__.py +35 -0
- batchalign/backends/asr/_torch_audio.py +70 -0
- batchalign/backends/asr/aliyun.py +136 -0
- batchalign/backends/asr/chatwhisper.py +921 -0
- batchalign/backends/asr/funasr.py +78 -0
- batchalign/backends/asr/funaudio.py +225 -0
- batchalign/backends/asr/google.py +359 -0
- batchalign/backends/asr/malayalam_wav2vec2.py +126 -0
- batchalign/backends/asr/openai_whisper.py +118 -0
- batchalign/backends/asr/qwen.py +163 -0
- batchalign/backends/asr/qwen3_asr.py +211 -0
- batchalign/backends/asr/rev.py +493 -0
- batchalign/backends/asr/tencent.py +217 -0
- batchalign/backends/asr/whisper.py +147 -0
- batchalign/backends/base.py +237 -0
- batchalign/backends/fa/__init__.py +13 -0
- batchalign/backends/fa/qwen3_fa.py +221 -0
- batchalign/backends/fa/wav2vec2.py +345 -0
- batchalign/backends/fa/whisper_fa.py +300 -0
- batchalign/backends/morphosyntax/__init__.py +7 -0
- batchalign/backends/morphosyntax/stanza.py +745 -0
- batchalign/backends/morphosyntax/ud/__init__.py +0 -0
- batchalign/backends/morphosyntax/ud/dp.py +277 -0
- batchalign/backends/morphosyntax/ud/en/__init__.py +0 -0
- batchalign/backends/morphosyntax/ud/en/irr.py +215 -0
- batchalign/backends/morphosyntax/ud/fr/__init__.py +0 -0
- batchalign/backends/morphosyntax/ud/fr/apm.py +15 -0
- batchalign/backends/morphosyntax/ud/fr/apmn.py +81 -0
- batchalign/backends/morphosyntax/ud/fr/case.py +33 -0
- batchalign/backends/morphosyntax/ud/it/__init__.py +5 -0
- batchalign/backends/morphosyntax/ud/it/workarounds.py +427 -0
- batchalign/backends/morphosyntax/ud/ja/__init__.py +0 -0
- batchalign/backends/morphosyntax/ud/ja/verbforms.py +147 -0
- batchalign/backends/morphosyntax/ud/lang.py +37 -0
- batchalign/backends/morphosyntax/ud/render.py +1373 -0
- batchalign/backends/morphosyntax/ud/tokenize.py +235 -0
- batchalign/backends/speaker/__init__.py +8 -0
- batchalign/backends/speaker/pyannote.py +182 -0
- batchalign/backends/speaker/pyannote_ai.py +376 -0
- batchalign/backends/translate/__init__.py +15 -0
- batchalign/backends/translate/aliyun.py +170 -0
- batchalign/backends/translate/google.py +222 -0
- batchalign/backends/translate/nllb.py +147 -0
- batchalign/backends/translate/tencent.py +167 -0
- batchalign/backends/utseg/__init__.py +9 -0
- batchalign/backends/utseg/cantonese.py +69 -0
- batchalign/backends/utseg/chatutterance.py +287 -0
- batchalign/backends/utseg/cleanup.py +101 -0
- batchalign/backends/utseg/malayalam_sat.py +73 -0
- batchalign/backends/utseg/support/filled_pauses.eng +5 -0
- batchalign/backends/utseg/support/replacements.eng +7 -0
- batchalign/base.py +75 -0
- batchalign/cli/__init__.py +233 -0
- batchalign/cli/_common.py +184 -0
- batchalign/cli/_logging.py +147 -0
- batchalign/cli/_options.py +61 -0
- batchalign/cli/ai.py +96 -0
- batchalign/cli/align.py +188 -0
- batchalign/cli/cache.py +160 -0
- batchalign/cli/compare.py +144 -0
- batchalign/cli/convert.py +123 -0
- batchalign/cli/daemon.py +225 -0
- batchalign/cli/diarize.py +94 -0
- batchalign/cli/hidden/coref.py +31 -0
- batchalign/cli/morphotag.py +132 -0
- batchalign/cli/transcribe.py +358 -0
- batchalign/cli/translate.py +86 -0
- batchalign/cli/tui/__init__.py +18 -0
- batchalign/cli/tui/bridge.py +97 -0
- batchalign/cli/tui/dashboard.py +654 -0
- batchalign/cli/tui/errors.py +380 -0
- batchalign/cli/tui/hints.py +50 -0
- batchalign/cli/tui/interface.py +740 -0
- batchalign/cli/tui/task.py +124 -0
- batchalign/cli/utseg.py +88 -0
- batchalign/cli/version.py +112 -0
- batchalign/config.py +544 -0
- batchalign/inputs.py +225 -0
- batchalign/lang.py +76 -0
- batchalign/recipes.py +222 -0
- batchalign/tests/__init__.py +0 -0
- batchalign/tests/conftest.py +91 -0
- batchalign/tests/fixture_paths.py +12 -0
- batchalign/tests/test_api_introspection.py +157 -0
- batchalign/tests/test_api_smoke.py +167 -0
- batchalign/tests/test_backends.py +381 -0
- batchalign/tests/test_ca_strip.py +34 -0
- batchalign/tests/test_cache_cli.py +60 -0
- batchalign/tests/test_cantonese_fa.py +53 -0
- batchalign/tests/test_chat_discovery.py +39 -0
- batchalign/tests/test_chatwhisper_device.py +99 -0
- batchalign/tests/test_chatwhisper_segment.py +76 -0
- batchalign/tests/test_cli.py +580 -0
- batchalign/tests/test_config.py +121 -0
- batchalign/tests/test_convert_cli.py +43 -0
- batchalign/tests/test_daemon_e2e.py +80 -0
- batchalign/tests/test_daemon_port.py +107 -0
- batchalign/tests/test_diarize_cli.py +160 -0
- batchalign/tests/test_dp_shim.py +61 -0
- batchalign/tests/test_golden_fixtures.py +217 -0
- batchalign/tests/test_google_asr.py +154 -0
- batchalign/tests/test_lang.py +83 -0
- batchalign/tests/test_malayalam_sat.py +70 -0
- batchalign/tests/test_malayalam_wav2vec2.py +98 -0
- batchalign/tests/test_morphotag_clear.py +84 -0
- batchalign/tests/test_morphotag_cli_failure.py +25 -0
- batchalign/tests/test_morphotag_render.py +819 -0
- batchalign/tests/test_morphotag_tokenize.py +86 -0
- batchalign/tests/test_proto.py +172 -0
- batchalign/tests/test_pyannote_ai.py +228 -0
- batchalign/tests/test_qwen3_contract.py +85 -0
- batchalign/tests/test_recipes.py +223 -0
- batchalign/tests/test_rev_presegmentation.py +76 -0
- batchalign/tests/test_revai_preflight.py +118 -0
- batchalign/tests/test_safe_resolve.py +60 -0
- batchalign/tests/test_sibling_media.py +59 -0
- batchalign/tests/test_stanza_manifest_refresh.py +80 -0
- batchalign/tests/test_stanza_pipeline_cache.py +335 -0
- batchalign/tests/test_tui_bridge.py +84 -0
- batchalign/tests/test_tui_dashboard.py +314 -0
- batchalign/tests/test_tui_interface.py +368 -0
- batchalign/tests/test_tui_parallel.py +552 -0
- batchalign/tests/test_tui_task.py +140 -0
- batchalign/tests/test_utseg_cleanup.py +81 -0
- batchalign/tests/test_utseg_sliding_window.py +60 -0
- batchalign/tests/test_utseg_typed_assignments.py +160 -0
- batchalign/tests/test_version_cmd.py +60 -0
- batchalign-0.10.0.dist-info/METADATA +268 -0
- batchalign-0.10.0.dist-info/RECORD +146 -0
- batchalign-0.10.0.dist-info/WHEEL +4 -0
- batchalign-0.10.0.dist-info/entry_points.txt +2 -0
batchalign/BUILD.bazel
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"""Bazel-native dev / test path for the batchalign Python package.
|
|
2
|
+
|
|
3
|
+
Runtime targets (no maturin, no uv at `bazel run` time):
|
|
4
|
+
|
|
5
|
+
bazel run //python/batchalign # CLI (py_binary)
|
|
6
|
+
bazel test //python/batchalign:tests # pytest (py_test)
|
|
7
|
+
bazel build //python/batchalign # py_binary + .so + py_library
|
|
8
|
+
|
|
9
|
+
The cdylib is built natively by `rust_shared_library` in
|
|
10
|
+
`//crates/batchalign/batchalign-engine` and lands at `_core.so`
|
|
11
|
+
alongside the Python sources in the runfiles tree.
|
|
12
|
+
|
|
13
|
+
Release-only (still maturin — sole reader of pyproject.toml's full
|
|
14
|
+
multi-platform tagging matrix + optional-deps groups):
|
|
15
|
+
|
|
16
|
+
bazel run //python/batchalign:wheel # build + install under
|
|
17
|
+
# python/target/wheels/<tag>.whl
|
|
18
|
+
|
|
19
|
+
The wheel command is one target end-to-end: it triggers the maturin
|
|
20
|
+
build (cached as `:_wheel_artifact`, also depended on by `:sidecar`)
|
|
21
|
+
and copies the result into `python/target/wheels/<tag>.whl` with the
|
|
22
|
+
PyPI-tagged basename. No multi-step incantations; no separate `bazel
|
|
23
|
+
build` + `cp` recipe for contributors or CI to memorize.
|
|
24
|
+
|
|
25
|
+
PyPI uploads are CI-only via trusted publishing
|
|
26
|
+
(`.github/workflows/publish-pypi.yml`). There is no local-publish
|
|
27
|
+
target — local twine uploads invite the wrong mental model and bypass
|
|
28
|
+
the OIDC + branch-protection guarantees that the CI flow provides.
|
|
29
|
+
|
|
30
|
+
`pyproject.toml` is the single source of truth for the wheel
|
|
31
|
+
(version, runtime deps, all `[project.optional-dependencies]`
|
|
32
|
+
groups, scripts, classifiers).
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
# Zero hand-listed deps. `all_runtime_deps()` returns every package in
|
|
36
|
+
# requirements.lock.txt, with rules_python's marker-conditional dangling
|
|
37
|
+
# names filtered out (see `//bazel/python:deps.bzl` for the why; the
|
|
38
|
+
# filter is a per-name list of *marker-excluded names from
|
|
39
|
+
# requirements.lock.txt*, not a list of deps to include).
|
|
40
|
+
load("//bazel/python:deps.bzl", "all_runtime_deps")
|
|
41
|
+
load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")
|
|
42
|
+
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
|
|
43
|
+
|
|
44
|
+
package(default_visibility = ["//visibility:public"])
|
|
45
|
+
|
|
46
|
+
_RUNTIME_DEPS = all_runtime_deps()
|
|
47
|
+
|
|
48
|
+
py_library(
|
|
49
|
+
name = "batchalign_lib",
|
|
50
|
+
# `_core/` is a Bazel subpackage (it owns the proto-codegen genrule),
|
|
51
|
+
# so glob stops there. We pull the `_core` sources back in by
|
|
52
|
+
# depending on its `:proto` py_library, which exports
|
|
53
|
+
# `batchalign._core.{__init__,proto,_proto_generated}` plus the
|
|
54
|
+
# staged `_core.so`.
|
|
55
|
+
srcs = glob(
|
|
56
|
+
["**/*.py"],
|
|
57
|
+
exclude = ["tests/**"],
|
|
58
|
+
),
|
|
59
|
+
# Non-Python data shipped with backends (e.g. BA2's disfluency /
|
|
60
|
+
# replacement word-lists `backends/utseg/support/*.eng` that the
|
|
61
|
+
# CHATUtterance segmenter reads at runtime). Without these in runfiles
|
|
62
|
+
# the lists load empty and `uh` never becomes `&-uh`.
|
|
63
|
+
data = glob(["**/support/*"]),
|
|
64
|
+
# `imports = [".."]` puts python/ on PYTHONPATH so `import batchalign`
|
|
65
|
+
# resolves. The package layout is python/batchalign/{__init__,...}.py.
|
|
66
|
+
imports = [".."],
|
|
67
|
+
deps = _RUNTIME_DEPS + ["//python/batchalign/_core:proto"],
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Default runnable target — `bazel run //python/batchalign` invokes
|
|
71
|
+
# `python -m batchalign` against the Bazel-built `.so`.
|
|
72
|
+
py_binary(
|
|
73
|
+
name = "batchalign",
|
|
74
|
+
srcs = ["__main__.py"],
|
|
75
|
+
main = "__main__.py",
|
|
76
|
+
deps = [":batchalign_lib"],
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
py_test(
|
|
80
|
+
name = "pytest",
|
|
81
|
+
srcs = glob(["tests/**/*.py"]) + ["_pytest_runner.py"],
|
|
82
|
+
main = "_pytest_runner.py",
|
|
83
|
+
# The tests form a package (`tests/__init__.py`). Pytest's default
|
|
84
|
+
# `prepend` mode resolves their source symlinks and inserts the checkout's
|
|
85
|
+
# `python/` directory ahead of Bazel's runfiles package. That is invisible
|
|
86
|
+
# on developer machines with a maturin-built `_core.abi3.so`, but clean CI
|
|
87
|
+
# then loses the staged `_core.so`. Importlib mode leaves Bazel's package
|
|
88
|
+
# root authoritative throughout collection.
|
|
89
|
+
args = ["python/batchalign/tests", "-q", "--import-mode=importlib"],
|
|
90
|
+
# Vendored parity fixtures used by `test_golden_fixtures.py`. Must
|
|
91
|
+
# be in runfiles for the test to find them — without this they were
|
|
92
|
+
# silently skipped on every CI run (the earlier copy lived in a
|
|
93
|
+
# developer-local `talkbank-alignment/` directory). Same for the
|
|
94
|
+
# Rust NUM2LANG table + the E316 spec, both probed by the hermetic
|
|
95
|
+
# Landing 3 #16/#17 guards.
|
|
96
|
+
data = [
|
|
97
|
+
"//resources/test_fixtures:parity_fixtures",
|
|
98
|
+
"//crates/batchalign/batchalign-core:asr_data",
|
|
99
|
+
"//resources/spec/errors:E316_angle_bracket_in_mor_stem.md",
|
|
100
|
+
],
|
|
101
|
+
# all_requirements already contains pytest + pytest-xdist via the
|
|
102
|
+
# dev extra in pyproject; don't re-list them or Bazel errors on
|
|
103
|
+
# duplicate-deps.
|
|
104
|
+
deps = [":batchalign_lib"] + _RUNTIME_DEPS,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
# Maturin-driven wheel (release path).
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
# Two-layer shape:
|
|
111
|
+
#
|
|
112
|
+
# :_wheel_artifact genrule. Produces `batchalign.whl` (wheel
|
|
113
|
+
# bytes) + `wheel_tag.txt` (maturin's PyPI-
|
|
114
|
+
# tagged basename, e.g.
|
|
115
|
+
# `batchalign-0.7.1-cp312-cp312-macosx_11_0_arm64.whl`).
|
|
116
|
+
# Tagged `no-cache` because maturin reads
|
|
117
|
+
# `.rs` / `.py` / `pyproject.toml` from the
|
|
118
|
+
# source tree directly, not through Bazel
|
|
119
|
+
# inputs — letting Bazel always re-invoke
|
|
120
|
+
# maturin (cargo's incremental cache makes the
|
|
121
|
+
# no-op case sub-second) is cheaper than
|
|
122
|
+
# declaring every Rust crate target in `srcs`
|
|
123
|
+
# for invalidation and paying for a parallel
|
|
124
|
+
# rules_rust build of every crate maturin
|
|
125
|
+
# already compiles. Not user-facing — both
|
|
126
|
+
# `:sidecar` and `:wheel` consume it.
|
|
127
|
+
#
|
|
128
|
+
# :wheel sh_binary. `bazel run //python/batchalign:wheel`
|
|
129
|
+
# builds :_wheel_artifact, reads the tag, and
|
|
130
|
+
# copies the wheel into
|
|
131
|
+
# `python/target/wheels/<tag>.whl`. This is
|
|
132
|
+
# the single user-facing wheel command —
|
|
133
|
+
# contributors, the justfile, and CI all
|
|
134
|
+
# invoke it the same way. No
|
|
135
|
+
# memorized-shell-snippet alternative.
|
|
136
|
+
#
|
|
137
|
+
# Hermetic C toolchain. The toolchains_llvm-managed clang + ar +
|
|
138
|
+
# ranlib are passed as execpaths; the shell scripts resolve them and
|
|
139
|
+
# export CC/CXX/AR/RANLIB before invoking maturin. cc-rs inside
|
|
140
|
+
# cargo then picks up the pinned compiler instead of /usr/bin/cc,
|
|
141
|
+
# which decouples every `*-sys` crate build from the host
|
|
142
|
+
# CommandLineTools state. See MODULE.bazel's `toolchains_llvm` block
|
|
143
|
+
# for the cflag injection that papers over the macOS 26.x SDK
|
|
144
|
+
# `__kernel_ptr_semantics` regression.
|
|
145
|
+
#
|
|
146
|
+
# `$(COMPILATION_MODE)` is a Bazel make-variable that expands to
|
|
147
|
+
# `opt`, `dbg`, or `fastbuild` based on `-c <mode>`. The shell scripts
|
|
148
|
+
# translate that into maturin's `--release` / dev profile, so users
|
|
149
|
+
# never need PYAPP_PROFILE / MATURIN_PROFILE env vars — `bazel run
|
|
150
|
+
# -c opt //...` is the only knob.
|
|
151
|
+
#
|
|
152
|
+
# Historical note: this used to be two parallel targets (a
|
|
153
|
+
# `sh_binary :wheel` that ran maturin and dropped a wheel into the
|
|
154
|
+
# source tree, plus a `:wheel_file` genrule that ran maturin a second
|
|
155
|
+
# time for Bazel-tracked output). Editing a single .rs file forced
|
|
156
|
+
# two maturin runs. Collapsed: one genrule produces the wheel bytes;
|
|
157
|
+
# the sh_binary is now a thin copy-out that reuses that output. A
|
|
158
|
+
# follow-up pass also stripped the rules_rust crate targets from
|
|
159
|
+
# `srcs` — they were never consumed by maturin, only triggering a
|
|
160
|
+
# Bazel-side rust_library build that doubled the rustc cost on every
|
|
161
|
+
# `.rs` edit. The `no-cache` tag below preserves correctness without
|
|
162
|
+
# that cost.
|
|
163
|
+
#
|
|
164
|
+
# Caveat — the rules_rust build isn't fully eliminated. `srcs` pulls
|
|
165
|
+
# in `:_proto_generated_py`, which transitively depends on
|
|
166
|
+
# `//crates/batchalign/batchalign-core:proto_schema_json`. That genrule
|
|
167
|
+
# uses `:emit_proto_schema` (a rust_binary) as its tool, and
|
|
168
|
+
# `:emit_proto_schema` links `:batchalign_core` as a rust_library.
|
|
169
|
+
# Result: every wheel build first compiles `batchalign_core` + a chunk
|
|
170
|
+
# of its transitive crate-universe deps via rules_rust in the exec
|
|
171
|
+
# config (visible as `Compiling Rust rlib batchalign_core ... [for
|
|
172
|
+
# tool]`), then maturin rebuilds the same crate graph from scratch
|
|
173
|
+
# under cargo's target/. Two compiles of overlapping crates per cold
|
|
174
|
+
# wheel build. We accept this for now — cargo's incremental cache
|
|
175
|
+
# absorbs the hit on warm rebuilds, and the schema-emitter-as-tool
|
|
176
|
+
# pattern is what gives us regen-on-build for `_proto_generated.py`
|
|
177
|
+
# without a hand-checked-in artifact. If this cost ever bites, three
|
|
178
|
+
# escape hatches: (a) check in `proto.schema.json` with a CI diff
|
|
179
|
+
# guard, (b) factor proto types into a leaf `batchalign-proto` crate
|
|
180
|
+
# so `:emit_proto_schema` doesn't pull all of `batchalign_core`, or
|
|
181
|
+
# (c) move schema emission into maturin's build itself and ship the
|
|
182
|
+
# generated `.py` inside the wheel.
|
|
183
|
+
config_setting(
|
|
184
|
+
name = "windows",
|
|
185
|
+
constraint_values = ["@platforms//os:windows"],
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
_LLVM_TOOLS = select({
|
|
189
|
+
":windows": [],
|
|
190
|
+
"//conditions:default": [
|
|
191
|
+
"//bazel/python:llvm_clang",
|
|
192
|
+
"//bazel/python:llvm_ar",
|
|
193
|
+
"//bazel/python:llvm_ranlib",
|
|
194
|
+
],
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
genrule(
|
|
198
|
+
name = "_wheel_artifact",
|
|
199
|
+
outs = ["batchalign.whl", "wheel_tag.txt"],
|
|
200
|
+
srcs = [
|
|
201
|
+
# `_proto_generated.py` is the only input the genrule action
|
|
202
|
+
# actually *reads via Bazel runfiles* — maturin_build.sh stages
|
|
203
|
+
# it into the source tree before maturin packages. The `.rs`
|
|
204
|
+
# sources under `crates/` and `.py` sources under
|
|
205
|
+
# `python/batchalign/` are read directly via
|
|
206
|
+
# `BUILD_WORKSPACE_DIRECTORY`, NOT through Bazel inputs (see the
|
|
207
|
+
# `tags` comment below for why).
|
|
208
|
+
"//python/batchalign/_core:_proto_generated_py",
|
|
209
|
+
# `pyproject.toml` IS declared as a Bazel input. It owns the
|
|
210
|
+
# wheel version (`[project].version`), the runtime deps, the
|
|
211
|
+
# optional-dependencies extras, and the maturin config — every
|
|
212
|
+
# one of these changes the wheel's identity or contents. Without
|
|
213
|
+
# it in `srcs`, a version bump in pyproject.toml does not
|
|
214
|
+
# invalidate this action and `bazel run //python/batchalign:wheel`
|
|
215
|
+
# silently serves the previous wheel. The `tags = ["no-cache"]`
|
|
216
|
+
# below does NOT compensate for this: that tag disables result
|
|
217
|
+
# caching, not action skipping — Bazel's incremental analysis
|
|
218
|
+
# still skips when declared inputs are unchanged. Declaring a
|
|
219
|
+
# plain TOML file in `srcs` does not trigger any `rust_library`
|
|
220
|
+
# rebuild (that concern only applies to `.rs` files reached via
|
|
221
|
+
# rules_rust).
|
|
222
|
+
"//python:pyproject.toml",
|
|
223
|
+
] + _LLVM_TOOLS,
|
|
224
|
+
tools = [
|
|
225
|
+
"//bazel/python:maturin_build.sh",
|
|
226
|
+
"//bazel/python:maturin_genrule.sh",
|
|
227
|
+
"//bazel/python:hermeticity_guard.sh",
|
|
228
|
+
"//bazel/python:runfiles_resolve.sh",
|
|
229
|
+
"@multitool//tools/uv",
|
|
230
|
+
"@rules_rust//rust/toolchain:current_cargo_files",
|
|
231
|
+
"@rules_rust//rust/toolchain:current_rustc_files",
|
|
232
|
+
] + _LLVM_TOOLS,
|
|
233
|
+
cmd = " ".join([
|
|
234
|
+
"PYAPP_RUST_BIN_DIRS='$(execpaths @rules_rust//rust/toolchain:current_cargo_files) $(execpaths @rules_rust//rust/toolchain:current_rustc_files)'",
|
|
235
|
+
"$(execpath //bazel/python:maturin_genrule.sh)",
|
|
236
|
+
"$(location batchalign.whl)",
|
|
237
|
+
"$(location wheel_tag.txt)",
|
|
238
|
+
"$(execpath @multitool//tools/uv)",
|
|
239
|
+
"$(execpath //python/batchalign/_core:_proto_generated_py)",
|
|
240
|
+
"$(COMPILATION_MODE)",
|
|
241
|
+
]) + select({
|
|
242
|
+
":windows": " host",
|
|
243
|
+
"//conditions:default": " llvm " + " ".join([
|
|
244
|
+
"$(execpath //bazel/python:llvm_clang)",
|
|
245
|
+
"$(execpath //bazel/python:llvm_ar)",
|
|
246
|
+
"$(execpath //bazel/python:llvm_ranlib)",
|
|
247
|
+
]),
|
|
248
|
+
}),
|
|
249
|
+
# `local = True`: maturin needs network access (cargo fetches) and a
|
|
250
|
+
# persistent target/ cache that a sandboxed action can't supply.
|
|
251
|
+
# uv, rustc, and cargo remain Bazel-managed. The C toolchain follows the
|
|
252
|
+
# platform contract in maturin_build.sh (LLVM, Xcode, or Visual Studio).
|
|
253
|
+
local = True,
|
|
254
|
+
# `no-cache`: disables caching of the action's outputs (local disk
|
|
255
|
+
# cache + remote cache). The `.rs` under `crates/` and `.py` under
|
|
256
|
+
# `python/batchalign/` are read from the source tree at run time,
|
|
257
|
+
# NOT through Bazel's declared input set — if we declared all of
|
|
258
|
+
# them in `srcs`, every `.rs` edit would force a parallel Bazel-side
|
|
259
|
+
# `rust_library` rebuild of every crate maturin already compiles
|
|
260
|
+
# via cargo (rustc twice per edit). Skipping the declaration keeps
|
|
261
|
+
# the GUI sidecar dev loop fast; cargo's incremental cache and
|
|
262
|
+
# maturin's "already up to date" check keep the no-op case
|
|
263
|
+
# sub-second.
|
|
264
|
+
#
|
|
265
|
+
# Caveat: `no-cache` does NOT force re-execution. Bazel's
|
|
266
|
+
# incremental analysis still skips this action when its declared
|
|
267
|
+
# input hashes are unchanged. That is why `pyproject.toml` is an
|
|
268
|
+
# explicit `srcs` entry — without it, a version bump silently
|
|
269
|
+
# serves the previously built wheel. Source-tree-resident `.rs` /
|
|
270
|
+
# `.py` edits also do not invalidate this action on their own;
|
|
271
|
+
# they're picked up when some declared input (e.g. the proto
|
|
272
|
+
# generator) changes or when the user forces a rebuild
|
|
273
|
+
# (`bazel clean`, deleting `bazel-bin/python/batchalign/*.whl`, or
|
|
274
|
+
# touching pyproject.toml). If pure `.rs`/`.py` invalidation
|
|
275
|
+
# becomes a real pain point, the next step is to declare those
|
|
276
|
+
# globs in `srcs` and accept the double-build cost, or move the
|
|
277
|
+
# whole wheel build out of a genrule into a `repository_rule` /
|
|
278
|
+
# external action with explicit input tracking.
|
|
279
|
+
tags = ["no-cache"],
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# User-facing wheel command. `bazel run //python/batchalign:wheel`
|
|
283
|
+
# (and `just batchalign wheel`, and CI wheel jobs) all funnel through
|
|
284
|
+
# this one target. It reads the cached `:_wheel_artifact` outputs from
|
|
285
|
+
# runfiles and copies them into `python/target/wheels/<tag>.whl`
|
|
286
|
+
# inside the source tree. No second maturin run; no shell snippet
|
|
287
|
+
# contributors need to memorize.
|
|
288
|
+
sh_binary(
|
|
289
|
+
name = "wheel",
|
|
290
|
+
srcs = ["//bazel/python:install_wheel.sh"],
|
|
291
|
+
args = [
|
|
292
|
+
"$(rootpath :batchalign.whl)",
|
|
293
|
+
"$(rootpath :wheel_tag.txt)",
|
|
294
|
+
],
|
|
295
|
+
data = [
|
|
296
|
+
":batchalign.whl",
|
|
297
|
+
":wheel_tag.txt",
|
|
298
|
+
],
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Standalone production-daemon sidecar binary built via PyApp.
|
|
302
|
+
#
|
|
303
|
+
# `bazel build //python/batchalign:sidecar` produces a self-contained
|
|
304
|
+
# executable at `bazel-bin/python/batchalign/sidecar` that bundles
|
|
305
|
+
# CPython + the batchalign wheel + the `[api]` extra. The binary IS
|
|
306
|
+
# the daemon — running it boots `batchalign3 daemon` directly without
|
|
307
|
+
# the user typing the subcommand. Named "sidecar" because that's the
|
|
308
|
+
# role it plays in a deployment (companion service next to the main
|
|
309
|
+
# CLI / wheel install, not a separate top-level app).
|
|
310
|
+
#
|
|
311
|
+
# Architecture (all Bazel-managed, no host tool requirements beyond
|
|
312
|
+
# bazel + just):
|
|
313
|
+
# - Wheel input from `:batchalign.whl` (one of `:_wheel_artifact`'s
|
|
314
|
+
# outputs). Depending on the specific output rather than the
|
|
315
|
+
# genrule label keeps `wheel_tag.txt` out of the sidecar's input
|
|
316
|
+
# set (clean and explicit; not strictly required since the tag
|
|
317
|
+
# file is small and irrelevant to PyApp).
|
|
318
|
+
# - pyapp source from `@pyapp_src` (Bazel `git_repository`, pinned
|
|
319
|
+
# by SHA in MODULE.bazel; fetched lazily on first build).
|
|
320
|
+
# - cargo + rustc from `@rules_rust//rust/toolchain` (PATH-injected
|
|
321
|
+
# by `pyapp_install.sh` so the inner `cargo install` never sees
|
|
322
|
+
# host cargo).
|
|
323
|
+
#
|
|
324
|
+
# `local = True` is required: pyapp's build.rs writes into its source
|
|
325
|
+
# dir at compile time (an upstream design choice — see
|
|
326
|
+
# bazel/python/pyapp_install.sh for the writable-copy workaround).
|
|
327
|
+
# All other hermeticity holds.
|
|
328
|
+
genrule(
|
|
329
|
+
name = "sidecar",
|
|
330
|
+
outs = ["sidecar"],
|
|
331
|
+
srcs = [
|
|
332
|
+
":batchalign.whl",
|
|
333
|
+
# pyapp source is fetched lazily via git_repository in
|
|
334
|
+
# MODULE.bazel — Bazel only clones when this target is
|
|
335
|
+
# actually built.
|
|
336
|
+
"@pyapp_src//:srcs",
|
|
337
|
+
"@pyapp_src//:Cargo.toml",
|
|
338
|
+
],
|
|
339
|
+
tools = [
|
|
340
|
+
"//bazel/python:pyapp_install.sh",
|
|
341
|
+
# Bazel-provided cargo + rustc. PATH-injected by the script
|
|
342
|
+
# via PYAPP_RUST_BIN_DIRS, so the inner `cargo install`
|
|
343
|
+
# never touches host cargo.
|
|
344
|
+
"@rules_rust//rust/toolchain:current_cargo_files",
|
|
345
|
+
"@rules_rust//rust/toolchain:current_rustc_files",
|
|
346
|
+
],
|
|
347
|
+
# `local = True` is required: pyapp's build.rs writes into its
|
|
348
|
+
# source dir (an upstream design choice); cargo install's tmpdir
|
|
349
|
+
# copy of the source satisfies that, but only if cargo can use a
|
|
350
|
+
# persistent build cache — which sandboxed actions can't supply.
|
|
351
|
+
# All other hermeticity holds: cargo + rustc from rules_rust,
|
|
352
|
+
# pyapp source from @pyapp_src (Bazel-fetched, pinned SHA), wheel
|
|
353
|
+
# from :batchalign.whl. No crates.io network, no host cargo.
|
|
354
|
+
cmd = " ".join([
|
|
355
|
+
"PYAPP_RUST_BIN_DIRS='$(execpaths @rules_rust//rust/toolchain:current_cargo_files) $(execpaths @rules_rust//rust/toolchain:current_rustc_files)'",
|
|
356
|
+
"$(execpath //bazel/python:pyapp_install.sh)",
|
|
357
|
+
"$@",
|
|
358
|
+
"$(execpath //python/batchalign:batchalign.whl)",
|
|
359
|
+
"$(execpath @pyapp_src//:Cargo.toml)",
|
|
360
|
+
"$(COMPILATION_MODE)",
|
|
361
|
+
]),
|
|
362
|
+
local = True,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Lint kept as a shell-out because mypy/ruff configs sit in the uv-managed
|
|
366
|
+
# venv. Could become a py_test rule later; for now sh_binary is enough.
|
|
367
|
+
sh_binary(
|
|
368
|
+
name = "lint",
|
|
369
|
+
srcs = ["//bazel/python:lint.sh"],
|
|
370
|
+
args = ["$(rootpath @multitool//tools/uv)"],
|
|
371
|
+
data = [
|
|
372
|
+
"@multitool//tools/uv",
|
|
373
|
+
"//bazel/python:hermeticity_guard.sh",
|
|
374
|
+
"//python:pyproject.toml",
|
|
375
|
+
],
|
|
376
|
+
)
|
batchalign/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# batchalign
|
|
2
|
+
|
|
3
|
+
TalkBank CHAT processing pipeline — ASR, forced alignment, morphosyntax
|
|
4
|
+
(`%mor` / `%gra`), utterance segmentation, translation, and compare.
|
|
5
|
+
The user-facing Python package; the runtime is a PyO3 extension backed
|
|
6
|
+
by the Rust crates in `crates/batchalign/`.
|
|
7
|
+
|
|
8
|
+
User and developer docs live in `book/src/batchalign/` (the mdBook is
|
|
9
|
+
the source of truth). See `book/src/batchalign/developer/building.md`
|
|
10
|
+
for the canonical build recipe.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
From PyPI (stable wheels):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install batchalign
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
From source: **use the `just` recipes** — they go through Bazel, so
|
|
21
|
+
every dep (proto codegen, Rust crates, PyO3 extension, Python wheel
|
|
22
|
+
extras) is materialized for you. Don't reach for `maturin develop` or
|
|
23
|
+
`uv sync` directly unless you're debugging the build itself.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
just batchalign build # build every Bazel target for batchalign
|
|
27
|
+
just batchalign test # run every Bazel test target
|
|
28
|
+
just batchalign cli --help # run `batchalign3` via the development bridge
|
|
29
|
+
just batchalign pytest # pytest (with full Bazel dep graph)
|
|
30
|
+
just batchalign wheel # host-platform wheel at python/target/wheels/
|
|
31
|
+
just batchalign sidecar # standalone daemon binary via PyApp
|
|
32
|
+
just batchalign lint # mypy (+ ruff)
|
|
33
|
+
just batchalign versions # source-of-truth version readout
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`just --list batchalign` shows the full recipe list. The `just`
|
|
37
|
+
recipes call into Bazel, so `tools/bazel` (the bundled wrapper) takes
|
|
38
|
+
care of:
|
|
39
|
+
|
|
40
|
+
- regenerating the pydantic-v2 wire types from
|
|
41
|
+
`crates/batchalign/batchalign-core/src/proto/*.rs`
|
|
42
|
+
- rebuilding the `batchalign_core` PyO3 cdylib via maturin under the
|
|
43
|
+
hood
|
|
44
|
+
- staging the binary into the wheel
|
|
45
|
+
- propagating dependency changes to dependent targets
|
|
46
|
+
|
|
47
|
+
If you genuinely need `bazel` directly (because a recipe you want
|
|
48
|
+
isn't wrapped):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
bazel build //... # everything
|
|
52
|
+
bazel test //... # everything
|
|
53
|
+
bazel run //book:html # static book HTML
|
|
54
|
+
bazel run //apps/batchalign/batchalign-gui:openapi # GUI OpenAPI codegen
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The base wheel ships only the lightweight runtime. Heavy ML backends
|
|
58
|
+
are gated behind extras — install only what you use:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install 'batchalign[whisper]' # Whisper ASR
|
|
62
|
+
pip install 'batchalign[malayalam]' # Malayalam Wav2Vec2 XLSR ASR
|
|
63
|
+
pip install 'batchalign[stanza]' # morphosyntax (%mor / %gra)
|
|
64
|
+
pip install 'batchalign[pyannote]' # optional local Pyannote backend API
|
|
65
|
+
pip install 'batchalign[revai]' # Rev.AI cloud ASR
|
|
66
|
+
pip install 'batchalign[google]' # Gemini cloud ASR + diarization
|
|
67
|
+
pip install 'batchalign[cantonese]' # Cantonese pipeline (FunASR, Tencent)
|
|
68
|
+
pip install 'batchalign[qwen3]' # Qwen3 ASR + forced aligner
|
|
69
|
+
pip install 'batchalign[nllb]' # NLLB translation
|
|
70
|
+
pip install 'batchalign[api]' # FastAPI daemon (`batchalign3 daemon`)
|
|
71
|
+
pip install 'batchalign[all]' # everything
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## CLI
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
batchalign3 --help
|
|
78
|
+
|
|
79
|
+
batchalign3 transcribe input_dir -o output_dir --lang eng
|
|
80
|
+
batchalign3 transcribe input_dir -o output_dir --lang eng --engine whisper --diarize --diarize-engine pyannote-ai
|
|
81
|
+
batchalign3 diarize input_dir -o output_dir --engine pyannote-ai
|
|
82
|
+
batchalign3 align input_dir -o output_dir --engine wav2vec
|
|
83
|
+
batchalign3 morphotag input_dir -o output_dir --language en
|
|
84
|
+
batchalign3 utseg input_dir -o output_dir
|
|
85
|
+
batchalign3 translate input_dir -o output_dir --target eng
|
|
86
|
+
batchalign3 compare input_dir gold_dir -o output_dir
|
|
87
|
+
|
|
88
|
+
batchalign3 version # banner, version, git SHA
|
|
89
|
+
batchalign3 cache {path,stats,clear} # local result cache
|
|
90
|
+
batchalign3 daemon # FastAPI server (needs [api])
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
When `-o` is omitted, results are written back in place. The CLI accepts
|
|
94
|
+
either a single CHAT/media file or a folder (walked recursively).
|
|
95
|
+
|
|
96
|
+
The diarization CLI defaults to pyannoteAI cloud. Select the optional local
|
|
97
|
+
backend with `--engine pyannote` (or `--diarize-engine pyannote` on
|
|
98
|
+
`transcribe`). Configure the cloud API key in the shared Batchalign config file
|
|
99
|
+
(or set `BATCHALIGN_PYANNOTE_KEY`):
|
|
100
|
+
|
|
101
|
+
```ini
|
|
102
|
+
[diarize]
|
|
103
|
+
engine.pyannote.key = YOUR_API_KEY
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Programmatic API
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import batchalign as ba
|
|
110
|
+
|
|
111
|
+
# Build a backend chain.
|
|
112
|
+
pipeline = ba.recipes.morphotag(
|
|
113
|
+
stanza_backend=ba.StanzaBackend(lang="en"),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Or compose your own.
|
|
117
|
+
asr = ba.WhisperBackend(language=ba.LanguageCode.from_iso("eng"))
|
|
118
|
+
utseg = ba.CHATUtteranceBackend(model="talkbank/CHATUtterance-en")
|
|
119
|
+
pipeline = ba.recipes.transcribe(asr_backend=asr, utseg_backend=utseg)
|
|
120
|
+
|
|
121
|
+
# Run.
|
|
122
|
+
inputs = [ba.media_from_path("session.wav")]
|
|
123
|
+
outcomes = list(pipeline.run(inputs))
|
|
124
|
+
for outcome in outcomes:
|
|
125
|
+
outcome.write("session.cha")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Repository layout
|
|
129
|
+
|
|
130
|
+
This package is one slice of the `talkbank-tools` monorepo:
|
|
131
|
+
|
|
132
|
+
- `python/batchalign/` — Python package (this file).
|
|
133
|
+
- `crates/batchalign/` — Rust crates (`batchalign-core`, `batchalign-engine`).
|
|
134
|
+
- The pinned TalkBank Chatter dependency supplies CHAT parsing, models, and transforms.
|
|
135
|
+
- `apps/batchalign/batchalign-gui/` — Tauri desktop GUI.
|
|
136
|
+
- `book/` — user + developer documentation (mdBook; source of truth).
|
|
137
|
+
|
|
138
|
+
For repo conventions, build commands, and the BA3 cutover plan, see
|
|
139
|
+
`CLAUDE.md` at the repo root and
|
|
140
|
+
`book/src/batchalign/developer/landing-status.md`.
|