Commit graph

55 commits

Author SHA1 Message Date
9ace38c806
docs: add hokusai-logo 2026-02-21 11:35:42 +01:00
d22b524182
fix: create parent directories for targets in subfolders
Build targets with subfolder paths (e.g. img/file.jpg) failed because
parent directories did not exist. Create them on demand in
_build_single_target before dispatching to providers.

Also clean up empty subdirectories in the clean command after removing
target files.
2026-02-21 11:21:51 +01:00
8f0e49ee6f
test: add nine-backslash escape test for prompt placeholders 2026-02-20 21:20:30 +01:00
3de3614433
feat: add prompt placeholder substitution with {filename} syntax 2026-02-20 21:20:29 +01:00
760eac5a7b
refactor: use ProjectConfig model for init command YAML output 2026-02-20 21:20:27 +01:00
c1ad6e6e3c
feat: add download target type for fetching files from URLs 2026-02-20 21:20:26 +01:00
a4600df4d5
feat: add init command and --project option to build 2026-02-20 20:45:30 +01:00
d772f5dcc5
feat: accept both .hokusai.yaml and .hokusai.yml config suffixes 2026-02-20 20:33:10 +01:00
1f75c72a96
feat: default to build command when no subcommand is given 2026-02-20 20:29:03 +01:00
4def49350e
chore: rename bulkgen to hokusai
All checks were successful
Continuous Integration / Build Package (push) Successful in 35s
Continuous Integration / Lint, Check & Test (push) Successful in 57s
2026-02-20 17:08:12 +01:00
a28cc97aed
docs: add archive_folder and OpenAI provider to README
All checks were successful
Continuous Integration / Build Package (push) Successful in 1m37s
Continuous Integration / Lint, Check & Test (push) Successful in 2m39s
2026-02-20 16:55:53 +01:00
0294252c2e Merge pull request 'chore(deps): update python docker tag to v3.14' (#2) from renovate/python-3.x into main
All checks were successful
Continuous Integration / Build Package (push) Successful in 34s
Continuous Integration / Lint, Check & Test (push) Successful in 40s
2026-02-18 01:04:15 +01:00
5df5b6e158 chore(deps): update python docker tag to v3.14
All checks were successful
Continuous Integration / Build Package (push) Successful in 2m19s
Continuous Integration / Lint, Check & Test (push) Successful in 2m37s
2026-02-18 00:01:51 +00:00
3bfad87dce
fix: pass all reference images to OpenAI images.edit endpoint
All checks were successful
Continuous Integration / Build Package (push) Successful in 29s
Continuous Integration / Lint, Check & Test (push) Successful in 46s
2026-02-15 15:06:58 +01:00
61f30a8bb1
fix: don't pass response_format to gpt-image-* models
All checks were successful
Continuous Integration / Build Package (push) Successful in 31s
Continuous Integration / Lint, Check & Test (push) Successful in 50s
gpt-image-* models return b64_json by default and reject the
response_format parameter with a 400 error. Only pass it for
DALL-E models which default to url.
2026-02-15 14:47:52 +01:00
d1bbd95c1c
fix: handle long/multiline prompts in _resolve_prompt
All checks were successful
Continuous Integration / Build Package (push) Successful in 31s
Continuous Integration / Lint, Check & Test (push) Successful in 51s
Skip the file-existence check when the prompt contains newlines (can't
be a filename) and catch OSError for prompts that exceed the OS path
length limit.
2026-02-15 14:43:46 +01:00
2aec223c5d
feat: add GPT-5 generation models to OpenAI providers
Text: gpt-5, gpt-5-mini, gpt-5-nano (all with vision), o3, o4-mini
(with vision), o3-pro (text only)
Image: gpt-image-1.5, gpt-image-1-mini (both with reference images)
2026-02-15 14:42:43 +01:00
0ecf1f0f9e
refactor: use project-named state file and store prompt/params directly
All checks were successful
Continuous Integration / Build Package (push) Successful in 48s
Continuous Integration / Lint, Check & Test (push) Successful in 1m1s
- State filename now derives from config: cards.bulkgen.yaml produces
  .cards.bulkgen-state.yaml instead of .bulkgen.state.yaml
- Store resolved prompt text and extra params directly in state file
  instead of hashing them, making state files human-readable
- Only file input contents remain hashed (SHA-256)
- Thread project_name through builder and CLI
- Remove hash_string() and _extra_hash() helpers
- Update .gitignore pattern to .*.bulkgen-state.yaml
2026-02-15 13:56:12 +01:00
870023865d
feat: add OpenAI as provider for text and image generation
- Add openai_text.py: text generation via OpenAI chat completions API
  (gpt-4o, gpt-4o-mini, gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, o3-mini)
- Add openai_image.py: image generation via OpenAI images API
  (gpt-image-1 with reference image support, dall-e-3, dall-e-2)
- Refactor builder provider dispatch from TargetType to model-name index
  to support multiple providers per target type
- Fix circular import between config.py and providers/__init__.py
  using TYPE_CHECKING guard
- Fix stale default model assertions in tests
- Add openai>=1.0.0 dependency
2026-02-15 13:48:06 +01:00
d0dac5b1bf
refactor: move model definitions into providers and extract resolve module
Some checks failed
Continuous Integration / Build Package (push) Successful in 30s
Continuous Integration / Lint, Check & Test (push) Failing after 38s
- Rename ImageProvider to BlackForestProvider, TextProvider to MistralProvider
- Add get_provided_models() abstract method to Provider base class
- Move model lists from models.py into each provider's get_provided_models()
- Add providers/registry.py to aggregate models from all providers
- Extract infer_required_capabilities and resolve_model from config.py to resolve.py
- Update tests to use new names and import paths
2026-02-15 11:03:57 +01:00
dc6a75f5c4
chore: slim down model capabilities
Some checks failed
Continuous Integration / Build Package (push) Successful in 32s
Continuous Integration / Lint, Check & Test (push) Failing after 42s
2026-02-15 10:30:06 +01:00
e7270a118d
feat: replace infer_target_type with capability-based model selection and validation 2026-02-15 10:30:05 +01:00
d15444bdb0
refactor: pass ModelInfo instead of model name string through provider interface 2026-02-15 10:30:04 +01:00
8e3ed7010f
refactor: use StrEnum for model capabilities instead of plain strings 2026-02-15 10:30:03 +01:00
6a80cfb78e
refactor: move models.py into providers package 2026-02-15 10:30:02 +01:00
6a7de0d031 chore: Configure Renovate (#1)
Some checks failed
Continuous Integration / Build Package (push) Successful in 1m54s
Continuous Integration / Lint, Check & Test (push) Failing after 1m58s
Co-authored-by: Renovate Bot <renovate@konstantinfickel.de>
Co-committed-by: Renovate Bot <renovate@konstantinfickel.de>
2026-02-15 07:57:18 +01:00
b536ff9d79
feat: add bulkgen models command listing available models with capabilities
Some checks failed
Continuous Integration / Build Package (push) Successful in 37s
Continuous Integration / Lint, Check & Test (push) Failing after 47s
2026-02-14 23:06:17 +01:00
47b53db760
chore: update default models to pixtral-large-latest and flux-2-pro 2026-02-14 21:27:16 +01:00
ee6c411f3c
feat: add click-based colorized output with progress events and build timer
- Add click as explicit dependency (already bundled with typer)
- Replace typer.echo calls with click.echo + click.style for colorized output
- Add BuildEvent enum and ProgressCallback to builder for decoupled progress reporting
- Remove direct typer dependency from builder module
- Show per-target status with colored labels (skip/ok/fail/...)
- Display elapsed build time in summary
- Colorize graph and clean command output
- Update CLI tests to match new output format
2026-02-14 21:25:38 +01:00
6a9d7efd5d
fix: send images to Mistral as base64 vision chunks instead of placeholders
All checks were successful
Continuous Integration / Build Package (push) Successful in 34s
Continuous Integration / Lint, Check & Test (push) Successful in 53s
The text provider now includes reference_images alongside inputs when
building prompts. Image files are sent as base64 data URLs via
ImageURLChunk for actual multimodal vision support, replacing the
previous [Attached image: ...] placeholder text.
2026-02-14 17:45:39 +01:00
d565329e16
feat: support multiple reference images with model-aware API mapping
All checks were successful
Continuous Integration / Build Package (push) Successful in 31s
Continuous Integration / Lint, Check & Test (push) Successful in 49s
Replace singular reference_image field with reference_images list to
support an arbitrary number of reference images. Map them to the correct
BFL API parameter names based on model family:
- flux-2-*: input_image, input_image_2, ..., input_image_8
- flux-kontext-*: input_image, input_image_2, ..., input_image_4
- flux 1.x: image_prompt (single)

BREAKING CHANGE: reference_image config field renamed to reference_images (list).
2026-02-14 17:19:54 +01:00
b69c38ac13
fix: inline WriteConfig type alias in tests to fix nix flake check
All checks were successful
Continuous Integration / Build Package (push) Successful in 32s
Continuous Integration / Lint, Check & Test (push) Successful in 51s
The tests.conftest import could not be resolved in the nix sandbox
because tests is not a proper package. Define the WriteConfig type
alias directly in test_builder.py and test_graph.py instead.
2026-02-14 16:50:55 +01:00
cf73511876
refactor: replace blackforest package with custom async BFL client
Some checks failed
Continuous Integration / Build Package (push) Successful in 41s
Continuous Integration / Lint, Check & Test (push) Failing after 59s
Implement bulkgen/providers/bfl.py with a fully async httpx-based client
that supports all current and future BFL models (including flux-2-*).
Remove the blackforest dependency and simplify the image provider by
eliminating the asyncio.to_thread wrapper.
2026-02-14 16:44:36 +01:00
fd09d127f2
docs: add home-manager installation instructions to README
Some checks failed
Continuous Integration / Build Package (push) Successful in 35s
Continuous Integration / Lint, Check & Test (push) Failing after 55s
2026-02-14 11:19:15 +01:00
eef9712924
test: add integration tests for all modules
Some checks failed
Continuous Integration / Build Package (push) Successful in 34s
Continuous Integration / Lint, Check & Test (push) Failing after 48s
- Add pytest-asyncio dev dependency and configure asyncio_mode=auto
- Add filterwarnings to suppress third-party PydanticDeprecatedSince20
- Add conftest.py with shared fixtures (project_dir, write_config, etc.)
- Add test_config.py: YAML loading, target type inference, model resolution
- Add test_graph.py: DAG construction, cycle detection, build ordering
- Add test_state.py: hash functions, state persistence, dirty checking
- Add test_builder.py: full build pipeline with FakeProvider, incremental
  builds, selective builds, error isolation, dependency cascading
- Add test_providers.py: ImageProvider and TextProvider with mocked clients
- Add test_cli.py: build/clean/graph commands via typer CliRunner
- All 94 tests pass with 0 basedpyright warnings
2026-02-14 11:07:36 +01:00
452b3c4eb0
docs: add MIT license
All checks were successful
Continuous Integration / Build Package (push) Successful in 2m7s
Continuous Integration / Lint, Check & Test (push) Successful in 2m16s
2026-02-14 10:48:13 +01:00
a662d69559
ci: add forgejo pipeline with nix flake check and build 2026-02-14 10:45:28 +01:00
08952eb70f
feat: add git-hooks.nix pre-commit checks to flake
Add cachix/git-hooks.nix input and wire basedpyright, ruff,
ruff-format, and commitizen hooks into flake checks and devShell.
The basedpyright hook runs against a Nix-built venv so imports
resolve correctly in the sandbox.
2026-02-14 10:42:33 +01:00
a32e0f1b3e
feat: add home-manager module for programs.bulkgen.enable
Expose homeManagerModules.bulkgen and homeManagerModules.default
from the flake. The module provides programs.bulkgen.enable and
programs.bulkgen.package options, adding bulkgen to home.packages
when enabled.
2026-02-14 10:33:33 +01:00
6926d0492d
fix: update default image model from flux-pro to flux-pro-1.1
The flux-pro endpoint returns 403 Forbidden as it has been
deprecated by BlackForestLabs. flux-pro-1.1 is the current
supported version.
2026-02-14 10:25:08 +01:00
c818e6452e
feat: add nix flake with uv2nix packaging and pytest checks 2026-02-13 20:44:12 +01:00
09ff809e9a
chore: add basedpyright and ruff to dev dependencies 2026-02-13 20:29:47 +01:00
7ab25d49cb
refactor: switch to basedpyright, remove pydantic-settings
- Replace pyright with basedpyright in devenv.nix (custom hook)
- Add basedpyright to devenv packages
- Fix all basedpyright warnings: add DiGraph[str] type args, annotate
  class attributes, narrow SyncResponse, handle unused call results,
  suppress unavoidable Any from yaml.safe_load and untyped blackforest
- Replace pydantic-settings[yaml] with direct pyyaml dependency
- Update CLAUDE.md to reflect basedpyright and dependency changes
2026-02-13 20:25:28 +01:00
f71af1cfaf
docs: add CLAUDE.md development guide and README.md user docs
CLAUDE.md covers architecture, data flow, code style conventions,
provider specifics, and all commands needed for development.

README.md covers installation, quick start, full config format
reference, CLI usage, incremental builds, and environment variables.
2026-02-13 20:18:51 +01:00
d38682597c
feat: wire entry point to typer CLI app
Replace stub with import of bulkgen.cli.app
2026-02-13 20:14:37 +01:00
1d98c0010a
feat: add CLI commands (build, clean, graph)
- build: executes all or specific target with dependency resolution
- clean: removes generated artifacts and state file, preserves inputs
- graph: prints dependency graph with build stages
- Config discovery: finds single *.bulkgen.yaml in working directory
2026-02-13 20:14:16 +01:00
bb4b2e2b86
feat: add build orchestrator with incremental and parallel execution
- run_build() drives the full build pipeline
- Topological generations executed concurrently via asyncio.gather()
- Incremental builds: skip clean targets, rebuild dirty ones
- Error isolation: failed targets don't block independent branches
- State saved after each generation for crash resilience
2026-02-13 20:13:33 +01:00
15fdff7763
feat: add Mistral text generation provider
- Uses native async API (chat.complete_async)
- Appends text input file contents to prompt with headers
- Notes image inputs as attached references
- Writes raw LLM response directly to output file
2026-02-13 20:12:45 +01:00
45f590cf11
chore: add pyrightconfig.json pointing to devenv venv
Allows pyright to resolve third-party imports from .devenv/state/venv
2026-02-13 20:10:48 +01:00
aecfc4b82f
feat: add abstract Provider base class
- Defines async generate() interface for generation backends
- Takes target config, resolved prompt/model, and project dir
2026-02-13 20:10:02 +01:00