BERTopic module¶
BERTopic is a first-class topic-modeling path alongside LDA/NMF topic_modeling.
The module code is always registered; the native stack is optional at install time.
Install posture (read this first)¶
What worked: For a stretch the BERTopic Python stack (bertopic / hdbscan / umap-learn) lived in base deps and ran fine next to the rest of the product — Docker, GUI, and analysis — when that host could install the wheels.
What broke the release gate: Clean core wheel installs on some hosts (notably macOS without a usable llvmlite wheel) try to build umap-learn → numba → llvmlite from source and fail. That made “base install” an unreliable release signal even though BERTopic itself was fine where binaries were available.
Current rule (does not block builds):
Path |
BERTopic stack? |
Notes |
|---|---|---|
|
No |
Core builds and clean-env audits must stay green without the stack |
|
Yes |
Same packages as before; may fail on hosts that cannot get |
Docker / |
Yes |
Production image still ships the stack |
Runtime behaviour when packages are missing: module stays in the catalogue; runs report missing_extra:bertopic / broken_extra:bertopic — pipeline continues. This is intentional optional-extra posture, not a broken product.
pip install -e '.[bertopic]'
# or
pip install -e '.[full]'
Sentence Transformers remains a base dependency (shared with semantic/echoes). BERTopic reuses that embedding stack once the optional packages are present.
Host macOS segfault note (OpenMP / Numba oversubscription)¶
On Apple Silicon host Python, BERTopic’s default UMAP (n_jobs=-1) / HDBSCAN (core_dist_n_jobs=-1) can segfault during fit_transform (process exit -11) when OpenMP and Numba pools oversubscribe — especially after other modules have already loaded natives.
Mitigations shipped in runtime:
Early
ensure_native_thread_env_defaults()(pipeline / web entry / bootstrap)Explicit UMAP/HDBSCAN backends with
n_jobs=1/core_dist_n_jobs=1viabuild_model_kwargs()Subprocess-isolated fit (
transcriptx.core.utils.bertopic_fit) so a residual SIGSEGV after a long in-process pipeline becomes a soft module failure instead of killing the parent runlimited_native_threads(1)inside the worker
The package is not on PyPI; install from this repository (see install_verification_matrix.md).
Public release: runtime install markers are
core|fullonly. See installation details and dependency_audit.md. Host.[bertopic]/.[full]is not a blocking clean-env proof whenllvmlitecannot install; Dockerimage_pip_checkis the production-image proof for the fuller stack.
Lifecycle states¶
State |
Detection |
Heavy import? |
|---|---|---|
Registered |
Specs + |
No |
Package detected |
|
No |
Import verified |
Execution preflight |
Yes |
Attempted / completed |
Module run finished |
Yes |
Catalogue/UI must not call importing is_extra_available("bertopic").
Missing vs broken (degraded installs / broken natives):
missing_extra:bertopic— distribution metadata absentbroken_extra:bertopic— metadata present, import/native load fails
Auto-install is off (auto_install=False).
Group aggregation¶
Group BERTopic refits from pooled source segments (via TranscriptService), not from transcript-level topic IDs. Member artifacts are activation/status only. Aggregation registry: selector=any_of(["bertopic"]), deps=[].
Ordering:
Transcript:
segment_indexascendingGroup:
(transcript_id, segment_index)ascending
Duplicates are retained as separate documents. All-outlier fits are succeeded with meta.all_outlier=true and emit no chart specifications.
UI / charts / export surfaces¶
Surface |
Wiring |
|---|---|
Module picker |
|
Chart registry |
|
Charts page |
Manifest artifacts with |
Artifacts / zip export |
Standard manifest discovery under |
Summary extractor |
|
Group charts |
|
Catalog |
Configurable knobs¶
All knobs live under analysis.bertopic (config.json / Settings UI) and have matching env overrides. Defaults match BERTopicSettingsModel.
Knob |
Default |
Env |
Notes |
|---|---|---|---|
|
|
|
Primary quality/speed trade-off |
|
|
|
Smaller → more topics |
|
|
|
|
|
|
|
Words kept per topic |
|
|
|
Words joined into display labels |
|
|
|
Soft probs; slower |
|
|
|
Wall-clock fit budget; timeout continues pipeline |
Cache fingerprints include the shaping knobs plus timeout_seconds. label_words is label-shaping only (not a BERTopic ctor arg).
Offline / models¶
Package install ≠ model download. Hub embedding IDs get syntax checks only; local paths are existence-checked. Real model usability is known at load/fit. Set TRANSCRIPTX_DISABLE_DOWNLOADS=1 in CI; provision a controlled cache for real offline smoke.
Verify¶
python -c "from bertopic import BERTopic; print('ok')"
pytest tests/core/analysis/test_bertopic_shaping_helpers.py tests/contracts/test_bertopic_optional_extra.py -q
# Optional real-model smoke (downloads allowed, isolated HF cache):
# TRANSCRIPTX_DISABLE_DOWNLOADS=0 HF_HOME=/tmp/tx-hf-cache pytest tests/optional/test_bertopic_real_model_smoke.py -q
Release platforms (blocking)¶
Platform |
Python |
Role |
|---|---|---|
Linux x86_64 |
3.10, 3.11, 3.12 |
Blocking — install + smoke |
macOS Apple Silicon |
3.11 |
Blocking |
macOS Intel |
any |
Non-blocking |
Record wheel vs source compilation (esp. HDBSCAN) per platform run. Hosts that cannot install llvmlite skip the optional stack; they must not fail core / clean-env gates.