webinar-transcriber 1.3.0__tar.gz

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.
Files changed (91) hide show
  1. webinar_transcriber-1.3.0/.editorconfig +16 -0
  2. webinar_transcriber-1.3.0/.gitattributes +6 -0
  3. webinar_transcriber-1.3.0/.github/dependabot.yml +21 -0
  4. webinar_transcriber-1.3.0/.github/workflows/ci.yml +36 -0
  5. webinar_transcriber-1.3.0/.github/workflows/codeql.yml +40 -0
  6. webinar_transcriber-1.3.0/.github/workflows/release.yml +122 -0
  7. webinar_transcriber-1.3.0/.gitignore +214 -0
  8. webinar_transcriber-1.3.0/.markdownlint.json +5 -0
  9. webinar_transcriber-1.3.0/.mdformat.toml +1 -0
  10. webinar_transcriber-1.3.0/.prettierrc.json +11 -0
  11. webinar_transcriber-1.3.0/.python-version +1 -0
  12. webinar_transcriber-1.3.0/.vscode/extensions.json +6 -0
  13. webinar_transcriber-1.3.0/.vscode/settings.json +8 -0
  14. webinar_transcriber-1.3.0/AGENTS.md +188 -0
  15. webinar_transcriber-1.3.0/LICENSE +21 -0
  16. webinar_transcriber-1.3.0/LICENSE-3RDPARTY.md +27 -0
  17. webinar_transcriber-1.3.0/Makefile +77 -0
  18. webinar_transcriber-1.3.0/PKG-INFO +335 -0
  19. webinar_transcriber-1.3.0/README.md +273 -0
  20. webinar_transcriber-1.3.0/docs/assets/social-preview-dark.png +0 -0
  21. webinar_transcriber-1.3.0/docs/assets/social-preview.png +0 -0
  22. webinar_transcriber-1.3.0/docs/development.md +63 -0
  23. webinar_transcriber-1.3.0/docs/pipeline.md +92 -0
  24. webinar_transcriber-1.3.0/docs/releasing.md +42 -0
  25. webinar_transcriber-1.3.0/docs/troubleshooting.md +65 -0
  26. webinar_transcriber-1.3.0/pyproject.toml +253 -0
  27. webinar_transcriber-1.3.0/uv.lock +2092 -0
  28. webinar_transcriber-1.3.0/webinar_transcriber/__init__.py +27 -0
  29. webinar_transcriber-1.3.0/webinar_transcriber/__main__.py +8 -0
  30. webinar_transcriber-1.3.0/webinar_transcriber/_env.py +56 -0
  31. webinar_transcriber-1.3.0/webinar_transcriber/_version.py +24 -0
  32. webinar_transcriber-1.3.0/webinar_transcriber/asr/__init__.py +22 -0
  33. webinar_transcriber-1.3.0/webinar_transcriber/asr/carryover.py +21 -0
  34. webinar_transcriber-1.3.0/webinar_transcriber/asr/config.py +20 -0
  35. webinar_transcriber-1.3.0/webinar_transcriber/asr/transcriber.py +362 -0
  36. webinar_transcriber-1.3.0/webinar_transcriber/asr/windows.py +43 -0
  37. webinar_transcriber-1.3.0/webinar_transcriber/assets/__init__.py +1 -0
  38. webinar_transcriber-1.3.0/webinar_transcriber/assets/silero_vad.onnx +0 -0
  39. webinar_transcriber-1.3.0/webinar_transcriber/cli.py +132 -0
  40. webinar_transcriber-1.3.0/webinar_transcriber/diagnostics.py +41 -0
  41. webinar_transcriber-1.3.0/webinar_transcriber/diarization/__init__.py +76 -0
  42. webinar_transcriber-1.3.0/webinar_transcriber/diarization/sherpa_diarizer.py +361 -0
  43. webinar_transcriber-1.3.0/webinar_transcriber/export/__init__.py +9 -0
  44. webinar_transcriber-1.3.0/webinar_transcriber/export/docx_report.py +131 -0
  45. webinar_transcriber-1.3.0/webinar_transcriber/export/formatting.py +56 -0
  46. webinar_transcriber-1.3.0/webinar_transcriber/export/json_report.py +22 -0
  47. webinar_transcriber-1.3.0/webinar_transcriber/export/markdown.py +59 -0
  48. webinar_transcriber-1.3.0/webinar_transcriber/io.py +15 -0
  49. webinar_transcriber-1.3.0/webinar_transcriber/llm/__init__.py +19 -0
  50. webinar_transcriber-1.3.0/webinar_transcriber/llm/processor.py +270 -0
  51. webinar_transcriber-1.3.0/webinar_transcriber/llm/prompts.py +57 -0
  52. webinar_transcriber-1.3.0/webinar_transcriber/llm/providers.py +96 -0
  53. webinar_transcriber-1.3.0/webinar_transcriber/llm/types.py +37 -0
  54. webinar_transcriber-1.3.0/webinar_transcriber/llm/utils.py +238 -0
  55. webinar_transcriber-1.3.0/webinar_transcriber/media.py +159 -0
  56. webinar_transcriber-1.3.0/webinar_transcriber/models.py +278 -0
  57. webinar_transcriber-1.3.0/webinar_transcriber/normalized_audio.py +123 -0
  58. webinar_transcriber-1.3.0/webinar_transcriber/paths.py +103 -0
  59. webinar_transcriber-1.3.0/webinar_transcriber/processor.py +529 -0
  60. webinar_transcriber-1.3.0/webinar_transcriber/py.typed +0 -0
  61. webinar_transcriber-1.3.0/webinar_transcriber/segmentation.py +152 -0
  62. webinar_transcriber-1.3.0/webinar_transcriber/structure.py +197 -0
  63. webinar_transcriber-1.3.0/webinar_transcriber/tests/__init__.py +1 -0
  64. webinar_transcriber-1.3.0/webinar_transcriber/tests/conftest.py +371 -0
  65. webinar_transcriber-1.3.0/webinar_transcriber/tests/fixtures/sample-audio.mp3 +0 -0
  66. webinar_transcriber-1.3.0/webinar_transcriber/tests/fixtures/sample-video.mp4 +0 -0
  67. webinar_transcriber-1.3.0/webinar_transcriber/tests/fixtures/speech-sample.wav +0 -0
  68. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_asr.py +515 -0
  69. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_cli.py +314 -0
  70. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_coalesce.py +139 -0
  71. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_diarization.py +677 -0
  72. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_export.py +401 -0
  73. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_llm.py +752 -0
  74. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_llm_utils.py +133 -0
  75. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_media.py +191 -0
  76. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_models.py +136 -0
  77. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_normalized_audio.py +381 -0
  78. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_paths.py +66 -0
  79. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_processor.py +866 -0
  80. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_reconciliation.py +146 -0
  81. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_structure.py +339 -0
  82. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_ui.py +141 -0
  83. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_version.py +28 -0
  84. webinar_transcriber-1.3.0/webinar_transcriber/tests/test_video_pipeline.py +207 -0
  85. webinar_transcriber-1.3.0/webinar_transcriber/text_utils.py +38 -0
  86. webinar_transcriber-1.3.0/webinar_transcriber/transcript/__init__.py +8 -0
  87. webinar_transcriber-1.3.0/webinar_transcriber/transcript/coalesce.py +85 -0
  88. webinar_transcriber-1.3.0/webinar_transcriber/transcript/reconcile.py +99 -0
  89. webinar_transcriber-1.3.0/webinar_transcriber/ui.py +181 -0
  90. webinar_transcriber-1.3.0/webinar_transcriber/video/__init__.py +15 -0
  91. webinar_transcriber-1.3.0/webinar_transcriber/video/scenes.py +214 -0
