textflowkit 0.1.7__tar.gz → 0.1.9__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.
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.github/workflows/ci.yml +5 -1
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.github/workflows/publish-pypi.yml +17 -5
- textflowkit-0.1.9/CHANGELOG.md +217 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/Dockerfile +6 -4
- {textflowkit-0.1.7 → textflowkit-0.1.9}/PKG-INFO +112 -17
- {textflowkit-0.1.7 → textflowkit-0.1.9}/README.md +108 -15
- {textflowkit-0.1.7 → textflowkit-0.1.9}/compose.yaml +4 -3
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/adapters.md +30 -18
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/architecture.md +24 -6
- textflowkit-0.1.9/docs/assets/architecture-overview.svg +189 -0
- textflowkit-0.1.9/docs/assets/whistle-resume.svg +164 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/index.html +19 -5
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/install.md +81 -27
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/release-checklist.md +21 -11
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/roadmap.md +57 -21
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/user-manual.md +125 -21
- {textflowkit-0.1.7 → textflowkit-0.1.9}/pyproject.toml +11 -5
- {textflowkit-0.1.7 → textflowkit-0.1.9}/scripts/smoke_live_youtube.py +28 -9
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/__init__.py +1 -1
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/adapters/http_server.py +7 -5
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/adapters/mcp_server.py +29 -19
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/cli.py +202 -39
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/checkpoint.py +226 -23
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/engine.py +201 -62
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/pipeline.py +193 -41
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/runner.py +3 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/submission.py +73 -6
- textflowkit-0.1.9/src/textflowkit/core/whistle.py +1198 -0
- textflowkit-0.1.9/src/textflowkit/core/whistle_assets.py +505 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_adapters.py +9 -5
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_admission_ownership.py +2 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_atomic_resume.py +9 -9
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_atomic_resume_aba.py +3 -3
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_batch.py +5 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_batch_item_boundary.py +2 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_batch_resume_shared.py +3 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_checkpoint.py +77 -5
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_checkpoint_single_copy.py +9 -3
- textflowkit-0.1.9/tests/test_cli.py +228 -0
- textflowkit-0.1.9/tests/test_cli_owning_recovery.py +300 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_cli_stage_feedback.py +2 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_diarize.py +3 -3
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_docx_publication_identity.py +2 -1
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_done_output_integrity.py +6 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_empty_formats_resume.py +3 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_engine_adapters.py +14 -14
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_engine_faster.py +20 -10
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_fetch_published_fonts.py +41 -2
- textflowkit-0.1.9/tests/test_mcp_batch_cookie_docs.py +98 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_partial_output_resume.py +3 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_partial_pdf_resume.py +1 -0
- textflowkit-0.1.9/tests/test_pipeline_resume_setup.py +270 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_publish_fonts_wiring.py +54 -4
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_publish_manifest_wiring.py +95 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_release_boundaries.py +10 -7
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_stage_reuse_resume.py +1 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_submission.py +7 -2
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_submission_validation.py +13 -7
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_translate.py +3 -3
- textflowkit-0.1.9/tests/test_whistle_assets.py +490 -0
- textflowkit-0.1.9/tests/test_whistle_defaults.py +661 -0
- textflowkit-0.1.9/tests/test_whistle_engine.py +1027 -0
- textflowkit-0.1.9/tests/test_whistle_job_resume.py +620 -0
- textflowkit-0.1.7/CHANGELOG.md +0 -100
- textflowkit-0.1.7/tests/test_cli.py +0 -105
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.dockerignore +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.env.example +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.gitattributes +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.github/dependabot.yml +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.gitignore +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/.pre-commit-config.yaml +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/CONTRIBUTING.md +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/LEGAL.md +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/LICENSE +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/SECURITY.md +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/.nojekyll +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/404.html +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/site-deployment.md +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/docs/sources.md +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/scripts/fetch_published_fonts.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/scripts/smoke_installed_wheel.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/scripts/verify_readme_release.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/scripts/verify_release_ci.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/scripts/verify_release_versions.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/scripts/write_release_manifest.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/adapters/__init__.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/assets/README.md +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/assets/selftest-speech.wav +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/__init__.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/batch.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/bind.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/cancel.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/diarize.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/executor.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/jobs.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/model.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/paths.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/retrieval.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/service.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/sqlite_store.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/startup.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/timeutil.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/core/translate.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/__init__.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/_cue_layout.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/_cue_text.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/_rename_excl.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/_rename_noreplace.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/docx.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/markdown.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/pdf.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/srt.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/txt.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/render/vtt.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/sources/__init__.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/sources/acquire.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/sources/detect.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/src/textflowkit/sources/scratch.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/__init__.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/ollama_stub.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_acquire.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_bind.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_cancellation_finalization.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_cli_batch_feedback.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_cli_resume_batch.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_cli_stdout_format.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_confined_decoder_boundary.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_confined_indirect_media.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_confined_resume_boundary.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_container_example.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_cue_layout.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_diarize_example_flags.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_diarize_token_docs.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_engine_cache.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_engine_quiet.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_executor.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_executor_store_failure.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_export.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_export_file_mode.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_export_preflight.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_faster_whisper_docs.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_font_notices.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_http_host_boundary.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_http_trusted_proxy.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_job_progress.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_jobs.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_landing_commands.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_landing_page.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_legal_summary.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_linux_no_hardlink.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_live_smoke_script.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_macos_no_hardlink.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_mcp_http_boundary.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_mcp_protocol_cleanliness.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_mcp_terminal_guidance.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_mcp_tool_descriptions.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_mcp_work_root.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_model.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_paths.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_pipeline_integrity.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_production_browser_cookies.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_publish_ci_gate.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_publish_workflow_gate.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_readme_layout.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_readme_release_guard.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_release_manifest.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_release_surfaces.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_render.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_retrieval.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_sdist_allowlist.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_service_profile.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_site_not_found.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_sources.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_startup_recovery.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_stdio_protocol.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_translate_completeness.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_verify_release_versions.py +0 -0
- {textflowkit-0.1.7 → textflowkit-0.1.9}/tests/test_windows_no_hardlink.py +0 -0
|
@@ -58,7 +58,11 @@ jobs:
|
|
|
58
58
|
run: |
|
|
59
59
|
python -m pip install --upgrade pip
|
|
60
60
|
python -m pip install ./packages/textflowkit-fonts
|
|
61
|
-
|
|
61
|
+
# `whisper` is installed here because the existing engine tests
|
|
62
|
+
# (openai-whisper's verbose/quiet contract, model-name validation)
|
|
63
|
+
# genuinely need the openai-whisper package present. It is an optional
|
|
64
|
+
# extra now that Whistle is the default engine, so it must be named.
|
|
65
|
+
python -m pip install -e ".[dev,mcp,http,export,whisper]"
|
|
62
66
|
|
|
63
67
|
- name: Show ffmpeg version
|
|
64
68
|
shell: bash
|
|
@@ -149,12 +149,24 @@ jobs:
|
|
|
149
149
|
packages-dir: dist/main/
|
|
150
150
|
|
|
151
151
|
publish-github-release:
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
152
|
+
# The release exists only to advertise a core version that is on PyPI, so it
|
|
153
|
+
# runs exactly on a successful core upload and nothing else. The condition is
|
|
154
|
+
# written out rather than left implicit for the same reason as the core
|
|
155
|
+
# upload above: a status check function must defeat skip propagation. An
|
|
156
|
+
# implicit `success()` here is still subject to it, and on a reused fonts
|
|
157
|
+
# release the workflow-wide propagation from the *skipped* fonts job reaches
|
|
158
|
+
# this job through `publish-main` and its skipped fonts dependency - v0.1.7
|
|
159
|
+
# initially skipped the release even though core reached PyPI, and the
|
|
160
|
+
# recovery published it.
|
|
161
|
+
#
|
|
162
|
+
# `always()` would also defeat the propagation, but it runs the job in a
|
|
163
|
+
# cancelled run too, so it is not used. Only a `success` core upload reaches
|
|
164
|
+
# the release: a failed, skipped or cancelled core upload, a cancelled run,
|
|
165
|
+
# and a build that did not succeed all leave no public release, so the
|
|
166
|
+
# release can never advertise a core version that is not on PyPI.
|
|
157
167
|
needs: publish-main
|
|
168
|
+
if: >-
|
|
169
|
+
${{ !cancelled() && needs.publish-main.result == 'success' }}
|
|
158
170
|
runs-on: ubuntu-latest
|
|
159
171
|
permissions:
|
|
160
172
|
contents: write
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.1.9 — 2026-10-05
|
|
4
|
+
|
|
5
|
+
**Whistle is now the default engine.** This release ships the Whistle integration
|
|
6
|
+
prepared on the `feat/whistle-primary` branch.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- **Whistle is the default engine; openai-whisper becomes an explicit opt-in.**
|
|
11
|
+
Whistle is a CPU-only native CLI that needs no PyTorch, so a fresh
|
|
12
|
+
`pip install textflowkit` no longer pulls the torch stack. `openai-whisper`
|
|
13
|
+
moves to a new optional `whisper` extra and is selected with `--engine whisper`
|
|
14
|
+
(or `engine="whisper"` on the Python/MCP/HTTP surfaces); the `all` extra still
|
|
15
|
+
includes it. A saved request or checkpoint from before this change keeps running
|
|
16
|
+
on the engine it names, so legacy Whisper work is never silently migrated, and
|
|
17
|
+
legacy engine aliases are preserved so older decoders keep working.
|
|
18
|
+
- **Engine selection is never silent.** An unknown engine name, an unsupported
|
|
19
|
+
language, or a non-CPU device for Whistle is refused at request construction —
|
|
20
|
+
before any media is fetched — and the message names `--engine whisper` as the
|
|
21
|
+
GPU alternative. The engine is not switched to satisfy a request.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Whistle engine.** Seven advertised languages (`en`, `de`, `fr`, `es`, `it`,
|
|
26
|
+
`nl`, `pl`); native Windows x86-64/arm64, Linux x86-64/arm64, and Apple Silicon
|
|
27
|
+
(an Intel Mac is refused and pointed at the explicit Whisper engine); no WSL.
|
|
28
|
+
One pinned native binary and one pinned model are downloaded and hash-verified
|
|
29
|
+
on first use, outside the package. Long audio is split into 26-second cores with
|
|
30
|
+
up to 2 seconds of context (each clip ≤ 30 s); a word is owned by the clip whose
|
|
31
|
+
core contains its midpoint, so repeated phrases are preserved. Native
|
|
32
|
+
`--audio-stream` is never used.
|
|
33
|
+
- **Durable per-block resume for Whistle.** A long run persists a partial
|
|
34
|
+
checkpoint after each core; a resume validates the source and decoded-WAV
|
|
35
|
+
hashes and the configuration, then re-runs only the unfinished blocks and adopts
|
|
36
|
+
the rest. A partial body is Whistle-only. Reuses the existing single SQLite
|
|
37
|
+
store and its one-owning-process model.
|
|
38
|
+
- **Prompt, bounded cancellation.** A running Whistle run polls for cancellation
|
|
39
|
+
between clips and terminates its exact owned child process, bounded by a
|
|
40
|
+
per-clip timeout and a bounded stdout cap. A single long `openai-whisper` model
|
|
41
|
+
call is still cancellable only at its next stage boundary.
|
|
42
|
+
- **Telemetry off, unconditionally.** Every Whistle child process forces
|
|
43
|
+
`NEEDLE_TELEMETRY=0`, `DO_NOT_TRACK=1`, and `CI=1`, overriding a parent that
|
|
44
|
+
opted in, and there is no opt-in setting. The product adds no analytics, usage
|
|
45
|
+
SDK, anonymous ids, or events. `TEXTFLOWKIT_OFFLINE` refuses downloads and
|
|
46
|
+
`TEXTFLOWKIT_MODELS_DIR` chooses the asset directory; the pinned first-use
|
|
47
|
+
asset/model download is not telemetry.
|
|
48
|
+
|
|
49
|
+
### Documentation
|
|
50
|
+
|
|
51
|
+
- README, user manual, install/adapter/architecture/roadmap/release docs and the
|
|
52
|
+
website source now describe Whistle as the shipped default. ROCm (and the
|
|
53
|
+
torch-pinning caveat) is scoped to the explicit `whisper` engine and
|
|
54
|
+
diarization, never to Whistle.
|
|
55
|
+
- Self-contained checked-in SVG architecture drawings were added under
|
|
56
|
+
`docs/assets` and embedded in the README, both manuals (`user-manual.md` and the
|
|
57
|
+
renamed **Developer and integration manual**, `adapters.md`), the architecture
|
|
58
|
+
guide, and the landing page.
|
|
59
|
+
- The live YouTube release smoke pins `--engine whisper --model tiny` so its
|
|
60
|
+
measured default-clip word stays reproducible across the default-engine change.
|
|
61
|
+
|
|
62
|
+
### Verification boundary
|
|
63
|
+
|
|
64
|
+
Local evidence only: a 4-hour (14,407-second) CPU run produced 555
|
|
65
|
+
clips / 34,596 words / 2,875 segments in 731 seconds, offline with telemetry
|
|
66
|
+
forced off, and a separate two-process run proved durable block resume. These are
|
|
67
|
+
first local test numbers on one machine — not a universal performance claim, and
|
|
68
|
+
not a claim that Whistle's accuracy equals Whisper's. No human-scored
|
|
69
|
+
word-error rate is claimed.
|
|
70
|
+
|
|
71
|
+
## v0.1.8 — 2026-10-02
|
|
72
|
+
|
|
73
|
+
The 2026-10-02 audit-lite repair release. Core version: 0.1.8; unchanged optional fonts: 0.1.6. It carries the four fixes for the post-release audit-lite of 2026-10-01 (findings AL-001 – AL-004). Release artifacts and publication status are recorded on [GitHub Releases](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.8). Local source-candidate verification does not establish installed-package or individual harness compatibility.
|
|
74
|
+
|
|
75
|
+
### Engineering fixes
|
|
76
|
+
|
|
77
|
+
- **AL-001 — CLI owning-process startup recovery.** A CLI invocation that was
|
|
78
|
+
interrupted left its PENDING/RUNNING row active; because the CLI submits with
|
|
79
|
+
`background=False` it never started the recovering executor, so a later
|
|
80
|
+
`--resume` read the orphan as "already active" and refused a job whose expensive
|
|
81
|
+
work was saved. Recovery now runs once per owning process at the CLI's
|
|
82
|
+
process/store boundary (`cli.main`), before single or batch resume selection,
|
|
83
|
+
through the shared per-store owner. It is deliberately **not** an unconditional
|
|
84
|
+
reap in every `submit_request(background=False)`: an embedding process can hold
|
|
85
|
+
genuinely live work. A recovery write failure is reported as a clear error with
|
|
86
|
+
a nonzero exit, not a false success or a raw traceback. The documented
|
|
87
|
+
one-owning-process-per-database model is unchanged.
|
|
88
|
+
- **AL-002 — preserve the completed transcript across resume setup failures.** The
|
|
89
|
+
source checkpoint serialized the local transcript (still `None`) before
|
|
90
|
+
hydrating the previous transcript, so a directory/`mkdtemp` failure replaced a
|
|
91
|
+
durable completed transcript with `null` while still listing `transcribe` as
|
|
92
|
+
finished. Reuse is now hydrated and validated **before** any directory setup,
|
|
93
|
+
and a replacement snapshot can no longer drop a still-finished transcript. A
|
|
94
|
+
changed local source is still refused, stages are never falsely marked complete,
|
|
95
|
+
and a healthy retry reuses the recognized work.
|
|
96
|
+
|
|
97
|
+
### Documentation corrections
|
|
98
|
+
|
|
99
|
+
- **AL-003 — MCP batch cookie capability.** The adapter guide said
|
|
100
|
+
`submit_batch_media` has no `cookies_from_browser` parameter. It does: the tool
|
|
101
|
+
accepts one shared value and forwards it to every request, and the production
|
|
102
|
+
refusal is enforced by the same shared guard as the other surfaces. The matrix
|
|
103
|
+
and paragraph (and the historical DOC-003 changelog text) were corrected, with a
|
|
104
|
+
schema-consistency regression test that reads the real SDK-generated tool schema.
|
|
105
|
+
- **AL-004 — roadmap evidence.** The POSIX file-mode and fonts-reuse follow-ups
|
|
106
|
+
are marked done against their exact receipts: current-main CI run 36961982098
|
|
107
|
+
(12 OS/Python test jobs + three installed-wheel jobs + Ruff) verifies POSIX modes
|
|
108
|
+
on Linux/macOS, and release-workflow run 36961162844 built the reused 0.1.6
|
|
109
|
+
fonts, skipped the fonts upload, and published core to PyPI. The GitHub-release
|
|
110
|
+
job skip, its manual recovery, and the PR #23 condition repair are recorded
|
|
111
|
+
without claiming the original workflow was all green or the repaired automatic
|
|
112
|
+
path has been live-tested. The v0.1.7 history and version are preserved.
|
|
113
|
+
|
|
114
|
+
## v0.1.7 — 2026-10-01
|
|
115
|
+
|
|
116
|
+
The 2026-10-01 audit repair release. Core version: 0.1.7; unchanged optional fonts: 0.1.6. Release artifacts and publication status are recorded on [GitHub Releases](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.7). Local source-candidate verification does not establish installed-package or individual harness compatibility.
|
|
117
|
+
|
|
118
|
+
### Engineering fixes
|
|
119
|
+
|
|
120
|
+
- **Confined decoder boundary.** With `TEXTFLOWKIT_INPUT_ROOT` set, a confined
|
|
121
|
+
local input is staged and its decode is restricted to FFmpeg's self-contained
|
|
122
|
+
demuxers (`-format_whitelist`), and the `ffprobe` duration probe carries the
|
|
123
|
+
same whitelist. A playlist/manifest that would make the decoder open referenced
|
|
124
|
+
files is refused. The restriction covers a resume that re-decodes; a stage with
|
|
125
|
+
durable output is reused, not re-decoded. Owner runs without an input root stay
|
|
126
|
+
unrestricted.
|
|
127
|
+
- **Atomic attempt ownership.** A worker pins the attempt generation it claimed;
|
|
128
|
+
every progress write, checkpoint write, and terminal finalize is guarded on it,
|
|
129
|
+
so a stale run cannot overwrite a newer attempt's label or verdict. The reopen
|
|
130
|
+
of a terminal row for resume is an atomic claim pinned to the observed attempt —
|
|
131
|
+
two callers racing one job id cannot both reopen it.
|
|
132
|
+
- **Atomic worker startup and cancellation.** Worker-thread startup is atomic, and
|
|
133
|
+
the completion/cancellation race is decided in one place against the owned
|
|
134
|
+
attempt: the row becomes CANCELLED only if that generation carries an accepted
|
|
135
|
+
cancellation, DONE otherwise, so a finished DONE row can never still carry an
|
|
136
|
+
accepted cancel.
|
|
137
|
+
- **DOCX identity.** Completed-output reuse for DOCX compares the archive bytes
|
|
138
|
+
with only the local and central ZIP time/date pairs blanked; every other byte
|
|
139
|
+
(member names and order, compression, flags, extra fields, comments, compressed
|
|
140
|
+
content) must be equal, and the comparison never inflates a member.
|
|
141
|
+
- **Completed-stage reuse.** A run leaves a durable per-stage marker for each
|
|
142
|
+
expensive stage it finishes; resume reuses only stages finished for the
|
|
143
|
+
**requested configuration**, and a legacy coarse `postprocess` marker is honoured
|
|
144
|
+
only for the optional stages that record actually requested. The rule keys on
|
|
145
|
+
the marker, never on transcript metadata.
|
|
146
|
+
- **Aggregate export preflight.** The whole requested format set is validated
|
|
147
|
+
(unsupported/duplicate names, then every dependency) and the aggregate rendered
|
|
148
|
+
bytes are bounded **before any destination file is written**, for every surface
|
|
149
|
+
that publishes output.
|
|
150
|
+
- **Translation completeness.** The translator's result length is checked against
|
|
151
|
+
its input and every result is validated **before mutating any segment**: a
|
|
152
|
+
blank result for a non-blank segment raises and leaves the transcript exactly as
|
|
153
|
+
it was, rather than half-writing a set.
|
|
154
|
+
- **Per-item batch validation.** Each batch entry is constructed and admitted
|
|
155
|
+
independently (HTTP and MCP): a non-object entry, a non-string/empty source, an
|
|
156
|
+
unsupported format, or an unusable engine is **that** entry's error, carrying its
|
|
157
|
+
original zero-based `index` and best-effort `source`, while the other entries
|
|
158
|
+
still queue, in submitted order. There is no whole-batch engine preflight.
|
|
159
|
+
- **Terminal feedback.** A non-quiet CLI run streams live stage progress to
|
|
160
|
+
**stderr, flushed** (stdout stays clean for `--stdout`); quiet suppresses it.
|
|
161
|
+
Batch prints each item's result as it finishes. Job status exposes the recorded
|
|
162
|
+
stage.
|
|
163
|
+
|
|
164
|
+
### Documentation corrections (DOC-001 – DOC-006)
|
|
165
|
+
|
|
166
|
+
- **DOC-001** — the user manual now states that canonical saved/exported JSON
|
|
167
|
+
retains hidden segments and is an archive, not a redaction mechanism, and adds
|
|
168
|
+
a worked safe-publication-copy example that filters the copy without mutating
|
|
169
|
+
the stored transcript.
|
|
170
|
+
- **DOC-002** — the adapter guide and manual now distinguish response shapes:
|
|
171
|
+
HTTP JSON returns a `transcript` object with page counts but no `next`, MCP
|
|
172
|
+
returns a JSON string under `content` and adds a `next` instruction, and HTTP
|
|
173
|
+
text reads carry no paging metadata. Time selection is described as
|
|
174
|
+
overlapping rather than clipped.
|
|
175
|
+
- **DOC-003** — "same options" is replaced with a per-surface parameter matrix
|
|
176
|
+
(HTTP `formats` array vs MCP comma-separated string; HTTP batch options are
|
|
177
|
+
**per item**, MCP batch options are shared), and the batch contract now states
|
|
178
|
+
that 202 means admission, not completion, with per-item indexed errors. MCP
|
|
179
|
+
`sources` is documented as a list of **strings**: a non-string entry (including
|
|
180
|
+
a nested object or a number) is that entry's own error, not a whole-call
|
|
181
|
+
refusal, while a string remains valid.
|
|
182
|
+
*Correction (2026-10-01, audit-lite AL-003):* the v0.1.7 text of this entry
|
|
183
|
+
claimed the MCP batch tool exposes **no** `cookies_from_browser` parameter.
|
|
184
|
+
That was wrong. `submit_batch_media` accepts `cookies_from_browser` and
|
|
185
|
+
forwards the one shared value to every request it builds; the production
|
|
186
|
+
refusal is enforced by the same shared guard as the other surfaces. The
|
|
187
|
+
adapter guide was corrected to match the generated tool schema.
|
|
188
|
+
- **DOC-004** — a full production settings reference (names, defaults, units,
|
|
189
|
+
required fields) is added to the adapter guide, and the production refusal of
|
|
190
|
+
browser-cookie submissions is stated across the adapter guide, the sources
|
|
191
|
+
guide, and `SECURITY.md`, including resume.
|
|
192
|
+
- **DOC-005** — the install guide chooses the diarization install path (preserve
|
|
193
|
+
a working ROCm stack vs fresh CPU) **before** the first pip command, and the
|
|
194
|
+
manual's ROCm deep link now resolves to the actual heading.
|
|
195
|
+
- **DOC-006** — new [architecture and lifecycle guide](docs/architecture.md)
|
|
196
|
+
maps the core/CLI/MCP/HTTP doors, single-owning-process SQLite, startup
|
|
197
|
+
recovery via the HTTP/MCP lifespans and the lazy executor, checkpoint reuse,
|
|
198
|
+
and output publication; the adapter guide's "reaped at startup" wording is
|
|
199
|
+
qualified to match. Linked from the README and `CONTRIBUTING.md`.
|
|
200
|
+
|
|
201
|
+
The production settings reference lives in
|
|
202
|
+
[docs/adapters.md](docs/adapters.md#production-settings-reference).
|
|
203
|
+
|
|
204
|
+
### Deferred audit follow-ups — later fixes
|
|
205
|
+
|
|
206
|
+
On 2026-10-01, the owner explicitly deferred the following findings from the
|
|
207
|
+
full five-role audit. They remain open; they are not completed fixes, new release
|
|
208
|
+
claims, or additions to the current core-bug repair goal. No release date is set.
|
|
209
|
+
|
|
210
|
+
See [Later fixes in the roadmap](docs/roadmap.md#later-fixes--2026-10-01-audit)
|
|
211
|
+
for the complete list and completion criteria. The current repair goal still
|
|
212
|
+
covers decoder confinement, job lifecycle, publication/resume, translation and
|
|
213
|
+
batch correctness, major terminal guidance/documentation, and its final gate.
|
|
214
|
+
|
|
215
|
+
This changelog starts with the v0.1.8 release notes, followed by the v0.1.7
|
|
216
|
+
release notes; published historical
|
|
217
|
+
release notes remain on [GitHub Releases](https://github.com/scottconverse/textflowkit/releases).
|
|
@@ -77,11 +77,13 @@ RUN set -eux; \
|
|
|
77
77
|
WORKDIR /app
|
|
78
78
|
COPY pyproject.toml README.md LICENSE ./
|
|
79
79
|
COPY src ./src
|
|
80
|
-
# The base dependencies are yt-dlp
|
|
81
|
-
#
|
|
82
|
-
#
|
|
80
|
+
# The base dependencies are yt-dlp alone: the default engine (Whistle) is a
|
|
81
|
+
# native CPU binary fetched on first use, so this image does not pull a torch
|
|
82
|
+
# build. Selecting the openai-whisper engine needs the `whisper` extra named
|
|
83
|
+
# here; the image installs it because the container is meant to serve the
|
|
84
|
+
# Whisper stack too, and openai-whisper is what pulls the torch build.
|
|
83
85
|
RUN python -m pip install --no-cache-dir --upgrade pip \
|
|
84
|
-
&& python -m pip install --no-cache-dir ".[http]"
|
|
86
|
+
&& python -m pip install --no-cache-dir ".[http,whisper]"
|
|
85
87
|
|
|
86
88
|
# Non-secret defaults only. The roots are the directories created above; Compose
|
|
87
89
|
# repeats them so the volumes it mounts land on the paths the service reads.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: textflowkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Cross-platform media transcription toolkit: URL or file in, timestamped transcripts and subtitle files out.
|
|
5
5
|
Project-URL: Homepage, https://www.textflowkit.org/
|
|
6
6
|
Project-URL: Issues, https://github.com/scottconverse/textflowkit/issues
|
|
@@ -21,11 +21,11 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
21
21
|
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
22
22
|
Classifier: Topic :: Text Processing
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
|
-
Requires-Dist: openai-whisper>=20240930
|
|
25
24
|
Requires-Dist: yt-dlp>=2025.1.1
|
|
26
25
|
Provides-Extra: all
|
|
27
26
|
Requires-Dist: fastapi>=0.115; extra == 'all'
|
|
28
27
|
Requires-Dist: mcp>=2.0; extra == 'all'
|
|
28
|
+
Requires-Dist: openai-whisper>=20240930; extra == 'all'
|
|
29
29
|
Requires-Dist: pyannote-audio>=4.0; extra == 'all'
|
|
30
30
|
Requires-Dist: pydantic>=2.7; extra == 'all'
|
|
31
31
|
Requires-Dist: python-docx>=1.1; extra == 'all'
|
|
@@ -53,6 +53,8 @@ Requires-Dist: pydantic>=2.7; extra == 'http'
|
|
|
53
53
|
Requires-Dist: uvicorn>=0.30; extra == 'http'
|
|
54
54
|
Provides-Extra: mcp
|
|
55
55
|
Requires-Dist: mcp>=2.0; extra == 'mcp'
|
|
56
|
+
Provides-Extra: whisper
|
|
57
|
+
Requires-Dist: openai-whisper>=20240930; extra == 'whisper'
|
|
56
58
|
Description-Content-Type: text/markdown
|
|
57
59
|
|
|
58
60
|
# textflowkit
|
|
@@ -62,9 +64,10 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
|
|
|
62
64
|
[Project landing page](https://www.textflowkit.org/) ·
|
|
63
65
|
[PyPI package](https://pypi.org/project/textflowkit/) ·
|
|
64
66
|
[GitHub releases](https://github.com/scottconverse/textflowkit/releases) ·
|
|
65
|
-
[User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
|
|
67
|
+
[User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md) ·
|
|
68
|
+
[Developer and integration manual](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md)
|
|
66
69
|
|
|
67
|
-
**Current release: [v0.1.
|
|
70
|
+
**Current release: [v0.1.9](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.9).**
|
|
68
71
|
|
|
69
72
|
The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
|
|
70
73
|
Pages. GitHub remains the source and CI host; the website does not run the
|
|
@@ -111,7 +114,7 @@ URL or file ─► detect platform ─► acquire media ─► ffmpeg
|
|
|
111
114
|
│
|
|
112
115
|
┌───────────────────────────┘
|
|
113
116
|
▼
|
|
114
|
-
|
|
117
|
+
speech-to-text (Whistle default; Whisper on request)
|
|
115
118
|
│
|
|
116
119
|
▼
|
|
117
120
|
canonical transcript (JSON)
|
|
@@ -125,6 +128,8 @@ DOCX and PDF are available through the optional `export` extra.
|
|
|
125
128
|
|
|
126
129
|
## Architecture
|
|
127
130
|
|
|
131
|
+

|
|
132
|
+
|
|
128
133
|
The design principle is **one engine, three doors**. Everything of substance lives in
|
|
129
134
|
the core; the interfaces are thin.
|
|
130
135
|
|
|
@@ -164,6 +169,22 @@ python -m pip install textflowkit
|
|
|
164
169
|
textflowkit doctor
|
|
165
170
|
```
|
|
166
171
|
|
|
172
|
+
The default engine is **Whistle**: a native CPU-only transcription CLI that
|
|
173
|
+
needs no PyTorch. It downloads one pinned model (about 17 MB) and a small pinned
|
|
174
|
+
binary on first use; a fresh default install therefore stays small and pulls no
|
|
175
|
+
torch. To use the `openai-whisper` engine instead — the ROCm/CUDA/CPU torch
|
|
176
|
+
stack — install the `whisper` extra and name it explicitly:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
python -m pip install 'textflowkit[whisper]'
|
|
180
|
+
textflowkit transcribe meeting.mp4 --engine whisper --model small
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
See [Whistle (default engine)](#whistle-default-engine) for platform and
|
|
184
|
+
language coverage, and the [install guide](https://github.com/scottconverse/textflowkit/blob/main/docs/install.md)
|
|
185
|
+
for the AMD ROCm path, which applies to the explicit `whisper` engine and
|
|
186
|
+
diarization.
|
|
187
|
+
|
|
167
188
|
For MCP or the JSON HTTP adapter, install the matching extra:
|
|
168
189
|
|
|
169
190
|
```bash
|
|
@@ -187,7 +208,6 @@ from textflowkit import transcribe
|
|
|
187
208
|
|
|
188
209
|
result = transcribe(
|
|
189
210
|
"meeting.mp4", # also accepts supported URLs
|
|
190
|
-
model="small",
|
|
191
211
|
formats=["json", "srt", "txt"],
|
|
192
212
|
output_dir="transcripts", # omit to return the transcript without writing files
|
|
193
213
|
)
|
|
@@ -200,6 +220,10 @@ for segment in result.transcript.segments:
|
|
|
200
220
|
print(" ", word.start, word.end, word.text)
|
|
201
221
|
```
|
|
202
222
|
|
|
223
|
+
`engine` defaults to the product default (Whistle) and `model=None` resolves to
|
|
224
|
+
that engine's own model. To use the Whisper-family engines, name one and its
|
|
225
|
+
model explicitly: `transcribe("meeting.mp4", engine="whisper", model="small")`.
|
|
226
|
+
|
|
203
227
|
`transcribe()` returns `TranscribeResult` with a canonical `Transcript` and
|
|
204
228
|
written output paths. `Transcript.to_dict()` / `.to_json()` preserve segment and
|
|
205
229
|
word timing; older transcript JSON without `words` remains readable. Pass
|
|
@@ -264,6 +288,60 @@ does **not** assert that the remote bytes are still identical.
|
|
|
264
288
|
textflowkit export ./transcript.json --format vtt
|
|
265
289
|
```
|
|
266
290
|
|
|
291
|
+
## Whistle (default engine)
|
|
292
|
+
|
|
293
|
+
**Whistle is the default engine** as of v0.1.9. It is a native CPU-only CLI that
|
|
294
|
+
needs no PyTorch, so a fresh `pip install textflowkit` stays small and pulls no
|
|
295
|
+
torch; `openai-whisper` is an explicitly selectable opt-in engine.
|
|
296
|
+
|
|
297
|
+

|
|
298
|
+
|
|
299
|
+
- **CPU only, no torch.** It runs a pinned native binary and downloads one pinned
|
|
300
|
+
model on first use. Naming a GPU device is refused rather than silently
|
|
301
|
+
ignored: a GPU request names the explicit `whisper` engine as the alternative,
|
|
302
|
+
and the engine is never switched for you.
|
|
303
|
+
- **Seven advertised languages:** `en`, `de`, `fr`, `es`, `it`, `nl`, `pl`. A
|
|
304
|
+
request for another language is refused at request construction, before any
|
|
305
|
+
media is fetched.
|
|
306
|
+
- **Native platforms:** Windows x86-64 and arm64, Linux x86-64 and arm64, and
|
|
307
|
+
Apple Silicon. **Intel Macs are not supported**; a request there is refused
|
|
308
|
+
with a message that names the explicit `whisper` engine instead. No WSL layer
|
|
309
|
+
is involved.
|
|
310
|
+
- **How it works:** long audio is split into 26-second cores with up to 2 seconds
|
|
311
|
+
of context on each side, so every standalone clip is ≤ 30 seconds (the native
|
|
312
|
+
CLI's limit). Overlap words are selected by core midpoint rather than text
|
|
313
|
+
deduplication, so repeated spoken phrases are preserved.
|
|
314
|
+
- **Streaming is not used.** The native `--audio-stream` mode failed its
|
|
315
|
+
coverage and is never passed; only the committed standalone output is parsed.
|
|
316
|
+
- **Durable block resume.** With `TEXTFLOWKIT_DB` set, a long run writes partial
|
|
317
|
+
per-block checkpoints; a resume checks the source and decoded-WAV hashes and the
|
|
318
|
+
config, then re-runs only the blocks that were not finished. The durable store
|
|
319
|
+
still assumes one owning process.
|
|
320
|
+
- **Cancellation.** A running Whistle run terminates its exact owned child
|
|
321
|
+
process bounded by a per-clip timeout. A single long `openai-whisper` model call
|
|
322
|
+
is still only cancellable at its next stage boundary, as documented for the
|
|
323
|
+
Whisper engine.
|
|
324
|
+
- **Telemetry is off, unconditionally.** Every Whistle child process forces
|
|
325
|
+
`NEEDLE_TELEMETRY=0`, `DO_NOT_TRACK=1`, and `CI=1`, overriding a parent that
|
|
326
|
+
opted in. The product ships no analytics, usage SDK, anonymous ids, or events,
|
|
327
|
+
and there is no opt-in setting. This applies the upstream documented gate; it
|
|
328
|
+
is **not** a claim that the upstream binary's tracking code is physically
|
|
329
|
+
removed.
|
|
330
|
+
- **Offline and downloads.** Set `TEXTFLOWKIT_OFFLINE` to refuse any download, and
|
|
331
|
+
`TEXTFLOWKIT_MODELS_DIR` to choose the asset directory. The Whistle asset helper
|
|
332
|
+
downloads only pinned runtime/model assets; media acquisition and optional
|
|
333
|
+
translation may independently use the network. These downloads are not
|
|
334
|
+
telemetry.
|
|
335
|
+
|
|
336
|
+
**Verification boundary.** A local run transcribed a 4-hour (14,407 s)
|
|
337
|
+
recording on CPU into 555 clips / 34,596 words / 2,875 segments in 731 s, with
|
|
338
|
+
telemetry forced off and offline mode on; a separate run proved durable block
|
|
339
|
+
resume across two processes. These are **first local test numbers**, not a
|
|
340
|
+
promise of universal performance or of accuracy equal to Whisper — no
|
|
341
|
+
human-scored word-error rate is claimed. See the
|
|
342
|
+
[user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
|
|
343
|
+
and [install guide](https://github.com/scottconverse/textflowkit/blob/main/docs/install.md).
|
|
344
|
+
|
|
267
345
|
## Use as an MCP server
|
|
268
346
|
|
|
269
347
|
```bash
|
|
@@ -323,7 +401,7 @@ scope here, so no image is claimed to build or start. See
|
|
|
323
401
|
|
|
324
402
|
```bash
|
|
325
403
|
TEXTFLOWKIT_DB=./jobs.db # job state survives restart (SQLite)
|
|
326
|
-
TEXTFLOWKIT_MAX_CONCURRENCY=1 # default;
|
|
404
|
+
TEXTFLOWKIT_MAX_CONCURRENCY=1 # default; bounded job worker pool
|
|
327
405
|
```
|
|
328
406
|
|
|
329
407
|
`cancel_job` stops a queued job immediately, or a running job at its next stage
|
|
@@ -338,14 +416,31 @@ web frontend share the job contract without blocking a request.
|
|
|
338
416
|
|
|
339
417
|
## Status
|
|
340
418
|
|
|
341
|
-
**v0.1.
|
|
342
|
-
coverage. This release
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
419
|
+
**v0.1.9 release.** Core, CLI, MCP, and HTTP have automated
|
|
420
|
+
coverage. This release makes **Whistle the default engine** — a native CPU-only
|
|
421
|
+
transcriber that needs no PyTorch — and moves `openai-whisper` to the optional
|
|
422
|
+
`whisper` extra, selected explicitly with `--engine whisper` or
|
|
423
|
+
`engine="whisper"`. Legacy engine aliases are preserved, so older saved jobs and
|
|
424
|
+
`transcribe()` calls still decode. Whistle refuses an unsupported platform,
|
|
425
|
+
language, or GPU request instead of silently falling back. Its engine's
|
|
426
|
+
verification boundary is in [Whistle (default engine)](#whistle-default-engine)
|
|
427
|
+
above. Release publication uses the tag workflow, which requires successful
|
|
428
|
+
exact-commit main CI before PyPI uploads and creates the public GitHub release
|
|
429
|
+
only afterward. Check the linked release for artifacts and workflow status; local
|
|
430
|
+
source verification is not a fresh PyPI-install or individual-harness receipt.
|
|
431
|
+
|
|
432
|
+
The v0.1.8 release carried four fixes for the 2026-10-01 post-release audit-lite
|
|
433
|
+
(findings AL-001 – AL-004): CLI owning-process startup recovery so an interrupted
|
|
434
|
+
run's saved work can be resumed instead of being refused as "already active",
|
|
435
|
+
preservation of the completed transcript across resume setup failures, a
|
|
436
|
+
correction to the MCP batch cookie capability, and corrected roadmap evidence
|
|
437
|
+
receipts. It built on the v0.1.7 audit repair set and the v0.1.6 review repairs
|
|
438
|
+
that precede it. The v0.1.7 source candidate was verified with real CLI, HTTP and
|
|
439
|
+
MCP speech, seven-format exports and completed resume; the v0.1.8 runtime fixes
|
|
440
|
+
were independently checked through fresh-process CLI restart probes, real SQLite
|
|
441
|
+
setup-failure tests, and the full Windows test suite. Those checks are not new
|
|
442
|
+
individual-harness receipts. That is the previous release's record, not evidence
|
|
443
|
+
for v0.1.9.
|
|
349
444
|
|
|
350
445
|
The v0.1.6 release was the post-v0.1.5 review repair set: security hardening
|
|
351
446
|
for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
|
|
@@ -362,9 +457,9 @@ The v0.1.6 release is public: the
|
|
|
362
457
|
[v0.1.6 GitHub release](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6)
|
|
363
458
|
and both [core](https://pypi.org/project/textflowkit/0.1.6/) and
|
|
364
459
|
[fonts](https://pypi.org/project/textflowkit-fonts/0.1.6/) PyPI projects are
|
|
365
|
-
live — that is the
|
|
460
|
+
live — that is the v0.1.6 release's evidence, not a receipt for a later release.
|
|
366
461
|
Everything from here to the end of this section is the v0.1.6 release's
|
|
367
|
-
historical record as published: it is not re-verified for v0.1.
|
|
462
|
+
historical record as published: it is not re-verified for v0.1.9, whose separate release evidence is not supplied by these historical paragraphs.
|
|
368
463
|
Merged-main CI passed 16/16 on the tagged commit; the published wheel and
|
|
369
464
|
sdist digests match the GitHub release assets and their SHA-256 list; and a fresh
|
|
370
465
|
Windows Python 3.12 install of `textflowkit[export,mcp,http]==0.1.6` from PyPI
|