utterplan 0.3.3__tar.gz → 0.3.4__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.
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/events/events.jsonl +4 -0
- utterplan-0.3.4/.ledger/releaseledger/data/ledgers/main/releases/0.3.4/audit/commit-audit.yaml +13 -0
- utterplan-0.3.4/.ledger/releaseledger/data/ledgers/main/releases/0.3.4/entries/entry-0001.md +34 -0
- utterplan-0.3.4/.ledger/releaseledger/data/ledgers/main/releases/0.3.4/release.md +27 -0
- utterplan-0.3.4/.ledger/releaseledger/work/0.3.4/audit-decisions.yaml +1 -0
- utterplan-0.3.4/.ledger/releaseledger/work/0.3.4/audit.yaml +13 -0
- utterplan-0.3.4/.ledger/releaseledger/work/0.3.4/entries.preview.yaml +11 -0
- utterplan-0.3.4/.ledger/releaseledger/work/0.3.4/entries.yaml +11 -0
- utterplan-0.3.4/.ledger/releaseledger/work/0.3.4/evidence/manifest.json +12 -0
- utterplan-0.3.4/.ledger/releaseledger/work/0.3.4/range.json +13 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/PKG-INFO +17 -1
- {utterplan-0.3.3 → utterplan-0.3.4}/README.md +16 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/changelog.md +8 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/python-api.md +27 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_linguistics.py +69 -1
- utterplan-0.3.4/tests/test_progress.py +231 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/__init__.py +5 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/_version.py +3 -3
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/compiler.py +4 -2
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/linguistics.py +105 -7
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/planner.py +184 -8
- utterplan-0.3.4/utterplan/progress.py +60 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan.egg-info/PKG-INFO +17 -1
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan.egg-info/SOURCES.txt +11 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan.egg-info/scm_file_list.json +11 -0
- utterplan-0.3.4/utterplan.egg-info/scm_version.json +8 -0
- utterplan-0.3.3/utterplan.egg-info/scm_version.json +0 -8
- {utterplan-0.3.3 → utterplan-0.3.4}/.codecrate.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.github/workflows/codecov.yml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.github/workflows/pre-commit.yml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.github/workflows/python-publish.yml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.github/workflows/tests.yml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.gitignore +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/ledger.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/.ledger-project.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/config.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/.ledger-project.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.0/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.0/entries/entry-0002.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.0/entries/entry-0003.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.0/entries/entry-0004.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.0/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.1/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.1/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/audit/commit-audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/entries/entry-0002.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/entries/entry-0003.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/entries/entry-0004.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/entries/entry-0005.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/entries/entry-0006.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.1.3/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.2.0/audit/commit-audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.2.0/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.2.0/entries/entry-0002.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.2.0/entries/entry-0003.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.2.0/entries/entry-0004.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.2.0/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/audit/commit-audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/entries/entry-0002.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/entries/entry-0003.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/entries/entry-0004.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/entries/entry-0005.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/entries/entry-0006.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.0/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.1/audit/commit-audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.1/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.1/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.2/audit/commit-audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.2/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.2/entries/entry-0002.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.2/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.3/audit/commit-audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.3/entries/entry-0001.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.3/entries/entry-0002.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/releases/0.3.3/release.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/audit-decisions.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/entries.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/evidence/2c64e29.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/evidence/4022753.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/evidence/66a88b6.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/evidence/manifest.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.1.2/range.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/audit-decisions.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/entries.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/evidence/9637182.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/evidence/c52aa68.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/evidence/e49a6dd.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/evidence/manifest.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.2.0/range.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.0/audit-decisions.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.0/audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.0/entries.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.0/evidence/49023f0.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.0/evidence/manifest.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.0/range.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.1/audit-decisions.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.1/audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.1/entries.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.1/evidence/70adac0.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.1/evidence/manifest.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.1/range.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/audit-decisions.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/entries.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/evidence/6c2a1ac.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/evidence/a2bc20d.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/evidence/ba3b5af.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/evidence/manifest.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.2/range.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.3/audit-decisions.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.3/audit.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.3/entries.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.3/evidence/66e2d83.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.3/evidence/ac797e4.patch +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.3/evidence/manifest.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/work/0.3.3/range.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/write.lock +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/taskledger/.ledger-project.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.ledger/taskledger/config.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/.pre-commit-config.yaml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/AGENTS.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/LICENSE +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/NOTICE +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/Makefile +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/architecture.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/cli.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/conf.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/consumer-guide.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/coordinate-spaces.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/debugging.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/format.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/getting-started.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/index.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/make.bat +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/phase-a-completion.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/pykokoro-integration.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/docs/requirements.txt +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/examples/basic.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/examples/chapter.ssmd +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/examples/compile_file.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/examples/inspect_plan.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/examples/multilingual.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/examples/ssmd_example.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/pyproject.toml +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/setup.cfg +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/spec/schemas/v1.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/spec/schemas/v2.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/spec/schemas/v3.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/spec/utterplan.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/__init__.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/fixtures/canonical_consumer_contract.expected.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/fixtures/canonical_consumer_contract.ssmd +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/fixtures/ssmd_09_comprehensive.ssmd +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/basic_en.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/directives.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/markers.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/multilingual.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/parenthetical.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/spokenform_offsets.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/ssmd_09_comprehensive.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/golden/ssmd_breaks.utterplan.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/__init__.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/cases.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/goldens/multilingual_preparation.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/goldens/spokenform_numbers.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/goldens/ssmd_break.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/goldens/ssmd_language_detection.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/goldens/ssmd_marker.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/goldens/ssmd_pronunciation.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/migration/test_semantic_migration_regressions.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/README.md +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v1/basic.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v1/directives.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v1/markers.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v1/multilingual.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v1/parenthetical.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v1/spokenform_offsets.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v1/ssmd_breaks.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/schema_history/v2/directives.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_boundaries.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_cli.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_compiler.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_consumer_contract.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_consumer_readiness.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_coordinates.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_directives.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_explain.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_fake_renderer.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_format.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_golden.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_identity_units.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_import_boundaries.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_language.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_migration_framework.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_pauses.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_planner.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_preparation.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_public_identity.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_schema_history.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_schema_registry.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_schema_strict.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_ssmd_09_parser.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/tests/test_version.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/__main__.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/cli.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/config.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/diagnostics.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/directives.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/exceptions.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/explain.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/format.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/hashing.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/language.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/migration.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/migrations/__init__.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/migrations/registry.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/migrations/v1_to_v2.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/migrations/v2_to_v3.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/model.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/parsers.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/pauses.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/preparation.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/py.typed +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/schema_registry.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/schemas/v1.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/schemas/v2.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/schemas/v3.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/serialization.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/spacy_models.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/spans.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/__init__.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/document/__init__.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/document/plain.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/document/ssmd.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/preparation/__init__.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/preparation/identity.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/preparation/spokenform.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/stages/protocols.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/units.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/utterplan.schema.json +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan/versioning.py +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan.egg-info/dependency_links.txt +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan.egg-info/entry_points.txt +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan.egg-info/requires.txt +0 -0
- {utterplan-0.3.3 → utterplan-0.3.4}/utterplan.egg-info/top_level.txt +0 -0
{utterplan-0.3.3 → utterplan-0.3.4}/.ledger/releaseledger/data/ledgers/main/events/events.jsonl
RENAMED
|
@@ -60,3 +60,7 @@
|
|
|
60
60
|
{"data":{"updated_rows":2},"event":"audit.updated","event_id":"event-0060","record_revisions":{"commit_audit_sheet":2},"release_version":"0.3.3","schema_version":2}
|
|
61
61
|
{"data":{"entry_ids":["entry-0001","entry-0002"],"synced_audit_rows":2},"event":"entry.batch_added","event_id":"event-0061","record_revisions":{"commit_audit_sheet":3,"entry:0.3.3/entry-0001":1,"entry:0.3.3/entry-0002":1,"release:0.3.3":3},"release_version":"0.3.3","schema_version":2}
|
|
62
62
|
{"data":{"released_at":"2026-10-03"},"event":"release.finalized","event_id":"event-0062","record_revisions":{"release:0.3.3":4},"release_version":"0.3.3","schema_version":2}
|
|
63
|
+
{"data":{"status":"planned"},"event":"release.created","event_id":"event-0063","record_revisions":{"release:0.3.4":1},"release_version":"0.3.4","schema_version":2}
|
|
64
|
+
{"data":{"fields":["git_base_ref","git_base_sha","git_commit_count","git_head_ref","git_head_sha","git_range"]},"event":"release.updated","event_id":"event-0064","record_revisions":{"release:0.3.4":2},"release_version":"0.3.4","schema_version":2}
|
|
65
|
+
{"data":{"row_count":0},"event":"audit.created","event_id":"event-0065","record_revisions":{"commit_audit_sheet":1},"release_version":"0.3.4","schema_version":2}
|
|
66
|
+
{"data":{"kind":"added","status":"accepted"},"entry_id":"entry-0001","event":"entry.added","event_id":"event-0066","record_revisions":{"entry:0.3.4/entry-0001":1,"release:0.3.4":3},"release_version":"0.3.4","schema_version":2}
|
utterplan-0.3.4/.ledger/releaseledger/data/ledgers/main/releases/0.3.4/audit/commit-audit.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
object_type: commit_audit_sheet
|
|
3
|
+
versioning:
|
|
4
|
+
schema_version: 1
|
|
5
|
+
revision: 1
|
|
6
|
+
release_version: 0.3.4
|
|
7
|
+
git_base_ref: v0.3.3
|
|
8
|
+
git_base_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
9
|
+
git_head_ref: HEAD
|
|
10
|
+
git_head_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
11
|
+
git_range: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158..0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
12
|
+
commit_count: 0
|
|
13
|
+
rows: []
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 2
|
|
3
|
+
object_type: release_entry
|
|
4
|
+
versioning:
|
|
5
|
+
schema_version: 1
|
|
6
|
+
revision: 1
|
|
7
|
+
entry_id: entry-0001
|
|
8
|
+
release_version: 0.3.4
|
|
9
|
+
kind: added
|
|
10
|
+
summary: Added typed planner progress events and reused unchanged linguistic analyses
|
|
11
|
+
status: accepted
|
|
12
|
+
audience: null
|
|
13
|
+
scopes: []
|
|
14
|
+
source_refs:
|
|
15
|
+
- tl:task-0021
|
|
16
|
+
paths:
|
|
17
|
+
- utterplan/progress.py
|
|
18
|
+
- utterplan/planner.py
|
|
19
|
+
- utterplan/linguistics.py
|
|
20
|
+
- utterplan/compiler.py
|
|
21
|
+
- tests/test_progress.py
|
|
22
|
+
- tests/test_linguistics.py
|
|
23
|
+
- docs/python-api.md
|
|
24
|
+
- README.md
|
|
25
|
+
issues: []
|
|
26
|
+
prs: []
|
|
27
|
+
sources: []
|
|
28
|
+
contributors: []
|
|
29
|
+
breaking: false
|
|
30
|
+
internal: false
|
|
31
|
+
order: 1
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
The optional synchronous callback reports planner phases, provider runs, and actual spaCy model loads without changing plan serialization; equivalent analyses are reused where safe.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 2
|
|
3
|
+
object_type: release
|
|
4
|
+
versioning:
|
|
5
|
+
schema_version: 1
|
|
6
|
+
revision: 3
|
|
7
|
+
version: 0.3.4
|
|
8
|
+
status: planned
|
|
9
|
+
history_state: curated
|
|
10
|
+
title: null
|
|
11
|
+
released_at: null
|
|
12
|
+
previous_version: 0.3.3
|
|
13
|
+
cancel_reason: null
|
|
14
|
+
superseded_by: null
|
|
15
|
+
changelog_file: null
|
|
16
|
+
boundary_ref: null
|
|
17
|
+
source_refs: []
|
|
18
|
+
source_count: null
|
|
19
|
+
entry_count: 1
|
|
20
|
+
artifact_count: 0
|
|
21
|
+
git_base_ref: v0.3.3
|
|
22
|
+
git_base_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
23
|
+
git_head_ref: HEAD
|
|
24
|
+
git_head_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
25
|
+
git_range: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158..0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
26
|
+
git_commit_count: 0
|
|
27
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rows: []
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
schema_version: 1
|
|
2
|
+
object_type: commit_audit_sheet
|
|
3
|
+
versioning:
|
|
4
|
+
schema_version: 1
|
|
5
|
+
revision: 1
|
|
6
|
+
release_version: 0.3.4
|
|
7
|
+
git_base_ref: v0.3.3
|
|
8
|
+
git_base_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
9
|
+
git_head_ref: HEAD
|
|
10
|
+
git_head_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
11
|
+
git_range: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158..0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
12
|
+
commit_count: 0
|
|
13
|
+
rows: []
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
schema: releaseledger.entry-batch.v1
|
|
2
|
+
operation: create
|
|
3
|
+
schema_version: 1
|
|
4
|
+
object_type: release_entry_batch
|
|
5
|
+
release_version: 0.3.4
|
|
6
|
+
git_base_ref: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
7
|
+
git_base_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
8
|
+
git_head_ref: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
9
|
+
git_head_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
10
|
+
include_merges: nontrivial
|
|
11
|
+
entries: []
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
schema: releaseledger.entry-batch.v1
|
|
2
|
+
operation: create
|
|
3
|
+
schema_version: 1
|
|
4
|
+
object_type: release_entry_batch
|
|
5
|
+
release_version: 0.3.4
|
|
6
|
+
git_base_ref: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
7
|
+
git_base_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
8
|
+
git_head_ref: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
9
|
+
git_head_sha: 0e4e0751e2b8a5d5c0592a3efbda5a31f653c158
|
|
10
|
+
include_merges: nontrivial
|
|
11
|
+
entries: []
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"candidate_count": 0,
|
|
3
|
+
"candidates": [],
|
|
4
|
+
"git_base_ref": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
5
|
+
"git_base_sha": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
6
|
+
"git_head_ref": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
7
|
+
"git_head_sha": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
8
|
+
"include_merges": "nontrivial",
|
|
9
|
+
"object_type": "git_evidence_manifest",
|
|
10
|
+
"release_version": "0.3.4",
|
|
11
|
+
"schema_version": 1
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"base_ref": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
3
|
+
"base_sha": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
4
|
+
"candidate_count": 0,
|
|
5
|
+
"commit_count": 0,
|
|
6
|
+
"head_ref": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
7
|
+
"head_sha": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158",
|
|
8
|
+
"include_merges": "nontrivial",
|
|
9
|
+
"kind": "git_range_summary",
|
|
10
|
+
"merge_commit_count": 0,
|
|
11
|
+
"merge_commits_skipped": 0,
|
|
12
|
+
"range": "0e4e0751e2b8a5d5c0592a3efbda5a31f653c158..0e4e0751e2b8a5d5c0592a3efbda5a31f653c158"
|
|
13
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: utterplan
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Engine-independent text-to-speech planning compiler and interchange format
|
|
5
5
|
Author-email: Holger Nahrstaedt <nahrstaedt@gmail.com>
|
|
6
6
|
License: Apache License
|
|
@@ -343,6 +343,22 @@ identity. Plain text must be explicitly selected in Python and always requires a
|
|
|
343
343
|
language fallback. Existing `UtterancePlanner.plan` remains supported and returns
|
|
344
344
|
only the plan; JSON is the portable persistence and interchange format.
|
|
345
345
|
|
|
346
|
+
For operational progress from a long-running Python plan, pass `on_progress` to
|
|
347
|
+
`UtterancePlanner.plan`, `UtterancePlanner.compile`, or `compile_document`. The
|
|
348
|
+
callback receives typed `PlannerProgressEvent` objects and does not change the plan:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from utterplan import PlannerConfig, UtterancePlanner
|
|
352
|
+
|
|
353
|
+
events = []
|
|
354
|
+
plan = UtterancePlanner(PlannerConfig(language="en-us")).plan(
|
|
355
|
+
text,
|
|
356
|
+
on_progress=events.append,
|
|
357
|
+
)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Callbacks run synchronously and exceptions propagate. See the [Python API guide](docs/python-api.md#planner-progress-callbacks) for event fields, model-load events, and reuse behavior.
|
|
361
|
+
|
|
346
362
|
## Renderer-consumer boundary
|
|
347
363
|
|
|
348
364
|
Renderers consume `PlanSegment.text`, which is prepared/spoken text, and use
|
|
@@ -101,6 +101,22 @@ identity. Plain text must be explicitly selected in Python and always requires a
|
|
|
101
101
|
language fallback. Existing `UtterancePlanner.plan` remains supported and returns
|
|
102
102
|
only the plan; JSON is the portable persistence and interchange format.
|
|
103
103
|
|
|
104
|
+
For operational progress from a long-running Python plan, pass `on_progress` to
|
|
105
|
+
`UtterancePlanner.plan`, `UtterancePlanner.compile`, or `compile_document`. The
|
|
106
|
+
callback receives typed `PlannerProgressEvent` objects and does not change the plan:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from utterplan import PlannerConfig, UtterancePlanner
|
|
110
|
+
|
|
111
|
+
events = []
|
|
112
|
+
plan = UtterancePlanner(PlannerConfig(language="en-us")).plan(
|
|
113
|
+
text,
|
|
114
|
+
on_progress=events.append,
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Callbacks run synchronously and exceptions propagate. See the [Python API guide](docs/python-api.md#planner-progress-callbacks) for event fields, model-load events, and reuse behavior.
|
|
119
|
+
|
|
104
120
|
## Renderer-consumer boundary
|
|
105
121
|
|
|
106
122
|
Renderers consume `PlanSegment.text`, which is prepared/spoken text, and use
|
|
@@ -41,6 +41,33 @@ the plan, is not renderer input, and does not change `plan.plan_id`. Its source
|
|
|
41
41
|
ranges index SSMD-clean structural text; transformation output ranges index prepared
|
|
42
42
|
spoken text, using Python string character offsets.
|
|
43
43
|
|
|
44
|
+
## Planner progress callbacks
|
|
45
|
+
|
|
46
|
+
`UtterancePlanner.plan`, `UtterancePlanner.compile`, and `compile_document` accept an optional keyword-only `on_progress` callback. It receives typed `PlannerProgressEvent` objects synchronously in the planner thread. Progress is operational only: it is not added to `PlannerConfig` or the plan, and enabling a callback does not change plan identity or serialization.
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from utterplan import PlannerConfig, PlannerProgressEvent, UtterancePlanner
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def report(event: PlannerProgressEvent) -> None:
|
|
53
|
+
if event.kind == "run.started":
|
|
54
|
+
print(
|
|
55
|
+
event.phase,
|
|
56
|
+
event.pass_index,
|
|
57
|
+
event.language,
|
|
58
|
+
event.provider,
|
|
59
|
+
event.model,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
planner = UtterancePlanner(PlannerConfig(language="en-us"))
|
|
64
|
+
plan = planner.plan(text, on_progress=report)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The phase events cover parsing, source analysis, preparation, spoken analysis, segmentation, and finalization. Each provider run reports `run.started` and `run.completed`; `completed` counts fully completed runs and `total` is the number of runs in that phase. When a local spaCy model is actually loaded, `model.started` and `model.completed` identify its language, provider, and resolved model. Cached models produce no load events. Events contain primitive metadata only, never spaCy pipelines or documents.
|
|
68
|
+
|
|
69
|
+
Callbacks should be lightweight. An exception raised by a callback propagates to the caller and can be used to stop planning. A provider invocation such as `pipeline(text)` is one opaque work unit: events report its start and completion but do not claim smooth percentages or internal progress. If spoken-text preparation leaves the linguistic input unchanged, the planner can reuse source analysis; the `spoken_analysis` phase events expose `details["reused"] = True` and no provider-run events are emitted for that reuse. Identity preparation also reports `source_analysis` as skipped (`details["skipped"] = True`) because it does not use source analyses; the single actual provider pass runs as logical pass 2.
|
|
70
|
+
|
|
44
71
|
## SSMD input and semantic plan
|
|
45
72
|
|
|
46
73
|
The SSMD parser accepts dialect 0.9 only. Select SSMD explicitly for unversioned canonical fragments with `PlannerConfig(document_format="ssmd")`. Older SSMD source must be migrated with `ssmd migrate FILE --to 0.9`; `migrate_plan_data` and `utterplan migrate` apply to serialized UtterPlan JSON, not source documents.
|
|
@@ -197,7 +197,7 @@ def test_clausal_boundaries_reuse_provider_document(monkeypatch):
|
|
|
197
197
|
|
|
198
198
|
plan = UtterancePlanner(config).plan(source)
|
|
199
199
|
|
|
200
|
-
assert len(docs) ==
|
|
200
|
+
assert len(docs) == 1
|
|
201
201
|
assert detected == [(source, docs[-1])]
|
|
202
202
|
boundary = next(item for item in plan.boundaries if item.kind == "clausal_comma")
|
|
203
203
|
assert boundary.position == source.index(",")
|
|
@@ -311,3 +311,71 @@ def test_token_semantics_change_unit_hash_but_provenance_does_not(monkeypatch):
|
|
|
311
311
|
|
|
312
312
|
metadata_only = replace(left, linguistic_runs=(changed_provenance,))
|
|
313
313
|
assert metadata_only.units[0].content_hash == left.units[0].content_hash
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _count_analysis_calls(monkeypatch: pytest.MonkeyPatch) -> list[str]:
|
|
317
|
+
calls: list[str] = []
|
|
318
|
+
original = LinguisticResourcePool.analyze
|
|
319
|
+
|
|
320
|
+
def counted(self, text, run, config, *, phase="source_analysis", on_progress=None):
|
|
321
|
+
calls.append(text)
|
|
322
|
+
return original(self, text, run, config, phase=phase, on_progress=on_progress)
|
|
323
|
+
|
|
324
|
+
monkeypatch.setattr(LinguisticResourcePool, "analyze", counted)
|
|
325
|
+
return calls
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_identity_preparation_analyzes_once_and_preserves_plan(
|
|
329
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
330
|
+
) -> None:
|
|
331
|
+
calls = _count_analysis_calls(monkeypatch)
|
|
332
|
+
config = PlannerConfig(language="en-us", document_format="plain", text_preparation="identity")
|
|
333
|
+
baseline = UtterancePlanner(config).plan("Hello world.")
|
|
334
|
+
assert len(calls) == 1
|
|
335
|
+
calls.clear()
|
|
336
|
+
|
|
337
|
+
events = []
|
|
338
|
+
observed = UtterancePlanner(config).plan("Hello world.", on_progress=events.append)
|
|
339
|
+
|
|
340
|
+
assert len(calls) == 1
|
|
341
|
+
assert observed.plan_id == baseline.plan_id
|
|
342
|
+
assert observed.to_json() == baseline.to_json()
|
|
343
|
+
assert observed.document_metadata["planning"]["linguistic_passes"] == 2
|
|
344
|
+
skipped = next(
|
|
345
|
+
event
|
|
346
|
+
for event in events
|
|
347
|
+
if event.kind == "phase.started" and event.phase == "source_analysis"
|
|
348
|
+
)
|
|
349
|
+
assert skipped.details["skipped"] is True
|
|
350
|
+
assert [event.pass_index for event in events if event.kind == "run.started"] == [2]
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def test_unchanged_spokenform_reuses_source_analysis(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
354
|
+
calls = _count_analysis_calls(monkeypatch)
|
|
355
|
+
events = []
|
|
356
|
+
plan = UtterancePlanner(
|
|
357
|
+
PlannerConfig(language="en-us", document_format="plain", text_preparation="spokenform")
|
|
358
|
+
).plan("Hello world.", on_progress=events.append)
|
|
359
|
+
|
|
360
|
+
assert plan.texts.spoken == "Hello world."
|
|
361
|
+
assert calls == ["Hello world."]
|
|
362
|
+
reused = [
|
|
363
|
+
event
|
|
364
|
+
for event in events
|
|
365
|
+
if event.phase == "spoken_analysis" and event.details.get("reused") is True
|
|
366
|
+
]
|
|
367
|
+
assert [event.kind for event in reused] == ["phase.started", "phase.completed"]
|
|
368
|
+
assert not any(
|
|
369
|
+
event.kind == "run.started" and event.phase == "spoken_analysis" for event in events
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def test_changed_spokenform_still_analyzes_prepared_text(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
374
|
+
calls = _count_analysis_calls(monkeypatch)
|
|
375
|
+
source = "Dr. Smith lives here."
|
|
376
|
+
plan = UtterancePlanner(
|
|
377
|
+
PlannerConfig(language="en-us", document_format="plain", text_preparation="spokenform")
|
|
378
|
+
).plan(source)
|
|
379
|
+
|
|
380
|
+
assert plan.texts.spoken == "Doctor Smith lives here."
|
|
381
|
+
assert calls == [source, plan.texts.spoken]
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from dataclasses import fields
|
|
7
|
+
from types import SimpleNamespace
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from utterplan import (
|
|
12
|
+
LinguisticsConfig,
|
|
13
|
+
PlannerConfig,
|
|
14
|
+
PlannerProgressEvent,
|
|
15
|
+
ProgressCallback,
|
|
16
|
+
UtterancePlanner,
|
|
17
|
+
compile_document,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
TEXT = "Hello world."
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _identity_config(**kwargs: object) -> PlannerConfig:
|
|
24
|
+
return PlannerConfig(
|
|
25
|
+
language="en-us",
|
|
26
|
+
document_format="plain",
|
|
27
|
+
text_preparation="identity",
|
|
28
|
+
**kwargs,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_public_progress_callback_reports_major_phases_and_runs() -> None:
|
|
33
|
+
events: list[PlannerProgressEvent] = []
|
|
34
|
+
planner = UtterancePlanner(_identity_config())
|
|
35
|
+
|
|
36
|
+
plan = planner.plan(TEXT, on_progress=events.append)
|
|
37
|
+
|
|
38
|
+
phases = [event.phase for event in events if event.kind == "phase.started"]
|
|
39
|
+
assert phases == [
|
|
40
|
+
"parse",
|
|
41
|
+
"source_analysis",
|
|
42
|
+
"preparation",
|
|
43
|
+
"spoken_analysis",
|
|
44
|
+
"segmentation",
|
|
45
|
+
"finalization",
|
|
46
|
+
]
|
|
47
|
+
for phase in phases:
|
|
48
|
+
assert (
|
|
49
|
+
sum(event.kind == "phase.completed" and event.phase == phase for event in events) == 1
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
run_events = [event for event in events if event.kind.startswith("run.")]
|
|
53
|
+
assert [(event.kind, event.phase, event.completed, event.total) for event in run_events] == [
|
|
54
|
+
("run.started", "spoken_analysis", 0, 1),
|
|
55
|
+
("run.completed", "spoken_analysis", 1, 1),
|
|
56
|
+
]
|
|
57
|
+
skipped_source = next(
|
|
58
|
+
event
|
|
59
|
+
for event in events
|
|
60
|
+
if event.kind == "phase.started" and event.phase == "source_analysis"
|
|
61
|
+
)
|
|
62
|
+
assert skipped_source.details["skipped"] is True
|
|
63
|
+
assert all(event.pass_total == 2 for event in run_events)
|
|
64
|
+
assert all(event.language == "en-us" for event in run_events)
|
|
65
|
+
assert all(event.char_count == len(TEXT) for event in run_events)
|
|
66
|
+
assert all(event.provider == "fallback" and event.model is None for event in run_events)
|
|
67
|
+
assert plan.texts.spoken == TEXT
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_compile_document_forwards_progress_callback() -> None:
|
|
71
|
+
events: list[PlannerProgressEvent] = []
|
|
72
|
+
result = compile_document(
|
|
73
|
+
TEXT,
|
|
74
|
+
input_format="plain",
|
|
75
|
+
config=_identity_config(),
|
|
76
|
+
on_progress=events.append,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
assert result.plan.texts.spoken == TEXT
|
|
80
|
+
assert events
|
|
81
|
+
assert events[0].kind == "phase.started"
|
|
82
|
+
assert events[0].phase == "parse"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_planner_compile_forwards_progress_callback() -> None:
|
|
86
|
+
events: list[PlannerProgressEvent] = []
|
|
87
|
+
result = UtterancePlanner(_identity_config()).compile(TEXT, on_progress=events.append)
|
|
88
|
+
|
|
89
|
+
assert result.plan.texts.spoken == TEXT
|
|
90
|
+
assert any(event.phase == "finalization" for event in events)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_progress_callback_exceptions_propagate() -> None:
|
|
94
|
+
class MarkerError(RuntimeError):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def callback(_event: PlannerProgressEvent) -> None:
|
|
98
|
+
raise MarkerError
|
|
99
|
+
|
|
100
|
+
with pytest.raises(MarkerError):
|
|
101
|
+
UtterancePlanner(_identity_config()).plan(TEXT, on_progress=callback)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_model_load_events_bracket_real_load_and_cached_calls(
|
|
105
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
106
|
+
) -> None:
|
|
107
|
+
timeline: list[str] = []
|
|
108
|
+
|
|
109
|
+
class Pipeline:
|
|
110
|
+
meta = {"version": "1.0"}
|
|
111
|
+
|
|
112
|
+
def __call__(self, text: str) -> list[SimpleNamespace]:
|
|
113
|
+
timeline.append("provider")
|
|
114
|
+
return [
|
|
115
|
+
SimpleNamespace(
|
|
116
|
+
idx=match.start(),
|
|
117
|
+
text=match.group(0),
|
|
118
|
+
pos_="NOUN",
|
|
119
|
+
tag_="NN",
|
|
120
|
+
lemma_=match.group(0).lower(),
|
|
121
|
+
morph="",
|
|
122
|
+
)
|
|
123
|
+
for match in re.finditer(r"\S+", text)
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
pipeline = Pipeline()
|
|
127
|
+
loaded: list[str] = []
|
|
128
|
+
|
|
129
|
+
def load(model: str) -> Pipeline:
|
|
130
|
+
timeline.append("load")
|
|
131
|
+
loaded.append(model)
|
|
132
|
+
return pipeline
|
|
133
|
+
|
|
134
|
+
monkeypatch.setitem(
|
|
135
|
+
sys.modules,
|
|
136
|
+
"spacy",
|
|
137
|
+
SimpleNamespace(__version__="3.7.0", load=load),
|
|
138
|
+
)
|
|
139
|
+
config = _identity_config(
|
|
140
|
+
linguistics=LinguisticsConfig(use_spacy=True, spacy_model="fake_model", require_spacy=True)
|
|
141
|
+
)
|
|
142
|
+
events: list[PlannerProgressEvent] = []
|
|
143
|
+
|
|
144
|
+
def report(event: PlannerProgressEvent) -> None:
|
|
145
|
+
events.append(event)
|
|
146
|
+
timeline.append(event.kind)
|
|
147
|
+
|
|
148
|
+
planner = UtterancePlanner(config)
|
|
149
|
+
planner.plan(TEXT, on_progress=report)
|
|
150
|
+
planner.plan(TEXT, on_progress=report)
|
|
151
|
+
|
|
152
|
+
assert loaded == ["fake_model"]
|
|
153
|
+
assert [event.kind for event in events if event.kind.startswith("model.")] == [
|
|
154
|
+
"model.started",
|
|
155
|
+
"model.completed",
|
|
156
|
+
]
|
|
157
|
+
model_events = [event for event in events if event.kind.startswith("model.")]
|
|
158
|
+
assert all(event.phase == "spoken_analysis" for event in model_events)
|
|
159
|
+
assert all(event.language == "en-us" for event in model_events)
|
|
160
|
+
assert all(event.provider == "spacy" and event.model == "fake_model" for event in model_events)
|
|
161
|
+
assert timeline.index("run.started") < timeline.index("model.started")
|
|
162
|
+
assert (
|
|
163
|
+
timeline.index("model.started") < timeline.index("load") < timeline.index("model.completed")
|
|
164
|
+
)
|
|
165
|
+
assert timeline.index("model.completed") < timeline.index("provider")
|
|
166
|
+
assert timeline.index("provider") < timeline.index("run.completed")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_progress_callback_type_is_public() -> None:
|
|
170
|
+
def callback(_event: PlannerProgressEvent) -> None:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
public_callback: ProgressCallback = callback
|
|
174
|
+
assert callable(public_callback)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_callback_output_matches_baseline_and_events_are_public_primitives() -> None:
|
|
178
|
+
source = "Dr. Smith lives here."
|
|
179
|
+
config = PlannerConfig(language="en-us", document_format="plain", text_preparation="spokenform")
|
|
180
|
+
baseline = UtterancePlanner(config).plan(source)
|
|
181
|
+
events: list[PlannerProgressEvent] = []
|
|
182
|
+
observed = UtterancePlanner(config).plan(source, on_progress=events.append)
|
|
183
|
+
|
|
184
|
+
assert observed.plan_id == baseline.plan_id
|
|
185
|
+
assert observed.to_json() == baseline.to_json()
|
|
186
|
+
|
|
187
|
+
def is_public_value(value: object) -> bool:
|
|
188
|
+
if value is None or isinstance(value, (str, int, float, bool)):
|
|
189
|
+
return True
|
|
190
|
+
if isinstance(value, Mapping):
|
|
191
|
+
return all(
|
|
192
|
+
isinstance(key, str) and is_public_value(item) for key, item in value.items()
|
|
193
|
+
)
|
|
194
|
+
if isinstance(value, (list, tuple)):
|
|
195
|
+
return all(is_public_value(item) for item in value)
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
assert events
|
|
199
|
+
assert all(
|
|
200
|
+
is_public_value(getattr(event, field.name)) for event in events for field in fields(event)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def test_multilingual_run_progress_is_monotonic_and_matches_plan_runs() -> None:
|
|
205
|
+
source = (
|
|
206
|
+
'---\nssmd_version: "0.9"\nlanguage: en-us\n---\n'
|
|
207
|
+
'Hello [bonjour]{lang="fr"} [hola]{lang="es"}.'
|
|
208
|
+
)
|
|
209
|
+
events: list[PlannerProgressEvent] = []
|
|
210
|
+
plan = UtterancePlanner(
|
|
211
|
+
PlannerConfig(language="en-us", document_format="ssmd", text_preparation="identity")
|
|
212
|
+
).plan(source, on_progress=events.append)
|
|
213
|
+
|
|
214
|
+
started = [
|
|
215
|
+
event
|
|
216
|
+
for event in events
|
|
217
|
+
if event.kind == "run.started" and event.phase == "spoken_analysis"
|
|
218
|
+
]
|
|
219
|
+
completed = [
|
|
220
|
+
event
|
|
221
|
+
for event in events
|
|
222
|
+
if event.kind == "run.completed" and event.phase == "spoken_analysis"
|
|
223
|
+
]
|
|
224
|
+
expected_languages = [run.language for run in plan.languages]
|
|
225
|
+
|
|
226
|
+
assert [event.language for event in started] == expected_languages
|
|
227
|
+
assert [event.language for event in completed] == expected_languages
|
|
228
|
+
assert [event.completed for event in started] == list(range(len(expected_languages)))
|
|
229
|
+
assert [event.completed for event in completed] == list(range(1, len(expected_languages) + 1))
|
|
230
|
+
assert all(event.total == len(expected_languages) for event in started + completed)
|
|
231
|
+
assert all(event.pass_index == 2 and event.pass_total == 2 for event in started + completed)
|
|
@@ -54,6 +54,7 @@ from .model import (
|
|
|
54
54
|
VoiceDirective,
|
|
55
55
|
)
|
|
56
56
|
from .planner import UtterancePlanner
|
|
57
|
+
from .progress import PlannerProgressEvent, ProgressCallback, ProgressKind, ProgressPhase
|
|
57
58
|
from .versioning import (
|
|
58
59
|
CURRENT_SCHEMA_VERSION,
|
|
59
60
|
FORMAT,
|
|
@@ -75,6 +76,10 @@ __all__ = [
|
|
|
75
76
|
"SSMDConfig",
|
|
76
77
|
"parse_duration",
|
|
77
78
|
"UtterancePlanner",
|
|
79
|
+
"PlannerProgressEvent",
|
|
80
|
+
"ProgressCallback",
|
|
81
|
+
"ProgressKind",
|
|
82
|
+
"ProgressPhase",
|
|
78
83
|
"UtterancePlan",
|
|
79
84
|
"PlanSource",
|
|
80
85
|
"PlanTexts",
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
21
|
+
__version__ = version = '0.3.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 4)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g938cb2c4a'
|
|
@@ -6,6 +6,7 @@ from typing import Literal
|
|
|
6
6
|
from .config import PlannerConfig
|
|
7
7
|
from .exceptions import ConfigurationError
|
|
8
8
|
from .model import Diagnostic, UtterancePlan
|
|
9
|
+
from .progress import ProgressCallback
|
|
9
10
|
|
|
10
11
|
InputFormat = Literal["ssmd", "plain"]
|
|
11
12
|
|
|
@@ -72,13 +73,14 @@ def compile_document(
|
|
|
72
73
|
config: PlannerConfig,
|
|
73
74
|
fallback_language: str | None = None,
|
|
74
75
|
trace: bool = False,
|
|
76
|
+
on_progress: ProgressCallback | None = None,
|
|
75
77
|
) -> CompileResult:
|
|
76
78
|
"""Compile one SSMD document or explicitly selected plain-text document.
|
|
77
79
|
|
|
78
80
|
The document's declared SSMD language is authoritative. ``fallback_language``
|
|
79
81
|
replaces the configured fallback for this request; it never forces a language
|
|
80
82
|
over document semantics. ``PlannerConfig.language`` remains the compatibility
|
|
81
|
-
fallback when ``fallback_language`` is omitted.
|
|
83
|
+
fallback when ``fallback_language`` is omitted. The optional progress callback is synchronous and operational only; exceptions raised by it propagate to the caller.
|
|
82
84
|
"""
|
|
83
85
|
if input_format not in {"ssmd", "plain"}:
|
|
84
86
|
raise ConfigurationError("input_format must be 'ssmd' or 'plain'")
|
|
@@ -94,7 +96,7 @@ def compile_document(
|
|
|
94
96
|
|
|
95
97
|
planner = UtterancePlanner(effective_config)
|
|
96
98
|
try:
|
|
97
|
-
return planner.compile(text, config=effective_config, trace=trace)
|
|
99
|
+
return planner.compile(text, config=effective_config, trace=trace, on_progress=on_progress)
|
|
98
100
|
finally:
|
|
99
101
|
planner.close()
|
|
100
102
|
|