@@ -0,0 +1,16 @@
1
+ root = true
2
+
3
+ [*.md]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ max_line_length = 100
8
+ trim_trailing_whitespace = false
9
+
10
+ [*.sh]
11
+ indent_style = space
12
+ indent_size = 4
13
+
14
+ [*.json]
15
+ indent_style = space
16
+ indent_size = 4
@@ -0,0 +1,6 @@
1
+ * text=auto
2
+ *.md text eol=lf
3
+ *.py text eol=lf
4
+ *.toml text eol=lf
5
+ *.yml text eol=lf
6
+ *.yaml text eol=lf
@@ -0,0 +1,21 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ open-pull-requests-limit: 5
8
+ groups:
9
+ python-dependencies:
10
+ patterns:
11
+ - "*"
12
+
13
+ - package-ecosystem: "github-actions"
14
+ directory: "/"
15
+ schedule:
16
+ interval: "weekly"
17
+ open-pull-requests-limit: 5
18
+ groups:
19
+ github-actions:
20
+ patterns:
21
+ - "*"
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ lint-test:
13
+ name: lint-test (${{ matrix.os }})
14
+ runs-on: ${{ matrix.os }}
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-latest, macos-latest]
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ with:
22
+ fetch-depth: 0
23
+ - uses: actions/setup-python@v6
24
+ with:
25
+ python-version: "3.12"
26
+ - uses: astral-sh/setup-uv@v7
27
+ - name: Sync dependencies
28
+ run: uv sync --extra llm --group dev
29
+ - name: Smoke CLI help
30
+ run: uv run webinar-transcriber --help
31
+ - name: Smoke whisper runtime
32
+ run: uv run python -c "from pywhispercpp.model import Model; assert Model.system_info().strip()"
33
+ - name: Lint
34
+ run: make lint
35
+ - name: Test
36
+ run: make test-all
@@ -0,0 +1,40 @@
1
+ name: CodeQL
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ - cron: "24 5 * * 2"
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ analyze:
16
+ name: Analyze (${{ matrix.language }})
17
+ runs-on: ubuntu-latest
18
+ permissions:
19
+ actions: read
20
+ contents: read
21
+ security-events: write
22
+
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ language: [actions, python]
27
+
28
+ steps:
29
+ - name: Checkout repository
30
+ uses: actions/checkout@v6
31
+
32
+ - name: Initialize CodeQL
33
+ uses: github/codeql-action/init@v4
34
+ with:
35
+ languages: ${{ matrix.language }}
36
+
37
+ - name: Perform CodeQL Analysis
38
+ uses: github/codeql-action/analyze@v4
39
+ with:
40
+ category: "/language:${{ matrix.language }}"
@@ -0,0 +1,122 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ validate:
11
+ name: validate (${{ matrix.os }})
12
+ permissions:
13
+ contents: read
14
+ runs-on: ${{ matrix.os }}
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ os: [ubuntu-latest, macos-latest]
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ with:
22
+ fetch-depth: 0
23
+
24
+ - uses: actions/setup-python@v6
25
+ with:
26
+ python-version: "3.12"
27
+
28
+ - uses: astral-sh/setup-uv@v7
29
+
30
+ - name: Sync dependencies
31
+ run: uv sync --extra llm --group dev
32
+
33
+ - name: Smoke CLI help
34
+ run: uv run webinar-transcriber --help
35
+
36
+ - name: Smoke whisper runtime
37
+ run: uv run python -c "from pywhispercpp.model import Model; assert Model.system_info().strip()"
38
+
39
+ - name: Lint
40
+ run: make lint
41
+
42
+ - name: Test
43
+ run: make test-all
44
+
45
+ release:
46
+ needs: validate
47
+ runs-on: ubuntu-latest
48
+ permissions:
49
+ contents: write
50
+ steps:
51
+ - uses: actions/checkout@v6
52
+ with:
53
+ fetch-depth: 0
54
+
55
+ - uses: actions/setup-python@v6
56
+ with:
57
+ python-version: "3.12"
58
+
59
+ - uses: astral-sh/setup-uv@v7
60
+
61
+ - name: Sync dependencies
62
+ run: uv sync --extra llm --group dev
63
+
64
+ - name: Build package
65
+ run: uv build
66
+
67
+ - name: Upload artifacts
68
+ uses: actions/upload-artifact@v7
69
+ with:
70
+ name: dist
71
+ path: |
72
+ dist/*.whl
73
+ dist/*.tar.gz
74
+
75
+ - name: Create GitHub Release
76
+ if: github.event_name == 'push'
77
+ uses: softprops/action-gh-release@v3
78
+ with:
79
+ files: |
80
+ dist/*.whl
81
+ dist/*.tar.gz
82
+
83
+ publish-testpypi:
84
+ needs: release
85
+ if: github.event_name == 'workflow_dispatch'
86
+ runs-on: ubuntu-latest
87
+ environment:
88
+ name: testpypi
89
+ url: https://test.pypi.org/p/webinar-transcriber
90
+ permissions:
91
+ id-token: write
92
+ steps:
93
+ - name: Download built distributions
94
+ uses: actions/download-artifact@v7
95
+ with:
96
+ name: dist
97
+ path: dist
98
+
99
+ - name: Publish to TestPyPI
100
+ uses: pypa/gh-action-pypi-publish@release/v1
101
+ with:
102
+ repository-url: https://test.pypi.org/legacy/
103
+ skip-existing: true
104
+
105
+ publish-pypi:
106
+ needs: release
107
+ if: github.event_name == 'push'
108
+ runs-on: ubuntu-latest
109
+ environment:
110
+ name: pypi
111
+ url: https://pypi.org/p/webinar-transcriber
112
+ permissions:
113
+ id-token: write
114
+ steps:
115
+ - name: Download built distributions
116
+ uses: actions/download-artifact@v7
117
+ with:
118
+ name: dist
119
+ path: dist
120
+
121
+ - name: Publish to PyPI
122
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,214 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+ webinar_transcriber/_version.py
54
+ runs/
55
+ models/.DS_Store
56
+ models/whisper-cpp/*.bin
57
+ models/whisper-cpp/*.wav
58
+ models/whisper-cpp/*.json
59
+
60
+ # Translations
61
+ *.mo
62
+ *.pot
63
+
64
+ # Django stuff:
65
+ *.log
66
+ local_settings.py
67
+ db.sqlite3
68
+ db.sqlite3-journal
69
+
70
+ # Flask stuff:
71
+ instance/
72
+ .webassets-cache
73
+
74
+ # Scrapy stuff:
75
+ .scrapy
76
+
77
+ # Sphinx documentation
78
+ docs/_build/
79
+
80
+ # PyBuilder
81
+ .pybuilder/
82
+ target/
83
+
84
+ # Jupyter Notebook
85
+ .ipynb_checkpoints
86
+
87
+ # IPython
88
+ profile_default/
89
+ ipython_config.py
90
+
91
+ # pyenv
92
+ # For a library or package, you might want to ignore these files since the code is
93
+ # intended to run in multiple environments; otherwise, check them in:
94
+ # .python-version
95
+ .DS_Store
96
+
97
+ # pipenv
98
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
99
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
100
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
101
+ # install all needed dependencies.
102
+ #Pipfile.lock
103
+
104
+ # UV
105
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ #uv.lock
109
+
110
+ # poetry
111
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
112
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
113
+ # commonly ignored for libraries.
114
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
115
+ #poetry.lock
116
+ #poetry.toml
117
+
118
+ # pdm
119
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
120
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
121
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
122
+ #pdm.lock
123
+ #pdm.toml
124
+ .pdm-python
125
+ .pdm-build/
126
+
127
+ # pixi
128
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
129
+ #pixi.lock
130
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
131
+ # in the .venv directory. It is recommended not to include this directory in version control.
132
+ .pixi
133
+
134
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
135
+ __pypackages__/
136
+
137
+ # Celery stuff
138
+ celerybeat-schedule
139
+ celerybeat.pid
140
+
141
+ # SageMath parsed files
142
+ *.sage.py
143
+
144
+ # Environments
145
+ .env
146
+ .envrc
147
+ .venv
148
+ env/
149
+ venv/
150
+ ENV/
151
+ env.bak/
152
+ venv.bak/
153
+
154
+ # Spyder project settings
155
+ .spyderproject
156
+ .spyproject
157
+
158
+ # Rope project settings
159
+ .ropeproject
160
+
161
+ # mkdocs documentation
162
+ /site
163
+
164
+ # mypy
165
+ .mypy_cache/
166
+ .dmypy.json
167
+ dmypy.json
168
+
169
+ # Pyre type checker
170
+ .pyre/
171
+
172
+ # pytype static type analyzer
173
+ .pytype/
174
+
175
+ # Cython debug symbols
176
+ cython_debug/
177
+
178
+ # PyCharm
179
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
180
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
181
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
182
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
183
+ #.idea/
184
+
185
+ # Abstra
186
+ # Abstra is an AI-powered process automation framework.
187
+ # Ignore directories containing user credentials, local state, and settings.
188
+ # Learn more at https://abstra.io/docs
189
+ .abstra/
190
+
191
+ # Visual Studio Code
192
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
193
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
194
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
195
+ # you could uncomment the following to ignore the entire vscode folder
196
+ # .vscode/
197
+
198
+ # Ruff stuff:
199
+ .ruff_cache/
200
+
201
+ # PyPI configuration file
202
+ .pypirc
203
+
204
+ # Cursor
205
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
206
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
207
+ # refer to https://docs.cursor.com/context/ignore-files
208
+ .cursorignore
209
+ .cursorindexingignore
210
+
211
+ # Marimo
212
+ marimo/_static/
213
+ marimo/_lsp/
214
+ __marimo__/
@@ -0,0 +1,5 @@
1
+ {
2
+ "MD013": {
3
+ "line_length": 100
4
+ }
5
+ }
@@ -0,0 +1 @@
1
+ wrap = 100
@@ -0,0 +1,11 @@
1
+ {
2
+ "overrides": [
3
+ {
4
+ "files": "*.md",
5
+ "options": {
6
+ "printWidth": 100,
7
+ "proseWrap": "preserve"
8
+ }
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "charliermarsh.ruff",
4
+ "ms-python.python"
5
+ ]
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "python.testing.pytestArgs": [
3
+ "--no-cov",
4
+ "webinar_transcriber/tests"
5
+ ],
6
+ "python.testing.unittestEnabled": false,
7
+ "python.testing.pytestEnabled": true
8
+ }
@@ -0,0 +1,188 @@
1
+ # AGENTS
2
+
3
+ This file is the contributor and coding-agent guide for this repository.
4
+
5
+ ## Scope
6
+
7
+ Documentation is organized by [Diátaxis](https://diataxis.fr) mode. Decide where content goes by
8
+ what it serves, not by which file is nearest:
9
+
10
+ - `README.md`: the entry point. A how-to quick start, user-facing reference (CLI flags, environment
11
+ variables, output layout), and a brief explanation of what the tool is.
12
+ - `docs/pipeline.md`: explanation. How and why the stages work; no step-by-step instructions and no
13
+ flag tables.
14
+ - `docs/troubleshooting.md`: how-to. Error-indexed fixes.
15
+ - `docs/development.md`: how-to and reference. Checkout setup, running from source, and the
16
+ make-target and quality-gate reference.
17
+ - `docs/releasing.md`: how-to. The release process.
18
+ - `AGENTS.md`: contributor and coding-agent process the user docs do not need.
19
+
20
+ When unsure, place content by Diátaxis axis: action for someone working is how-to, information for
21
+ someone working is reference, understanding is explanation. Keep modes separate within a file: do
22
+ not fold explanation into a how-to, or a flag table into the pipeline explanation.
23
+
24
+ Prefer extending `README.md` first. Add a new file under `docs/` only when a section is long enough
25
+ to obscure the user-facing flow and self-contained enough to read on its own.
26
+
27
+ Repository-visible assets stay under `docs/assets/`. The README preview uses GitHub's theme
28
+ suffixes: `social-preview.png#gh-light-mode-only` and `social-preview-dark.png#gh-dark-mode-only`.
29
+ Keep `social-preview-dark.png` suitable for the repository social-preview setting.
30
+
31
+ ## Tooling
32
+
33
+ Keep setup docs and Makefile targets in sync. If you add, rename, or remove a `make install*`
34
+ target, update README in the same change. If you add, rename, or remove a `make sync*` target or
35
+ quality-gate target, update `docs/development.md` in the same change.
36
+
37
+ Use `make test` for quick iteration (fast subset, skips slow tests, no coverage gate). Run
38
+ `make format` then `make check` before committing — `make check` is the full coverage-gated gate.
39
+
40
+ GitHub Actions runs `make lint` and `make test-all` so CI mirrors the local quality gate. CI runs on
41
+ `ubuntu-latest` and `macos-latest`; Windows is not part of the CI matrix and is treated as
42
+ best-effort.
43
+
44
+ In sandboxed Codex runs, prefer `UV_CACHE_DIR=/tmp/uv-cache` for `uv` and `make` commands to avoid
45
+ cache-permission failures.
46
+
47
+ ## Definition of Done
48
+
49
+ A change is ready to merge when all of the following hold:
50
+
51
+ - **Quality gates pass.** `make format && make check` runs cleanly — the full coverage-gated test
52
+ suite, `ruff`, `ty`, and the markdown linters.
53
+ - **100% coverage stays.** Add `# pragma: no cover` to genuinely unreachable defensive branches
54
+ rather than lowering the threshold.
55
+ - **Docs stay in sync.** If the change affects CLI flags, output artifacts, install targets, package
56
+ layout, or runtime contracts, update `README.md` and `AGENTS.md` in the same PR.
57
+ - **No dead code.** Remove unused imports, helpers with no callers, fields that are written or
58
+ serialized but never read back, and `# TODO` comments that will not be addressed in this PR.
59
+ - **PR has a summary and a test plan.** Each pull request description includes a brief summary and
60
+ an explicit test-plan checklist.
61
+ - **Commits follow Conventional Commits.** Prefix commit messages with `refactor:`, `docs:`, `ci:`,
62
+ `style:`, `feat:`, `fix:`, `test:`, `build:`, or `chore:`. Use an optional scope when it adds
63
+ clarity, for example `build(deps):`.
64
+
65
+ ## Package Layout
66
+
67
+ The package is deliberately flat: shallow subpackages by domain (`asr`, `diarization`, `llm`,
68
+ `transcript`, `video`, `export`) with leaf modules at the root. Read the tree directly rather than
69
+ maintaining a copy here, and avoid adding deep nesting.
70
+
71
+ ## Runtime Contracts
72
+
73
+ - Successful default CLI runs write the report artifact set described in `README.md`.
74
+ - `--diarize` runs locally and adds `diarization.json` plus speaker fields on transcript segments.
75
+ - Successful runs write `diagnostics.json`; failed runs also write it once the run directory exists,
76
+ though early failures can still leave only partial intermediate artifacts and no final report
77
+ outputs.
78
+ - Temporary WAV audio extracted for transcription should stay outside the run directory;
79
+ `--keep-audio` may write the compressed `transcription-audio.mp3` artifact described in
80
+ `README.md`.
81
+
82
+ ## Testing Notes
83
+
84
+ - Keep tests under `webinar_transcriber/tests/`, mirroring the package hierarchy in filenames and
85
+ grouping where it helps scanability. Split a test file into a sibling when it crosses roughly 800
86
+ lines or when the production-side module it covers has already split — for example, `llm/utils.py`
87
+ and `llm/processor.py` map to `test_llm_utils.py` and `test_llm.py`.
88
+ - Prefer tiny deterministic fixtures committed to the repo.
89
+ - Prefer `pytest` `monkeypatch` for simple state replacement and `unittest.mock.patch` when a test
90
+ needs mock semantics such as `side_effect`, `return_value`, or call assertions.
91
+ - Group related tests by behavior using `class Test...` when it improves scanability; keep helpers
92
+ and fakes scoped to the narrowest test class that uses them, and leave them module-level only when
93
+ they are intentionally shared across multiple test classes or fixtures.
94
+ - CLI tests should generally monkeypatch the reporter and heavy runtime seams instead of invoking
95
+ real media-processing work.
96
+ - Use `pytest.mark.parametrize` when cases share the same setup and assertions and the test
97
+ naturally becomes a clear input/output table; avoid it when cases need materially different
98
+ fixtures, monkeypatching, or fake-object wiring.
99
+ - Prefer asserting current observable behavior over asserting that recently removed options, fields,
100
+ dependencies, import paths, or artifacts are absent. Deletion-based assertions are usually shallow
101
+ and brittle.
102
+ - Avoid tests that only prove one function forwards fields into another helper unless that wiring is
103
+ itself a meaningful behavioral contract.
104
+ - For predicate-style tests, prefer `assert expr` / `assert not expr`. Reserve `is True` /
105
+ `is False` for cases where exact boolean identity matters.
106
+
107
+ ## Implementation Notes
108
+
109
+ - When an earlier pipeline stage already guarantees an invariant, prefer enforcing that invariant
110
+ directly instead of carrying defensive conversion logic downstream. For example, transcription
111
+ audio is normalized to `16000 Hz`, so the `sherpa-onnx` Silero VAD integration should assert that
112
+ contract rather than silently resample.
113
+ - Let genuine errors propagate. Reserve warnings for recoverable, user-actionable conditions; do not
114
+ downgrade a real failure (a failed image write, a missing required stream) into a warning that the
115
+ run then has to thread around.
116
+ - The Silero VAD ONNX model is vendored under `webinar_transcriber/assets/` so default speech-region
117
+ detection does not require PyTorch or a first-run network download.
118
+ - Speaker diarization uses `sherpa-onnx` with downloaded ONNX models cached under
119
+ `~/.cache/webinar-transcriber/diarization`; keep those model artifacts out of the wheel.
120
+
121
+ ## Simplification and Refactoring Notes
122
+
123
+ This repo has improved repeatedly by making complexity justify itself, not the reverse. In review
124
+ the maintainer keeps pushing past a first "this is load-bearing" reading and is usually right. Carry
125
+ that prior: an abstraction must earn its keep, the default answer to "can this be simpler" is yes
126
+ until the code proves otherwise, and a challenge to a conservative answer is a request for evidence.
127
+ Grep the real readers and callers, then produce a verified cleanup or give the precise structural
128
+ reason it can't be done and act rather than re-explain. A small flag-free helper is fine when a
129
+ linter genuinely forces extraction; a state flag or multi-helper split to dodge one is
130
+ over-engineering.
131
+
132
+ Recurring smells to check for:
133
+
134
+ - **Test-only knobs** — a param/field whose only non-default caller is a test; delete it and let
135
+ tests monkeypatch the constant or exercise the real contract.
136
+ - **1:1 wrappers / single-caller helpers** that only rename one shape or forward one call; inline at
137
+ the call site.
138
+ - **Write-only fields** serialized or assigned but never read back; delete unless an external
139
+ artifact contract consumes them.
140
+ - **Duplicated diagnostics or serialization** — one value under two keys or two artifacts; pick the
141
+ single owner.
142
+ - **Wrapper / result-object chains** threading one value through re-keying passes, or a plan/spec
143
+ wrapping a `min()`/`len()`; collapse to one shape or a function.
144
+ - **Re-export / lazy-import surfaces with no production consumer**; import from the defining leaf
145
+ module, keeping a package re-export only to isolate an optional extra or a real public boundary.
146
+ - **Protocols for a single in-tree implementation**; type the collaborator concretely and let tests
147
+ use duck-typed fakes. A Protocol earns its place only at a third-party or polymorphic seam.
148
+ - **Fakes re-implementing production logic**; replace with a Mock-based double that runs the real
149
+ policy or object, so the test proves production behavior.
150
+ - **Defensive branches guarding states an earlier stage forbids** (a guard on a positive constant, a
151
+ `getattr` default masking a missing attribute); assert the invariant or delete the branch.
152
+ - **Errors downgraded to warnings** the run then threads around; let them propagate, reserving
153
+ warnings for recoverable, user-actionable conditions.
154
+ - **Config bags between exactly two callers**; inline as kwargs or fields.
155
+ - **Stale suppressions** — ruff ignores with zero hits, `type: ignore` / `cast()` that narrow away
156
+ with `isinstance`; re-enable or narrow, keeping only documented stub gaps.
157
+
158
+ When acting: put ownership at the boundary that already knows the value (the CLI builds
159
+ collaborators, stages record their own diagnostics, ASR owns window policy, export/report models
160
+ carry no diagnostics-only data); prefer the narrowest cohesive cleanup with a clear before/after
161
+ story; verify with a byte-identical fixture-artifact diff where possible over tests that only prove
162
+ one helper forwarded to another; and make any user-visible contract change (CLI flags, artifact
163
+ paths, JSON shapes, diagnostics or stage keys) explicit in the PR with `README.md` and `AGENTS.md`
164
+ updated in the same change.
165
+
166
+ ## Style Notes
167
+
168
+ - `cast()` is a runtime no-op and a smell when used purely to satisfy the type checker. Prefer
169
+ `isinstance()` checks for narrowing. Reserve `cast()` for genuine third-party stub gaps and always
170
+ add a comment explaining which stub is missing and why.
171
+ - `# type: ignore` is the same smell. Only use it for genuine stub gaps, bare (no bracket error
172
+ codes — `ty` does not use mypy's `[attr-defined]` syntax), with a comment on the same or preceding
173
+ line.
174
+ - When adapting third-party types that don't stub well, define a private `Protocol`. If `Any` is
175
+ unavoidable in a Protocol method signature due to stub-level invariance constraints, suppress ruff
176
+ with `# noqa: ANN401` and add a comment.
177
+ - Bind caught exceptions as `ex` — `except SomeError as ex:` — never `as error:` or `as exc:`. The
178
+ same applies to `pytest.raises(...) as ex` in tests.
179
+ - Replace `assert x is not None` guards in production code with explicit `if x is None: raise`.
180
+ `assert` is stripped by optimised builds and banned by ruff S101.
181
+ - Use the walrus operator selectively when it removes repeated work without making the code harder
182
+ to read.
183
+ - Prefer short loop and comprehension variable names when the surrounding context already makes the
184
+ meaning obvious.
185
+ - In ASR code and artifacts, use `speech region` for VAD/planning inputs and `window` for Whisper
186
+ decode units; avoid `chunk` for ASR concepts.
187
+ - Treat acronyms as words in PascalCase identifiers: `LlmProcessor`, `AsrPipelineDiagnostics`,
188
+ `WhisperCppTranscriber`. Constants stay SCREAMING_SNAKE: `ASR_BACKEND_NAME`, `LLM_PROVIDER_ENV`.