zer0-image-generator 0.3.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/README.md +18 -0
- data/lib/zer0_image_generator/preview_generator.py +35 -5
- data/lib/zer0_image_generator/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c105d9c5a9aab3c6b772c558fbf070788058e929a4ce846e9cbaa5da019e117d
|
|
4
|
+
data.tar.gz: '0597cbdb685887b2733b29123aee8704366d96b1bca6781395a7bccf760d3748'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e791021d473f1b0b0246973a2e9dab6add20919b5c96b1e2df0806fa12269fd6ea3053ee20b3094dfa1aa365bcdb38ca5ea3e5f315116ff83fd073429aa40487
|
|
7
|
+
data.tar.gz: edd3c2fcad1bbe57e3b87bab8a30b168855865b4ab79a6fc215727d8cbc9a42467da587bde09217798c0eb7bd2b40552db67ca5217ab7de71f7e08bf41a36f0d
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0](https://github.com/bamr87/zer0-image-generator/compare/zer0-image-generator-v0.4.0...zer0-image-generator/v0.5.0) (2026-07-15)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* extract the AI preview-image engine from zer0-mistakes as a portable Jekyll plugin ([58190c9](https://github.com/bamr87/zer0-image-generator/commit/58190c996ccc93ae2bdfee7d3086196eb11ea297))
|
|
14
|
+
* honor Jekyll's top-level `source:` key (zer0-pages layouts) ([baa34df](https://github.com/bamr87/zer0-image-generator/commit/baa34dfa36a836677684e1244dd87953d6f6746d))
|
|
15
|
+
* low-effort Claude orchestration for faster generation ([f037fc9](https://github.com/bamr87/zer0-image-generator/commit/f037fc9f304da937b8575b0da5371f5fc7686189))
|
|
16
|
+
* per-collection preview styles (collection_styles) ([0f14eeb](https://github.com/bamr87/zer0-image-generator/commit/0f14eeb9eda472e2c6540559f2aab32c97c8146b))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* accept repo-root-relative --file paths on `source:` sites ([30d950d](https://github.com/bamr87/zer0-image-generator/commit/30d950d48f5245511e5790671e6b6cfd7af9b588))
|
|
22
|
+
* forward Mercenary options to the engine (symbol hash keys) ([1e91fc6](https://github.com/bamr87/zer0-image-generator/commit/1e91fc6bcd10fff2e278b6c76c25d1cd0b00cf7f))
|
|
23
|
+
|
|
24
|
+
## [0.4.0] - 2026-07-15
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `claude_effort` knob (default `low`): the Claude art-brief and vision-review
|
|
29
|
+
calls now send `output_config: {effort: ...}` — short creative tasks don't
|
|
30
|
+
need deep reasoning, and `low` substantially cuts per-image orchestration
|
|
31
|
+
latency. Configurable per site (`preview_images.claude_effort`); `''`
|
|
32
|
+
disables. Gracefully retries without the parameter on models that reject it.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- README documents the batch-performance contract: one `--collection` run
|
|
37
|
+
parallelizes across files (`--parallel N`, default 4); per-file loops don't.
|
|
38
|
+
|
|
8
39
|
## [0.3.0] - 2026-07-14
|
|
9
40
|
|
|
10
41
|
### Added
|
data/README.md
CHANGED
|
@@ -87,6 +87,9 @@ preview_images:
|
|
|
87
87
|
output_dir: assets/images/previews
|
|
88
88
|
prompt_engine: claude # claude analyzes each article | template
|
|
89
89
|
review_engine: claude # claude vision-reviews each render | none
|
|
90
|
+
claude_effort: low # output_config.effort for the brief/review calls
|
|
91
|
+
# (low = fastest, right-sized for short briefs;
|
|
92
|
+
# high = deepest; '' sends no effort parameter)
|
|
90
93
|
# --- portability knobs (the zer0-isms, now configurable) ---
|
|
91
94
|
collections: [posts] # list of collections, or 'auto' to discover
|
|
92
95
|
# them from Jekyll's own `collections:` map
|
|
@@ -130,6 +133,21 @@ preview_images:
|
|
|
130
133
|
# front_matter_key/preview + /assets prefixing are already the defaults
|
|
131
134
|
```
|
|
132
135
|
|
|
136
|
+
### Batch performance
|
|
137
|
+
|
|
138
|
+
The engine parallelizes **within one invocation** (4 workers by default, `-j N`
|
|
139
|
+
to change). For many files, one collection run beats a loop of single-file
|
|
140
|
+
runs — the loop pays process startup per file and never engages the pool:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bundle exec jekyll preview-images --collection posts --parallel 6 # ✓ parallel
|
|
144
|
+
for f in ...; do jekyll preview-images -f "$f"; done # ✗ serial
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Claude orchestration runs at `claude_effort: low` by default — right-sized for
|
|
148
|
+
a short art brief and a render verdict. Raise it (or set `claude_model`) if you
|
|
149
|
+
want deeper art direction at the cost of latency.
|
|
150
|
+
|
|
133
151
|
### Per-collection styles
|
|
134
152
|
|
|
135
153
|
Give each collection its own visual identity — tutorials render differently
|
|
@@ -128,6 +128,8 @@ DEFAULTS: Dict[str, Any] = {
|
|
|
128
128
|
"prompt_engine": "claude", # claude analyzes the article; falls back to template
|
|
129
129
|
"review_engine": "claude", # claude vision-reviews the render; `none` disables
|
|
130
130
|
"claude_model": "", # empty → DEFAULT_CLAUDE_MODEL
|
|
131
|
+
"claude_effort": "low", # output_config.effort for brief/review calls
|
|
132
|
+
# (short creative tasks; "" sends no effort)
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
# Enhance mode (OpenAI /v1/images/edits — see OpenAIProvider.edit)
|
|
@@ -650,6 +652,7 @@ class Settings:
|
|
|
650
652
|
prompt_engine: str = DEFAULTS["prompt_engine"]
|
|
651
653
|
review_engine: str = DEFAULTS["review_engine"]
|
|
652
654
|
claude_model: str = DEFAULTS["claude_model"]
|
|
655
|
+
claude_effort: str = DEFAULTS["claude_effort"]
|
|
653
656
|
|
|
654
657
|
dry_run: bool = False
|
|
655
658
|
verbose: bool = False
|
|
@@ -752,6 +755,7 @@ def resolve_settings(args: argparse.Namespace, config: Dict[str, Any]) -> Settin
|
|
|
752
755
|
prompt_engine=pick(args.prompt_engine, "PROMPT_ENGINE", "prompt_engine"),
|
|
753
756
|
review_engine=pick(args.review, "REVIEW_ENGINE", "review_engine"),
|
|
754
757
|
claude_model=str(cfg("claude_model", "") or ""),
|
|
758
|
+
claude_effort=str(cfg("claude_effort", DEFAULTS["claude_effort"]) or ""),
|
|
755
759
|
dry_run=args.dry_run or _env_flag("DRY_RUN"),
|
|
756
760
|
verbose=args.verbose or _env_flag("VERBOSE"),
|
|
757
761
|
force=args.force or _env_flag("FORCE"),
|
|
@@ -1120,6 +1124,7 @@ def claude_article_brief(client: "AnthropicClient", cf: ContentFile,
|
|
|
1120
1124
|
text = client.complete(
|
|
1121
1125
|
ART_DIRECTOR_SYSTEM, article,
|
|
1122
1126
|
model=claude_model_for(settings), max_tokens=2048,
|
|
1127
|
+
effort=settings.claude_effort,
|
|
1123
1128
|
).strip()
|
|
1124
1129
|
if text:
|
|
1125
1130
|
debug(f"Claude art-direction brief: {text[:300]}...")
|
|
@@ -1159,6 +1164,7 @@ def claude_review_image(client: "AnthropicClient", image_path: Path,
|
|
|
1159
1164
|
text = client.complete_vision(
|
|
1160
1165
|
REVIEWER_SYSTEM, context, image_path,
|
|
1161
1166
|
model=claude_model_for(settings),
|
|
1167
|
+
effort=settings.claude_effort,
|
|
1162
1168
|
)
|
|
1163
1169
|
data = _extract_json_object(text)
|
|
1164
1170
|
if not data:
|
|
@@ -1496,6 +1502,7 @@ class AnthropicClient:
|
|
|
1496
1502
|
user_text: str,
|
|
1497
1503
|
model: str = DEFAULT_CLAUDE_MODEL,
|
|
1498
1504
|
max_tokens: int = CLAUDE_MAX_TOKENS,
|
|
1505
|
+
effort: str = "",
|
|
1499
1506
|
) -> str:
|
|
1500
1507
|
"""One Messages-API turn (or CLI run); returns concatenated text blocks.
|
|
1501
1508
|
|
|
@@ -1520,6 +1527,8 @@ class AnthropicClient:
|
|
|
1520
1527
|
"system": system_blocks,
|
|
1521
1528
|
"messages": [{"role": "user", "content": user_text}],
|
|
1522
1529
|
}
|
|
1530
|
+
if effort:
|
|
1531
|
+
payload["output_config"] = {"effort": effort}
|
|
1523
1532
|
|
|
1524
1533
|
def call(body: Dict[str, Any]) -> dict:
|
|
1525
1534
|
return with_retries(
|
|
@@ -1530,7 +1539,12 @@ class AnthropicClient:
|
|
|
1530
1539
|
try:
|
|
1531
1540
|
data = call(payload)
|
|
1532
1541
|
except HttpStatusError as exc:
|
|
1533
|
-
if exc.status == 400
|
|
1542
|
+
message = exc.message().lower() if exc.status == 400 else ""
|
|
1543
|
+
if exc.status == 400 and ("output_config" in message or "effort" in message):
|
|
1544
|
+
debug("Retrying without output_config parameter")
|
|
1545
|
+
payload.pop("output_config", None)
|
|
1546
|
+
data = call(payload)
|
|
1547
|
+
elif exc.status == 400 and "thinking" in message:
|
|
1534
1548
|
debug("Retrying without thinking parameter")
|
|
1535
1549
|
payload.pop("thinking", None)
|
|
1536
1550
|
data = call(payload)
|
|
@@ -1564,6 +1578,7 @@ class AnthropicClient:
|
|
|
1564
1578
|
image_path: Path,
|
|
1565
1579
|
model: str = DEFAULT_CLAUDE_MODEL,
|
|
1566
1580
|
max_tokens: int = 2048,
|
|
1581
|
+
effort: str = "",
|
|
1567
1582
|
) -> str:
|
|
1568
1583
|
"""One vision turn over a local PNG (review stage). CLI mode passes the
|
|
1569
1584
|
file path and lets `claude -p` read it; API modes embed base64."""
|
|
@@ -1595,10 +1610,25 @@ class AnthropicClient:
|
|
|
1595
1610
|
],
|
|
1596
1611
|
}],
|
|
1597
1612
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
)
|
|
1613
|
+
if effort:
|
|
1614
|
+
payload["output_config"] = {"effort": effort}
|
|
1615
|
+
|
|
1616
|
+
def call(body: Dict[str, Any]) -> dict:
|
|
1617
|
+
return with_retries(
|
|
1618
|
+
lambda: http_json(ANTHROPIC_API_URL, body, self.headers(), timeout=900),
|
|
1619
|
+
"Anthropic API (review)",
|
|
1620
|
+
)
|
|
1621
|
+
|
|
1622
|
+
try:
|
|
1623
|
+
data = call(payload)
|
|
1624
|
+
except HttpStatusError as exc:
|
|
1625
|
+
message = exc.message().lower() if exc.status == 400 else ""
|
|
1626
|
+
if exc.status == 400 and ("output_config" in message or "effort" in message):
|
|
1627
|
+
debug("Retrying review without output_config parameter")
|
|
1628
|
+
payload.pop("output_config", None)
|
|
1629
|
+
data = call(payload)
|
|
1630
|
+
else:
|
|
1631
|
+
raise
|
|
1602
1632
|
if data.get("stop_reason") == "refusal":
|
|
1603
1633
|
details = data.get("stop_details") or {}
|
|
1604
1634
|
raise ClaudeRefusal(details.get("category") if isinstance(details, dict) else None)
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zer0-image-generator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Amr Abdel
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-07-15 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: jekyll
|
|
@@ -59,6 +60,7 @@ metadata:
|
|
|
59
60
|
changelog_uri: https://github.com/bamr87/zer0-image-generator/blob/main/CHANGELOG.md
|
|
60
61
|
bug_tracker_uri: https://github.com/bamr87/zer0-image-generator/issues
|
|
61
62
|
rubygems_mfa_required: 'true'
|
|
63
|
+
post_install_message:
|
|
62
64
|
rdoc_options: []
|
|
63
65
|
require_paths:
|
|
64
66
|
- lib
|
|
@@ -74,7 +76,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
76
|
version: '0'
|
|
75
77
|
requirements:
|
|
76
78
|
- Python 3.9+ with PyYAML (python3 -m pip install pyyaml)
|
|
77
|
-
rubygems_version:
|
|
79
|
+
rubygems_version: 3.5.22
|
|
80
|
+
signing_key:
|
|
78
81
|
specification_version: 4
|
|
79
82
|
summary: AI preview/social images for any Jekyll site — Claude directs and reviews,
|
|
80
83
|
an image model renders.
|