utterplan 0.6.1__tar.gz → 0.6.2__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.6.2/AGENTS.md +29 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/PKG-INFO +1 -1
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/changelog.md +16 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/python-api.md +22 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/ssmdbook.md +30 -0
- utterplan-0.6.2/tests/test_book_api.py +439 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_renderplan_dependency_contract.py +10 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/__init__.py +26 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/_version.py +3 -3
- utterplan-0.6.2/utterplan/book.py +598 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/cli.py +125 -323
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan.egg-info/PKG-INFO +1 -1
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan.egg-info/SOURCES.txt +2 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan.egg-info/scm_file_list.json +40 -0
- utterplan-0.6.2/utterplan.egg-info/scm_version.json +8 -0
- utterplan-0.6.1/AGENTS.md +0 -20
- utterplan-0.6.1/utterplan.egg-info/scm_version.json +0 -8
- {utterplan-0.6.1 → utterplan-0.6.2}/.codecrate.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/.github/workflows/codecov.yml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/.github/workflows/pre-commit.yml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/.github/workflows/python-publish.yml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/.github/workflows/tests.yml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/.gitignore +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/.pre-commit-config.yaml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/LICENSE +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/MANIFEST.in +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/NOTICE +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/README.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/Makefile +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/architecture.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/cli.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/conf.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/consumer-guide.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/coordinate-spaces.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/debugging.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/format.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/getting-started.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/index.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/make.bat +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/phase-a-completion.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/pykokoro-integration.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/docs/requirements.txt +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/examples/basic.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/examples/chapter.ssmd +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/examples/compile_file.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/examples/inspect_plan.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/examples/multilingual.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/examples/ssmd_example.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/pyproject.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/setup.cfg +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/spec/schemas/v1.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/spec/schemas/v2.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/spec/schemas/v3.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/spec/schemas/v4.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/spec/utterplan.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/__init__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/compiler_helpers.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/fixtures/canonical_consumer_contract.expected.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/fixtures/canonical_consumer_contract.ssmd +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/fixtures/ssmd_09_comprehensive.ssmd +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/basic_en.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/directives.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/markers.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/multilingual.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/parenthetical.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/spokenform_offsets.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/ssmd_09_comprehensive.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/golden/ssmd_breaks.utterplan.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/__init__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/cases.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/basic_en.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/directives.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/markers.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/multilingual.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/parenthetical.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/spokenform_offsets.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/ssmd_09_comprehensive.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/fixtures/v4/ssmd_breaks.utterplan.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/goldens/multilingual_preparation.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/goldens/spokenform_numbers.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/goldens/ssmd_break.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/goldens/ssmd_language_detection.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/goldens/ssmd_marker.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/goldens/ssmd_pronunciation.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/test_legacy_json_import.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/migration/test_semantic_migration_regressions.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/README.md +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v1/basic.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v1/directives.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v1/markers.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v1/multilingual.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v1/parenthetical.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v1/spokenform_offsets.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v1/ssmd_breaks.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v2/directives.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v3/multilingual.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v3/ssmd_09_comprehensive.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/schema_history/v5/canonical_consumer_contract.toml +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_atomic_io.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_batch.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_boundaries.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_cli.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_cli_batch.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_cli_render_book.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_cli_ssmdbook.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_compiler.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_consumer_contract.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_consumer_readiness.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_coordinates.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_dependency_architecture.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_directives.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_explain.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_fake_renderer.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_flow_compilation.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_format.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_golden.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_identity_units.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_import_boundaries.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_language.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_linguistics.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_migration_framework.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_migration_v3_to_v4.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_migration_v4_to_v5.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_pauses.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_planner.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_preparation.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_preview.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_progress.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_public_identity.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_renderability.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_renderability_repair.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_renderplan_batch.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_renderplan_export.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_renderplan_replan.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_renderplan_v7_contract.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_replacement_provenance.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_schema_history.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_schema_registry.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_schema_strict.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_schema_v6_source.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_schema_v7.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_segment_topology.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_segmentation_adapter.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_segmentation_recovery.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_semantic_boundaries.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_sentence_topology.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_ssmd_09_parser.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_ssmd_substitution_preparation.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_ssmdbook.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_token_topology.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_toml_format.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_ttsready_preparation_adapter.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/tests/test_version.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/__main__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/atomic_io.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/attempt_serialization.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/attempts.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/batch.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/boundaries.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/codecs/__init__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/codecs/v4_toml.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/codecs/v5_toml.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/codecs/v6_toml.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/codecs/v7_toml.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/compiler.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/config.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/diagnostics.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/directives.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/exceptions.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/explain.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/flow_projection.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/format.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/hashing.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/language.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/linguistics.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migration.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/__init__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/registry.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/v1_to_v2.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/v2_to_v3.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/v3_to_v4.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/v4_to_v5.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/v5_to_v6.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/migrations/v6_to_v7.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/model.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/parsers.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/pauses.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/planner.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/preparation.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/preview.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/progress.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/py.typed +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/renderability.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/renderplan_batch.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/renderplan_export.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/renderplan_grouping.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/renderplan_projection.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/renderplan_replan.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/schema_registry.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/schemas/v1.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/schemas/v2.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/schemas/v3.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/schemas/v4.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/segmentation.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/serialization.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/spacy_models.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/spans.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/ssmdbook.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/__init__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/document/__init__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/document/plain.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/document/ssmd.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/preparation/__init__.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/preparation/identity.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/preparation/ssmd.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/preparation/ttsready.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/stages/protocols.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/token_topology.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/toml_codec.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/trace_codec.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/units.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/utterplan.schema.json +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan/versioning.py +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan.egg-info/dependency_links.txt +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan.egg-info/entry_points.txt +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan.egg-info/requires.txt +0 -0
- {utterplan-0.6.1 → utterplan-0.6.2}/utterplan.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
<!-- Generated by memoryledger. Do not edit directly. -->
|
|
4
|
+
<!-- Source: memoryledger records; regenerate with `memoryledger render` and `memoryledger export`. -->
|
|
5
|
+
|
|
6
|
+
## Project memory policy
|
|
7
|
+
|
|
8
|
+
This file contains reviewed long-term project memory.
|
|
9
|
+
|
|
10
|
+
## Rules
|
|
11
|
+
|
|
12
|
+
- UtterPlan is engine-independent. Core code must not import PyKokoro, PiperSynth, kokorog2p, piperg2p, ONNX Runtime, audio packages, or any consumer package.
|
|
13
|
+
- UtterPlan ends before G2P. Engine/model selection, synthesis, and audio composition happen downstream.
|
|
14
|
+
- `.utterplan.toml` schema v7 is the current inter-package contract; schema-v5/v6 TOML and historical JSON schemas v1–v4 remain sequential migration inputs only, and only the current schema version is natively supported.
|
|
15
|
+
- Python objects may not contain semantic state that cannot survive file roundtrip.
|
|
16
|
+
- Public FlowPlan objects contain no phonemes, model token IDs, models, sessions, or audio; completed plans must not retain spaCy or provider documents.
|
|
17
|
+
- Every offset has a documented coordinate space.
|
|
18
|
+
- Default planning is deterministic; the same source and configuration always produce the same executable plan and `plan_id`.
|
|
19
|
+
- Schema changes require explicit compatibility review, frozen historical fixture coverage, deterministic migration tests, and package artifact checks.
|
|
20
|
+
- Released schema files are immutable and retained under versioned paths; schemas v5, v6, and v7 have no JSON Schema resources.
|
|
21
|
+
- Every supported older schema must retain a sequential migration path to current (v1 through v6 feed v7).
|
|
22
|
+
- Migrations operate on serialized plain data and may not call parser, planner, G2P, renderer, or audio stages.
|
|
23
|
+
- Package version and schema version remain independent. Migration preserves a compiled plan; replanning is a separate operation.
|
|
24
|
+
- Optional compiler traces are sidecars; they must never alter executable plan content or identity.
|
|
25
|
+
- UtterPlan parses SSMD 0.9 only; it does not parse SSMD 0.8 or migrate source documents (`ssmd migrate` owns that).
|
|
26
|
+
- Book commands (`compile-book`, `render-book`) read the SSMDBook manifest only for chapter discovery, never modify the manifest or source chapters, write derived plans to separate directories, and never build book-level combined plans.
|
|
27
|
+
- RenderPlan projection is optional: VoiceRender creates and validates engine-backed RenderProfiles, while UtterPlan projects stored schema-v7 FlowPlans into profile-specific RenderPlans (with `renderplan-set.toml` and reports) and performs deterministic replan refinement only.
|
|
28
|
+
- UtterPlan depends directly on SSMD, PhraseSplit, TTSReady, and the renderplan contract package only; production code does not import Spokenform, and spaCy stays optional best-effort enrichment.
|
|
29
|
+
- Repository tests must be self-contained and must not require sibling consumer checkouts; consumer-specific compatibility testing belongs in the consumer repository.
|
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.6.2] - 2026-10-14
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added public book compilation and RenderPlan projection services with safe chapter selection and per-chapter reporting
|
|
12
|
+
|
|
13
|
+
<!-- generated by releaseledger -->
|
|
14
|
+
|
|
15
|
+
## [0.6.1] - 2026-10-13
|
|
16
|
+
|
|
17
|
+
### Documentation
|
|
18
|
+
|
|
19
|
+
- Documented the arc42 system architecture, structure, and boundaries
|
|
20
|
+
|
|
21
|
+
<!-- generated by releaseledger -->
|
|
22
|
+
|
|
7
23
|
## [v0.6.0] - 2026-10-13
|
|
8
24
|
|
|
9
25
|
### Added
|
|
@@ -187,6 +187,28 @@ from utterplan.renderplan_batch import RenderRequest, new_renderplan_set, render
|
|
|
187
187
|
|
|
188
188
|
`render_to_files()` projects schema-v7 `FlowPlan` inputs; grouping, source mapping, export, and replan logic remain UtterPlan-owned.
|
|
189
189
|
|
|
190
|
+
## Public SSMDBook services
|
|
191
|
+
|
|
192
|
+
`compile_book()` and `render_book()` are the public Python counterparts of the `compile-book` and `render-book` commands. They take paths and keyword options directly; callers do not construct `argparse.Namespace`, spawn a subprocess, or reproduce chapter selection, path safety, variant naming, snapshot/set initialization, or report logic. The CLI delegates to these same services.
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from utterplan import compile_book, render_book
|
|
196
|
+
|
|
197
|
+
compiled = compile_book(".", chapters="5-17")
|
|
198
|
+
projected = render_book(
|
|
199
|
+
".",
|
|
200
|
+
profile="narration.renderprofile.toml",
|
|
201
|
+
chapters="5-17",
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
assert compiled.exit_code == projected.exit_code == 0
|
|
205
|
+
print(projected.variant) # resolved <label>--<profile fingerprint>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`BookCompileRequest` / `BookRenderRequest` are immutable option records. Their corresponding result records expose the workspace, selector, selected chapter IDs, artifact and report paths, per-chapter outcomes, `failed`, and `exit_code`; the render result also includes `profile_id`, `variant`, `variant_dir`, snapshot, and set paths. `variant` is authoritative: do not duplicate the label normalization or profile fingerprint algorithm downstream.
|
|
209
|
+
|
|
210
|
+
Optional `on_progress` callbacks receive the existing `CompileOutcome` or `RenderOutcome` for each chapter. Optional `on_start` callbacks receive a resolved book-start record before batch processing. Services do not print. They preserve schema-v7 output and CLI filesystem/report behavior; workspace/path safety errors raise `BookServiceError`, and chapter-level failures are returned as outcomes. Pass `language=` to provide the fallback/required-language override, or supply a `PlannerConfig` with `planner_config=`. See the [SSMDBook guide](ssmdbook) for layouts, safety rules, and CLI/Python examples.
|
|
211
|
+
|
|
190
212
|
## Planner progress callbacks
|
|
191
213
|
|
|
192
214
|
`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.
|
|
@@ -137,3 +137,33 @@ the collection's snapshot remains available for inspection and reproducible
|
|
|
137
137
|
replanning. See the [CLI reference](cli.md#produce-renderplans-for-selected-ssmdbook-chapters),
|
|
138
138
|
[RenderPlan](https://pypi.org/project/renderplan/), and
|
|
139
139
|
[VoiceRender](https://pypi.org/project/voicerender/).
|
|
140
|
+
|
|
141
|
+
## Python API for SSMDBook workspaces
|
|
142
|
+
|
|
143
|
+
The Python API and CLI share the same book orchestration and artifact layout. The public services read the manifest for chapter discovery, select by original `source_number`, validate safe workspace paths, and write the same reports and per-chapter plans as the commands above. They do not print to the console or retain CLI argument objects.
|
|
144
|
+
|
|
145
|
+
CLI:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
utterplan compile-book . --chapters 5-17
|
|
149
|
+
utterplan render-book . --profile narration.renderprofile.toml --chapters 5-17
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Equivalent Python:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from utterplan import compile_book, render_book
|
|
156
|
+
|
|
157
|
+
compiled = compile_book(".", chapters="5-17")
|
|
158
|
+
projected = render_book(
|
|
159
|
+
".",
|
|
160
|
+
profile="narration.renderprofile.toml",
|
|
161
|
+
chapters="5-17",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
print(projected.variant) # exact profile-specific RenderPlan directory name
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`BookCompileResult` and `BookRenderResult` expose the resolved workspace, selector, selected chapter IDs, artifact/report paths, per-chapter outcomes, `failed`, and `exit_code`. `BookRenderResult.variant` is the final sanitized label plus profile fingerprint; consumers should use it rather than reproduce the naming algorithm. Optional `on_progress` callbacks receive each compile or projection outcome, and `on_start` callbacks receive resolved selection and output metadata. A chapter must declare its language unless `language=` is explicitly supplied to `compile_book()`; compiler behavior can otherwise be configured with `planner_config=`. Invalid workspaces and unsafe paths raise `BookServiceError`, while ordinary per-chapter failures are represented in outcomes.
|
|
168
|
+
|
|
169
|
+
The public request models `BookCompileRequest` and `BookRenderRequest` capture the corresponding service options. These APIs remain engine-independent: they neither inspect TTS engines nor synthesize or compose audio.
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
import tomlkit
|
|
11
|
+
from renderplan import PausePolicy, PlanningPolicy, RenderProfile, RoleProfile, load_renderplan_set
|
|
12
|
+
from renderplan import codec as renderplan_codec
|
|
13
|
+
|
|
14
|
+
from utterplan import (
|
|
15
|
+
BookCompileRequest,
|
|
16
|
+
BookCompileResult,
|
|
17
|
+
BookRenderRequest,
|
|
18
|
+
BookRenderResult,
|
|
19
|
+
BookServiceError,
|
|
20
|
+
compile_book,
|
|
21
|
+
render_book,
|
|
22
|
+
)
|
|
23
|
+
from utterplan.config import LinguisticsConfig, PlannerConfig
|
|
24
|
+
from utterplan.model import FlowPlan
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _ssmd(text: str) -> str:
|
|
28
|
+
return f'---\nssmd_version: "0.9"\nlanguage: en-US\n---\n{text}\n'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _book(root: Path, numbers: tuple[int, ...]) -> Path:
|
|
32
|
+
chapters = root / "chapters"
|
|
33
|
+
chapters.mkdir(parents=True)
|
|
34
|
+
entries: list[dict[str, object]] = []
|
|
35
|
+
for number in numbers:
|
|
36
|
+
source = chapters / f"chapter-{number:04d}.ssmd.md"
|
|
37
|
+
source.write_text(_ssmd(f"Chapter {number}."), encoding="utf-8")
|
|
38
|
+
entries.append(
|
|
39
|
+
{
|
|
40
|
+
"id": f"chapter-{number:04d}",
|
|
41
|
+
"source_number": number,
|
|
42
|
+
"path": f"chapters/{source.name}",
|
|
43
|
+
"title": f"Chapter {number}",
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
(root / "manifest.json").write_text(
|
|
48
|
+
json.dumps(
|
|
49
|
+
{
|
|
50
|
+
"format": "ssmdconvert.book",
|
|
51
|
+
"schema_version": 1,
|
|
52
|
+
"ssmd_version": "0.9",
|
|
53
|
+
"chapters": entries,
|
|
54
|
+
}
|
|
55
|
+
),
|
|
56
|
+
encoding="utf-8",
|
|
57
|
+
)
|
|
58
|
+
return root
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _profile(path: Path, voice: str = "amy") -> RenderProfile:
|
|
62
|
+
profile = RenderProfile(
|
|
63
|
+
roles={
|
|
64
|
+
"narrator": RoleProfile(language="en-US", binding={"engine": "test", "voice": voice})
|
|
65
|
+
},
|
|
66
|
+
default_role="narrator",
|
|
67
|
+
planning=PlanningPolicy(split="sentence", capacity_fallback=("token",)),
|
|
68
|
+
pauses=PausePolicy(mode="manual", durations_ms={"sentence": 250}),
|
|
69
|
+
)
|
|
70
|
+
renderplan_codec.save_profile(profile, path)
|
|
71
|
+
return renderplan_codec.load_profile(path)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_public_request_and_result_models_are_available_at_package_root() -> None:
|
|
75
|
+
compile_request = BookCompileRequest(Path("book"))
|
|
76
|
+
render_request = BookRenderRequest(Path("book"), Path("profile.renderprofile.toml"))
|
|
77
|
+
compile_result = BookCompileResult(
|
|
78
|
+
workspace=Path("book"),
|
|
79
|
+
chapter_selector="all",
|
|
80
|
+
selected_chapter_ids=(),
|
|
81
|
+
output_dir=Path("book/utterplan"),
|
|
82
|
+
report_path=Path("book/utterplan/compile-report.toml"),
|
|
83
|
+
outcomes=(),
|
|
84
|
+
)
|
|
85
|
+
render_result = BookRenderResult(
|
|
86
|
+
workspace=Path("book"),
|
|
87
|
+
chapter_selector="all",
|
|
88
|
+
profile_path=Path("profile.renderprofile.toml"),
|
|
89
|
+
profile_id="sha256:abc",
|
|
90
|
+
variant="profile--abc",
|
|
91
|
+
variant_dir=Path("book/renderplan/profile--abc"),
|
|
92
|
+
snapshot_path=Path("book/renderplan/profile--abc/renderprofile.toml"),
|
|
93
|
+
set_path=Path("book/renderplan/profile--abc/renderplan-set.toml"),
|
|
94
|
+
report_path=Path("book/renderplan/profile--abc/render-report.toml"),
|
|
95
|
+
selected_chapter_ids=(),
|
|
96
|
+
outcomes=(),
|
|
97
|
+
)
|
|
98
|
+
assert compile_request.book == Path("book")
|
|
99
|
+
assert render_request.profile == Path("profile.renderprofile.toml")
|
|
100
|
+
assert compile_result.exit_code == render_result.exit_code == 0
|
|
101
|
+
assert compile_result.failed == render_result.failed == 0
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_public_book_services_return_artifact_paths_and_progress(tmp_path: Path) -> None:
|
|
105
|
+
root = _book(tmp_path / "ordered.ssmdbook", (5, 7, 6))
|
|
106
|
+
compile_events = []
|
|
107
|
+
compiled = compile_book(
|
|
108
|
+
root,
|
|
109
|
+
chapters="5-7",
|
|
110
|
+
planner_config=PlannerConfig(
|
|
111
|
+
language="en-US",
|
|
112
|
+
text_preparation="identity",
|
|
113
|
+
linguistics=LinguisticsConfig(use_spacy=False),
|
|
114
|
+
),
|
|
115
|
+
on_progress=compile_events.append,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
assert compiled.workspace == root
|
|
119
|
+
assert compiled.chapter_selector == "5-7"
|
|
120
|
+
assert compiled.selected_chapter_ids == ("chapter-0005", "chapter-0007", "chapter-0006")
|
|
121
|
+
assert compiled.output_dir == root / "utterplan"
|
|
122
|
+
assert compiled.report_path == root / "utterplan/compile-report.toml"
|
|
123
|
+
assert compiled.failed == 0
|
|
124
|
+
assert compiled.exit_code == 0
|
|
125
|
+
assert tuple(compile_events) == compiled.outcomes
|
|
126
|
+
assert all(FlowPlan.load(outcome.output).schema_version == 7 for outcome in compiled.outcomes)
|
|
127
|
+
compile_report = tomlkit.parse(compiled.report_path.read_text(encoding="utf-8"))
|
|
128
|
+
assert compile_report["requested"] == 3
|
|
129
|
+
assert compile_report["written"] == 3
|
|
130
|
+
|
|
131
|
+
profile_path = root / "narration.renderprofile.toml"
|
|
132
|
+
profile = _profile(profile_path)
|
|
133
|
+
render_events = []
|
|
134
|
+
rendered = render_book(
|
|
135
|
+
root,
|
|
136
|
+
profile=profile_path,
|
|
137
|
+
chapters="5-7",
|
|
138
|
+
on_progress=render_events.append,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
assert rendered.workspace == root
|
|
142
|
+
assert rendered.chapter_selector == "5-7"
|
|
143
|
+
assert rendered.selected_chapter_ids == compiled.selected_chapter_ids
|
|
144
|
+
assert rendered.profile_path == profile_path
|
|
145
|
+
assert rendered.profile_id == profile.profile_id
|
|
146
|
+
assert rendered.variant == f"narration--{profile.profile_id.removeprefix('sha256:')[:12]}"
|
|
147
|
+
assert rendered.variant_dir == root / "renderplan" / rendered.variant
|
|
148
|
+
assert rendered.snapshot_path == rendered.variant_dir / "renderprofile.toml"
|
|
149
|
+
assert rendered.set_path == rendered.variant_dir / "renderplan-set.toml"
|
|
150
|
+
assert rendered.report_path == rendered.variant_dir / "render-report.toml"
|
|
151
|
+
assert rendered.failed == 0
|
|
152
|
+
assert rendered.exit_code == 0
|
|
153
|
+
assert tuple(render_events) == rendered.outcomes
|
|
154
|
+
collection = load_renderplan_set(rendered.set_path)
|
|
155
|
+
assert [chapter.id for chapter in collection.chapters] == list(compiled.selected_chapter_ids)
|
|
156
|
+
render_report = tomlkit.parse(rendered.report_path.read_text(encoding="utf-8"))
|
|
157
|
+
assert render_report["requested"] == 3
|
|
158
|
+
assert render_report["written"] == 3
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@pytest.mark.parametrize(
|
|
162
|
+
("selector", "expected"),
|
|
163
|
+
[
|
|
164
|
+
("all", (5, 7, 6, 11, 13, 12)),
|
|
165
|
+
("5", (5,)),
|
|
166
|
+
("5-7", (5, 7, 6)),
|
|
167
|
+
("5,7,11-13", (5, 7, 11, 13, 12)),
|
|
168
|
+
],
|
|
169
|
+
)
|
|
170
|
+
def test_compile_book_selects_original_source_numbers(
|
|
171
|
+
tmp_path: Path, selector: str, expected: tuple[int, ...]
|
|
172
|
+
) -> None:
|
|
173
|
+
root = _book(tmp_path / "selection.ssmdbook", (5, 7, 6, 11, 13, 12))
|
|
174
|
+
result = compile_book(
|
|
175
|
+
root,
|
|
176
|
+
chapters=selector,
|
|
177
|
+
planner_config=PlannerConfig(
|
|
178
|
+
language="en-US",
|
|
179
|
+
text_preparation="identity",
|
|
180
|
+
linguistics=LinguisticsConfig(use_spacy=False),
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
assert tuple(int(outcome.id[-4:]) for outcome in result.outcomes) == expected
|
|
184
|
+
assert result.exit_code == 0
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_public_api_matches_cli_artifacts_and_report_semantics(tmp_path: Path, capsys) -> None:
|
|
188
|
+
from utterplan.cli import main
|
|
189
|
+
|
|
190
|
+
selector = "5,7,11-13"
|
|
191
|
+
api_root = _book(tmp_path / "api.ssmdbook", (5, 7, 6, 11, 13, 12))
|
|
192
|
+
cli_root = _book(tmp_path / "cli.ssmdbook", (5, 7, 6, 11, 13, 12))
|
|
193
|
+
config = PlannerConfig(
|
|
194
|
+
language="en-US",
|
|
195
|
+
text_preparation="identity",
|
|
196
|
+
linguistics=LinguisticsConfig(use_spacy=False),
|
|
197
|
+
)
|
|
198
|
+
api_compile = compile_book(api_root, chapters=selector, planner_config=config)
|
|
199
|
+
assert (
|
|
200
|
+
main(
|
|
201
|
+
[
|
|
202
|
+
"compile-book",
|
|
203
|
+
str(cli_root),
|
|
204
|
+
"--chapters",
|
|
205
|
+
selector,
|
|
206
|
+
"--text-preparation",
|
|
207
|
+
"identity",
|
|
208
|
+
"--spacy",
|
|
209
|
+
"off",
|
|
210
|
+
]
|
|
211
|
+
)
|
|
212
|
+
== 0
|
|
213
|
+
)
|
|
214
|
+
capsys.readouterr()
|
|
215
|
+
|
|
216
|
+
assert api_compile.selected_chapter_ids == (
|
|
217
|
+
"chapter-0005",
|
|
218
|
+
"chapter-0007",
|
|
219
|
+
"chapter-0011",
|
|
220
|
+
"chapter-0013",
|
|
221
|
+
"chapter-0012",
|
|
222
|
+
)
|
|
223
|
+
api_compile_outputs = [
|
|
224
|
+
(outcome.id, outcome.plan_id, outcome.output.relative_to(api_root))
|
|
225
|
+
for outcome in api_compile.outcomes
|
|
226
|
+
]
|
|
227
|
+
cli_compile_outputs = [
|
|
228
|
+
(
|
|
229
|
+
chapter_id,
|
|
230
|
+
FlowPlan.load(cli_root / "utterplan" / f"{chapter_id}.utterplan.toml").plan_id,
|
|
231
|
+
Path("utterplan") / f"{chapter_id}.utterplan.toml",
|
|
232
|
+
)
|
|
233
|
+
for chapter_id in api_compile.selected_chapter_ids
|
|
234
|
+
]
|
|
235
|
+
assert api_compile_outputs == cli_compile_outputs
|
|
236
|
+
api_compile_report = tomlkit.parse(api_compile.report_path.read_text(encoding="utf-8"))
|
|
237
|
+
cli_compile_report = tomlkit.parse(
|
|
238
|
+
(cli_root / "utterplan/compile-report.toml").read_text(encoding="utf-8")
|
|
239
|
+
)
|
|
240
|
+
compile_summary = ("format", "version", "requested", "written", "failed", "skipped", "complete")
|
|
241
|
+
assert tuple(api_compile_report[key] for key in compile_summary) == tuple(
|
|
242
|
+
cli_compile_report[key] for key in compile_summary
|
|
243
|
+
)
|
|
244
|
+
assert [
|
|
245
|
+
(item["id"], item.get("plan_id"), item["status"]) for item in api_compile_report["item"]
|
|
246
|
+
] == [(item["id"], item.get("plan_id"), item["status"]) for item in cli_compile_report["item"]]
|
|
247
|
+
|
|
248
|
+
api_profile_path = api_root / "narration.renderprofile.toml"
|
|
249
|
+
cli_profile_path = cli_root / "narration.renderprofile.toml"
|
|
250
|
+
api_profile = _profile(api_profile_path)
|
|
251
|
+
cli_profile = _profile(cli_profile_path)
|
|
252
|
+
api_render = render_book(api_root, profile=api_profile_path, chapters=selector)
|
|
253
|
+
assert (
|
|
254
|
+
main(
|
|
255
|
+
[
|
|
256
|
+
"render-book",
|
|
257
|
+
str(cli_root),
|
|
258
|
+
"--chapters",
|
|
259
|
+
selector,
|
|
260
|
+
"--profile",
|
|
261
|
+
str(cli_profile_path),
|
|
262
|
+
]
|
|
263
|
+
)
|
|
264
|
+
== 0
|
|
265
|
+
)
|
|
266
|
+
capsys.readouterr()
|
|
267
|
+
|
|
268
|
+
cli_variant_dir = cli_root / "renderplan" / api_render.variant
|
|
269
|
+
assert api_profile.profile_id == cli_profile.profile_id
|
|
270
|
+
assert api_render.variant == f"narration--{api_profile.profile_id.removeprefix('sha256:')[:12]}"
|
|
271
|
+
cli_set_path = cli_variant_dir / "renderplan-set.toml"
|
|
272
|
+
assert api_render.set_path.read_bytes() == cli_set_path.read_bytes()
|
|
273
|
+
api_render_outputs = [
|
|
274
|
+
(
|
|
275
|
+
outcome.id,
|
|
276
|
+
outcome.source_plan_id,
|
|
277
|
+
outcome.render_plan_id,
|
|
278
|
+
outcome.source_plan.relative_to(api_root),
|
|
279
|
+
outcome.output.relative_to(api_render.variant_dir),
|
|
280
|
+
)
|
|
281
|
+
for outcome in api_render.outcomes
|
|
282
|
+
]
|
|
283
|
+
cli_render_outputs = [
|
|
284
|
+
(
|
|
285
|
+
chapter_id,
|
|
286
|
+
FlowPlan.load(cli_root / "utterplan" / f"{chapter_id}.utterplan.toml").plan_id,
|
|
287
|
+
renderplan_codec.load_plan(cli_variant_dir / f"{chapter_id}.renderplan.toml").plan_id,
|
|
288
|
+
Path("utterplan") / f"{chapter_id}.utterplan.toml",
|
|
289
|
+
Path(f"{chapter_id}.renderplan.toml"),
|
|
290
|
+
)
|
|
291
|
+
for chapter_id in api_render.selected_chapter_ids
|
|
292
|
+
]
|
|
293
|
+
assert api_render_outputs == cli_render_outputs
|
|
294
|
+
api_render_report = tomlkit.parse(api_render.report_path.read_text(encoding="utf-8"))
|
|
295
|
+
cli_render_report = tomlkit.parse(
|
|
296
|
+
(cli_variant_dir / "render-report.toml").read_text(encoding="utf-8")
|
|
297
|
+
)
|
|
298
|
+
render_summary = (
|
|
299
|
+
"format",
|
|
300
|
+
"version",
|
|
301
|
+
"requested",
|
|
302
|
+
"written",
|
|
303
|
+
"failed",
|
|
304
|
+
"skipped",
|
|
305
|
+
"complete",
|
|
306
|
+
"variant",
|
|
307
|
+
"profile_id",
|
|
308
|
+
)
|
|
309
|
+
assert tuple(api_render_report[key] for key in render_summary) == tuple(
|
|
310
|
+
cli_render_report[key] for key in render_summary
|
|
311
|
+
)
|
|
312
|
+
assert [
|
|
313
|
+
(
|
|
314
|
+
item["id"],
|
|
315
|
+
item["source_number"],
|
|
316
|
+
item["status"],
|
|
317
|
+
item.get("source_plan_id"),
|
|
318
|
+
item.get("render_plan_id"),
|
|
319
|
+
)
|
|
320
|
+
for item in api_render_report["item"]
|
|
321
|
+
] == [
|
|
322
|
+
(
|
|
323
|
+
item["id"],
|
|
324
|
+
item["source_number"],
|
|
325
|
+
item["status"],
|
|
326
|
+
item.get("source_plan_id"),
|
|
327
|
+
item.get("render_plan_id"),
|
|
328
|
+
)
|
|
329
|
+
for item in cli_render_report["item"]
|
|
330
|
+
]
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def test_package_root_book_api_does_not_import_cli() -> None:
|
|
334
|
+
code = (
|
|
335
|
+
"import sys, utterplan; "
|
|
336
|
+
"names=('BookCompileRequest','BookCompileResult','BookRenderRequest',"
|
|
337
|
+
"'BookRenderResult','compile_book','render_book'); "
|
|
338
|
+
"assert all(hasattr(utterplan, name) for name in names); "
|
|
339
|
+
"assert 'utterplan.cli' not in sys.modules"
|
|
340
|
+
)
|
|
341
|
+
subprocess.run([sys.executable, "-c", code], check=True, capture_output=True, text=True)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def test_render_book_incremental_reuse_and_force(tmp_path: Path) -> None:
|
|
345
|
+
root = _book(tmp_path / "incremental.ssmdbook", (5, 6, 7))
|
|
346
|
+
compile_book(
|
|
347
|
+
root,
|
|
348
|
+
planner_config=PlannerConfig(
|
|
349
|
+
language="en-US",
|
|
350
|
+
text_preparation="identity",
|
|
351
|
+
linguistics=LinguisticsConfig(use_spacy=False),
|
|
352
|
+
),
|
|
353
|
+
)
|
|
354
|
+
profile_path = root / "narration.renderprofile.toml"
|
|
355
|
+
_profile(profile_path)
|
|
356
|
+
|
|
357
|
+
first = render_book(root, profile=profile_path, chapters="5")
|
|
358
|
+
second = render_book(root, profile=profile_path, chapters="6")
|
|
359
|
+
assert first.variant == second.variant
|
|
360
|
+
collection = load_renderplan_set(second.set_path)
|
|
361
|
+
assert [chapter.source_number for chapter in collection.chapters] == [5, 6]
|
|
362
|
+
|
|
363
|
+
protected = render_book(root, profile=profile_path, chapters="5")
|
|
364
|
+
assert protected.exit_code == 1
|
|
365
|
+
assert protected.failed == 1
|
|
366
|
+
forced = render_book(root, profile=profile_path, chapters="5", force=True)
|
|
367
|
+
assert forced.exit_code == 0
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_render_book_refuses_orphan_variant_and_profile_mismatch(tmp_path: Path) -> None:
|
|
371
|
+
root = _book(tmp_path / "orphan.ssmdbook", (5,))
|
|
372
|
+
compile_book(
|
|
373
|
+
root,
|
|
374
|
+
planner_config=PlannerConfig(
|
|
375
|
+
language="en-US",
|
|
376
|
+
text_preparation="identity",
|
|
377
|
+
linguistics=LinguisticsConfig(use_spacy=False),
|
|
378
|
+
),
|
|
379
|
+
)
|
|
380
|
+
profile_a_path = root / "narration.renderprofile.toml"
|
|
381
|
+
profile_b_path = root / "other.renderprofile.toml"
|
|
382
|
+
profile_a = _profile(profile_a_path)
|
|
383
|
+
profile_b = _profile(profile_b_path, "joe")
|
|
384
|
+
orphan_dir = (
|
|
385
|
+
root / "renderplan" / f"orphan--{profile_a.profile_id.removeprefix('sha256:')[:12]}"
|
|
386
|
+
)
|
|
387
|
+
orphan_dir.mkdir(parents=True)
|
|
388
|
+
(orphan_dir / "scratch.txt").write_text("unindexed", encoding="utf-8")
|
|
389
|
+
with pytest.raises(BookServiceError, match="refusing to adopt non-empty"):
|
|
390
|
+
render_book(root, profile=profile_a_path, variant="orphan")
|
|
391
|
+
|
|
392
|
+
source_variant = render_book(root, profile=profile_a_path, variant="shared").variant_dir
|
|
393
|
+
target_variant = (
|
|
394
|
+
root / "renderplan" / f"shared--{profile_b.profile_id.removeprefix('sha256:')[:12]}"
|
|
395
|
+
)
|
|
396
|
+
shutil.copytree(source_variant, target_variant)
|
|
397
|
+
with pytest.raises(BookServiceError, match="does not match the active RenderProfile"):
|
|
398
|
+
render_book(root, profile=profile_b_path, chapters="5", variant="shared")
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def test_render_book_refuses_snapshot_identity_mismatch(tmp_path: Path) -> None:
|
|
402
|
+
root = _book(tmp_path / "snapshot.ssmdbook", (5,))
|
|
403
|
+
compile_book(
|
|
404
|
+
root,
|
|
405
|
+
planner_config=PlannerConfig(
|
|
406
|
+
language="en-US",
|
|
407
|
+
text_preparation="identity",
|
|
408
|
+
linguistics=LinguisticsConfig(use_spacy=False),
|
|
409
|
+
),
|
|
410
|
+
)
|
|
411
|
+
profile_path = root / "narration.renderprofile.toml"
|
|
412
|
+
_profile(profile_path)
|
|
413
|
+
result = render_book(root, profile=profile_path, chapters="5")
|
|
414
|
+
changed_profile = _profile(root / "changed.renderprofile.toml", "joe")
|
|
415
|
+
renderplan_codec.save_profile(changed_profile, result.snapshot_path)
|
|
416
|
+
|
|
417
|
+
with pytest.raises(BookServiceError, match="snapshot identity"):
|
|
418
|
+
render_book(root, profile=profile_path, chapters="5")
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def test_public_book_services_refuse_outputs_inside_source_chapters(tmp_path: Path) -> None:
|
|
422
|
+
root = _book(tmp_path / "unsafe.ssmdbook", (5,))
|
|
423
|
+
with pytest.raises(BookServiceError, match="inside the source chapters"):
|
|
424
|
+
compile_book(root, output_dir=root / "chapters" / "plans")
|
|
425
|
+
assert not (root / "chapters/plans").exists()
|
|
426
|
+
|
|
427
|
+
compile_book(
|
|
428
|
+
root,
|
|
429
|
+
planner_config=PlannerConfig(
|
|
430
|
+
language="en-US",
|
|
431
|
+
text_preparation="identity",
|
|
432
|
+
linguistics=LinguisticsConfig(use_spacy=False),
|
|
433
|
+
),
|
|
434
|
+
)
|
|
435
|
+
profile_path = root / "narration.renderprofile.toml"
|
|
436
|
+
_profile(profile_path)
|
|
437
|
+
with pytest.raises(BookServiceError, match="inside source chapters"):
|
|
438
|
+
render_book(root, profile=profile_path, output_root=root / "chapters" / "renderplans")
|
|
439
|
+
assert not (root / "chapters/renderplans").exists()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import tomlkit
|
|
3
6
|
from renderplan import (
|
|
4
7
|
RENDERPLAN_SET_FORMAT,
|
|
5
8
|
RENDERPLAN_SET_VERSION,
|
|
@@ -20,3 +23,10 @@ def test_renderplan_minimum_public_api_is_available() -> None:
|
|
|
20
23
|
assert RENDERPLAN_SET_VERSION == 1
|
|
21
24
|
assert all(isinstance(value, type) for value in _PUBLIC_TYPES)
|
|
22
25
|
assert all(callable(value) for value in _PUBLIC_CODECS)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_renderplan_collection_api_minimum_is_declared_as_runtime_dependency() -> None:
|
|
29
|
+
project = tomlkit.parse(
|
|
30
|
+
(Path(__file__).resolve().parents[1] / "pyproject.toml").read_text(encoding="utf-8")
|
|
31
|
+
).unwrap()
|
|
32
|
+
assert "renderplan>=0.1.1,<0.2" in project["project"]["dependencies"]
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
from ._version import __version__
|
|
2
2
|
from .attempts import PlanDraft, PlanningAttempt, PlanningAttemptStatus
|
|
3
3
|
from .batch import BatchProgressCallback, CompileOutcome, CompileRequest, compile_to_files
|
|
4
|
+
from .book import (
|
|
5
|
+
BookCompileRequest,
|
|
6
|
+
BookCompileResult,
|
|
7
|
+
BookCompileStart,
|
|
8
|
+
BookCompileStartCallback,
|
|
9
|
+
BookRenderRequest,
|
|
10
|
+
BookRenderResult,
|
|
11
|
+
BookRenderStart,
|
|
12
|
+
BookRenderStartCallback,
|
|
13
|
+
BookServiceError,
|
|
14
|
+
RenderProgressCallback,
|
|
15
|
+
compile_book,
|
|
16
|
+
render_book,
|
|
17
|
+
)
|
|
4
18
|
from .boundaries import BoundaryKind
|
|
5
19
|
from .compiler import (
|
|
6
20
|
CompileResult,
|
|
@@ -100,6 +114,18 @@ from .versioning import (
|
|
|
100
114
|
|
|
101
115
|
__all__ = [
|
|
102
116
|
"__version__",
|
|
117
|
+
"BookCompileRequest",
|
|
118
|
+
"BookCompileResult",
|
|
119
|
+
"BookCompileStart",
|
|
120
|
+
"BookCompileStartCallback",
|
|
121
|
+
"BookRenderRequest",
|
|
122
|
+
"BookRenderResult",
|
|
123
|
+
"BookRenderStart",
|
|
124
|
+
"BookRenderStartCallback",
|
|
125
|
+
"BookServiceError",
|
|
126
|
+
"RenderProgressCallback",
|
|
127
|
+
"compile_book",
|
|
128
|
+
"render_book",
|
|
103
129
|
"CompileResult",
|
|
104
130
|
"BatchProgressCallback",
|
|
105
131
|
"CompileOutcome",
|
|
@@ -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.6.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 6,
|
|
21
|
+
__version__ = version = '0.6.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 6, 2)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g2dc6b7932'
|