polyptych 0.2.0__tar.gz → 0.2.1__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.
- {polyptych-0.2.0 → polyptych-0.2.1}/CHANGELOG.md +16 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/PKG-INFO +1 -1
- {polyptych-0.2.0 → polyptych-0.2.1}/pyproject.toml +1 -1
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/image_batch.py +2 -2
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/pipeline_base.py +7 -2
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_pipeline_base.py +43 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/.gitignore +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/CONTRIBUTING.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/LICENSE +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/NOTICE +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/README.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/SECURITY.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/image-presets.yaml +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/image_model_config.yaml +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/model_config.yaml +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/pipeline-presets.yaml +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/providers/gemini-best-practices.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/providers/openai-best-practices.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/providers/xai-best-practices.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/anime/anime-dark.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/editorial/scholarly-monograph.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/infographic/clean-minimalist.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/infographic/semi-flat-vector.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/noir/cinematic-illustrative-noir.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/period-art/attic-vase-pastoral.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-01-genre-classification.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-02-source-analysis.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-03-structure-planning.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-04-content-allocation.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-05-visual-design.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-06-slide-specification-fiction.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-06-slide-specification.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-07-image-generation.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-critique.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-i0-analysis.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-i1-design.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-i2-critique.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-i2-prompts.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/prompts/tasks/task-i2-refine.md +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/common/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/common/compat.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/common/logging_setup.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/common/usage_log.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/_datafiles.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/batch_utils.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/clean_source.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/cli.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/client.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/concurrent_engine.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/cross_validate.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/ext.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/logging_setup.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/model_config.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/models/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/models/infographic.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/models/slide.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/pipeline.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/pipeline_config.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/pipeline_infographic.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/pipeline_task.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/presets.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/prompt_loader.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/providers/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/providers/anthropic.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/providers/base.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/providers/gemini.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/providers/openai.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/providers/vertex.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/providers/xai.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/ref_utils.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/run_config.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/task_registry.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_01_genre.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_02_analysis.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_03_structure.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_04_content.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_05_design.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_06_slides.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_07_prompts.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_i0_analysis.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_i1_design.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_i2_critique.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/tasks/task_i2_prompts.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/text_utils.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/src/polyptych/usage_log.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/common/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/common/test_compat.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/conftest.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/mock_client.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/providers/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/providers/test_base.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/providers/test_helpers.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/conftest.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/factories.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_character_canon.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_cross_validate.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_local_manifest.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_prompt_provider.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_schema_ergonomics.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_slide_image_paths.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_slide_pipeline_flow.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/slide/test_task7_assembly.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/tasks/__init__.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/tasks/test_task_05_design.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_clean_source.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_cli.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_client.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_dependency_resolver.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_image_batch.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_infographic_critique.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_logging_setup.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_pipeline_config.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_pipeline_infographic.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_pipeline_init.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_pipeline_resume.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_ref_utils.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_run_config.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_task_registry.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/polyptych/test_text_utils.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/test_concurrent_engine.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/test_concurrent_engine_ordering.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/test_datafiles.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/test_ext.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/test_model_config.py +0 -0
- {polyptych-0.2.0 → polyptych-0.2.1}/tests/test_presets.py +0 -0
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.1] - 2026-06-20
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Image-generation failures no longer print a stack trace.** A failed
|
|
15
|
+
per-image request (e.g. an OpenAI output-moderation block) already logs a
|
|
16
|
+
one-line warning carrying the error; the redundant `exc_info=True` traceback
|
|
17
|
+
on the `polyptych.image_batch` logger has been dropped and the error message
|
|
18
|
+
folded into that warning, so the log stays a clean one-liner.
|
|
19
|
+
- **Resuming a run no longer crashes when `source_file` is the output dir's own
|
|
20
|
+
`source.md`.** `PipelineBase.__init__` copied the source into the output
|
|
21
|
+
directory unconditionally, so a resume command that passed
|
|
22
|
+
`<output_dir>/source.md` (the path the first run wrote) raised
|
|
23
|
+
`shutil.SameFileError` before any work ran. The copy is now skipped when the
|
|
24
|
+
source and destination resolve to the same file.
|
|
25
|
+
|
|
10
26
|
## [0.2.0] - 2026-06-14
|
|
11
27
|
|
|
12
28
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polyptych
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Turn a source text into visual media — slide decks and single-page infographics, with multi-provider text and image generation
|
|
5
5
|
Project-URL: Homepage, https://github.com/jdinkla/polyptych-studio
|
|
6
6
|
Project-URL: Repository, https://github.com/jdinkla/polyptych-studio
|
|
@@ -208,10 +208,10 @@ class ImageBatchGenerator:
|
|
|
208
208
|
f"{item.index}: {error_msg}"
|
|
209
209
|
)
|
|
210
210
|
logger.warning(
|
|
211
|
-
"Failed to generate image for %s %s",
|
|
211
|
+
"Failed to generate image for %s %s: %s",
|
|
212
212
|
self.label,
|
|
213
213
|
item.index,
|
|
214
|
-
|
|
214
|
+
error_msg,
|
|
215
215
|
)
|
|
216
216
|
return result
|
|
217
217
|
|
|
@@ -136,8 +136,13 @@ class SlidePipelineBase:
|
|
|
136
136
|
(self.output_dir / "prompts").mkdir(exist_ok=True)
|
|
137
137
|
(self.output_dir / "images").mkdir(exist_ok=True)
|
|
138
138
|
|
|
139
|
-
# Copy source material into output directory
|
|
140
|
-
|
|
139
|
+
# Copy source material into output directory. Skip when the source is
|
|
140
|
+
# already the in-dir copy (e.g. a resume run that passes
|
|
141
|
+
# <output_dir>/source.md, the path the first run wrote); copying a file
|
|
142
|
+
# onto itself raises shutil.SameFileError.
|
|
143
|
+
source_copy = self.output_dir / "source.md"
|
|
144
|
+
if self.source_path.resolve() != source_copy.resolve():
|
|
145
|
+
shutil.copy2(self.source_path, source_copy)
|
|
141
146
|
|
|
142
147
|
def _model_for(self, task_name: str) -> str:
|
|
143
148
|
"""Resolve the concrete model string for a pipeline step."""
|
|
@@ -30,6 +30,49 @@ def _build_pipeline(
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
class TestSourceCopy:
|
|
34
|
+
def test_resume_with_in_dir_source_does_not_raise(
|
|
35
|
+
self, tmp_path, source_text, test_model_config
|
|
36
|
+
):
|
|
37
|
+
"""Resuming a run by pointing source_path at the output dir's own
|
|
38
|
+
source.md must not crash. The first run writes <output_dir>/source.md,
|
|
39
|
+
so a natural resume command passes that exact path back in; copying it
|
|
40
|
+
onto itself used to raise shutil.SameFileError before any work ran."""
|
|
41
|
+
output_dir = tmp_path / "output"
|
|
42
|
+
output_dir.mkdir(parents=True)
|
|
43
|
+
in_dir_source = output_dir / "source.md"
|
|
44
|
+
in_dir_source.write_text(source_text)
|
|
45
|
+
|
|
46
|
+
pipeline = SlidePipeline(
|
|
47
|
+
source_path=in_dir_source,
|
|
48
|
+
output_dir=output_dir,
|
|
49
|
+
model_config=test_model_config,
|
|
50
|
+
text_provider="gemini",
|
|
51
|
+
text_fallback=["none"],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# The self-copy is skipped, not attempted: the in-dir source survives
|
|
55
|
+
# untouched and the pipeline still loaded it.
|
|
56
|
+
assert (output_dir / "source.md").read_text() == source_text
|
|
57
|
+
assert pipeline.source_essay == source_text
|
|
58
|
+
|
|
59
|
+
def test_fresh_run_copies_source_into_output_dir(
|
|
60
|
+
self, tmp_path, source_text, test_model_config
|
|
61
|
+
):
|
|
62
|
+
"""A normal run (source outside the output dir) still copies source.md
|
|
63
|
+
into the output directory."""
|
|
64
|
+
source_file = tmp_path / "essay.md"
|
|
65
|
+
source_file.write_text(source_text)
|
|
66
|
+
pipeline = SlidePipeline(
|
|
67
|
+
source_path=source_file,
|
|
68
|
+
output_dir=tmp_path / "output",
|
|
69
|
+
model_config=test_model_config,
|
|
70
|
+
text_provider="gemini",
|
|
71
|
+
text_fallback=["none"],
|
|
72
|
+
)
|
|
73
|
+
assert (pipeline.output_dir / "source.md").read_text() == source_text
|
|
74
|
+
|
|
75
|
+
|
|
33
76
|
class TestSaveYamlAtomic:
|
|
34
77
|
def test_save_writes_file_and_leaves_no_temp(
|
|
35
78
|
self, tmp_path, source_text, test_model_config
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/noir/cinematic-illustrative-noir.md
RENAMED
|
File without changes
|
{polyptych-0.2.0 → polyptych-0.2.1}/prompts/style-transfer/period-art/attic-vase-pastoral.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|