genblaze-core 0.1.0__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.
Files changed (121) hide show
  1. genblaze_core-0.1.0/.gitignore +76 -0
  2. genblaze_core-0.1.0/PKG-INFO +167 -0
  3. genblaze_core-0.1.0/README.md +130 -0
  4. genblaze_core-0.1.0/genblaze_core/__init__.py +110 -0
  5. genblaze_core-0.1.0/genblaze_core/_utils.py +158 -0
  6. genblaze_core-0.1.0/genblaze_core/_version.py +1 -0
  7. genblaze_core-0.1.0/genblaze_core/agents/__init__.py +20 -0
  8. genblaze_core-0.1.0/genblaze_core/agents/evaluator.py +100 -0
  9. genblaze_core-0.1.0/genblaze_core/agents/loop.py +307 -0
  10. genblaze_core-0.1.0/genblaze_core/builders/__init__.py +6 -0
  11. genblaze_core-0.1.0/genblaze_core/builders/run_builder.py +55 -0
  12. genblaze_core-0.1.0/genblaze_core/builders/step_builder.py +67 -0
  13. genblaze_core-0.1.0/genblaze_core/canonical/__init__.py +5 -0
  14. genblaze_core-0.1.0/genblaze_core/canonical/_normalize.py +59 -0
  15. genblaze_core-0.1.0/genblaze_core/canonical/json.py +23 -0
  16. genblaze_core-0.1.0/genblaze_core/exceptions.py +44 -0
  17. genblaze_core-0.1.0/genblaze_core/media/__init__.py +83 -0
  18. genblaze_core-0.1.0/genblaze_core/media/aac.py +97 -0
  19. genblaze_core-0.1.0/genblaze_core/media/base.py +66 -0
  20. genblaze_core-0.1.0/genblaze_core/media/embedder.py +162 -0
  21. genblaze_core-0.1.0/genblaze_core/media/flac.py +90 -0
  22. genblaze_core-0.1.0/genblaze_core/media/jpeg.py +110 -0
  23. genblaze_core-0.1.0/genblaze_core/media/mp3.py +82 -0
  24. genblaze_core-0.1.0/genblaze_core/media/mp4.py +353 -0
  25. genblaze_core-0.1.0/genblaze_core/media/png.py +71 -0
  26. genblaze_core-0.1.0/genblaze_core/media/sidecar.py +124 -0
  27. genblaze_core-0.1.0/genblaze_core/media/wav.py +173 -0
  28. genblaze_core-0.1.0/genblaze_core/media/webp.py +78 -0
  29. genblaze_core-0.1.0/genblaze_core/models/__init__.py +35 -0
  30. genblaze_core-0.1.0/genblaze_core/models/asset.py +100 -0
  31. genblaze_core-0.1.0/genblaze_core/models/enums.py +59 -0
  32. genblaze_core-0.1.0/genblaze_core/models/manifest.py +300 -0
  33. genblaze_core-0.1.0/genblaze_core/models/policy.py +16 -0
  34. genblaze_core-0.1.0/genblaze_core/models/prompt_template.py +42 -0
  35. genblaze_core-0.1.0/genblaze_core/models/run.py +41 -0
  36. genblaze_core-0.1.0/genblaze_core/models/step.py +70 -0
  37. genblaze_core-0.1.0/genblaze_core/observability/__init__.py +24 -0
  38. genblaze_core-0.1.0/genblaze_core/observability/events.py +94 -0
  39. genblaze_core-0.1.0/genblaze_core/observability/logger.py +59 -0
  40. genblaze_core-0.1.0/genblaze_core/observability/span.py +70 -0
  41. genblaze_core-0.1.0/genblaze_core/observability/tracer.py +297 -0
  42. genblaze_core-0.1.0/genblaze_core/pipeline/__init__.py +7 -0
  43. genblaze_core-0.1.0/genblaze_core/pipeline/cache.py +104 -0
  44. genblaze_core-0.1.0/genblaze_core/pipeline/moderation.py +82 -0
  45. genblaze_core-0.1.0/genblaze_core/pipeline/pipeline.py +1422 -0
  46. genblaze_core-0.1.0/genblaze_core/pipeline/result.py +112 -0
  47. genblaze_core-0.1.0/genblaze_core/pipeline/streaming.py +126 -0
  48. genblaze_core-0.1.0/genblaze_core/pipeline/template.py +176 -0
  49. genblaze_core-0.1.0/genblaze_core/providers/__init__.py +17 -0
  50. genblaze_core-0.1.0/genblaze_core/providers/_ffmpeg_utils.py +112 -0
  51. genblaze_core-0.1.0/genblaze_core/providers/base.py +635 -0
  52. genblaze_core-0.1.0/genblaze_core/providers/compositor.py +147 -0
  53. genblaze_core-0.1.0/genblaze_core/providers/progress.py +32 -0
  54. genblaze_core-0.1.0/genblaze_core/providers/registry.py +32 -0
  55. genblaze_core-0.1.0/genblaze_core/providers/transform.py +376 -0
  56. genblaze_core-0.1.0/genblaze_core/py.typed +0 -0
  57. genblaze_core-0.1.0/genblaze_core/runnable/__init__.py +6 -0
  58. genblaze_core-0.1.0/genblaze_core/runnable/base.py +33 -0
  59. genblaze_core-0.1.0/genblaze_core/runnable/config.py +24 -0
  60. genblaze_core-0.1.0/genblaze_core/sinks/__init__.py +6 -0
  61. genblaze_core-0.1.0/genblaze_core/sinks/base.py +43 -0
  62. genblaze_core-0.1.0/genblaze_core/sinks/parquet.py +190 -0
  63. genblaze_core-0.1.0/genblaze_core/storage/__init__.py +19 -0
  64. genblaze_core-0.1.0/genblaze_core/storage/base.py +164 -0
  65. genblaze_core-0.1.0/genblaze_core/storage/sink.py +262 -0
  66. genblaze_core-0.1.0/genblaze_core/storage/transfer.py +549 -0
  67. genblaze_core-0.1.0/genblaze_core/testing.py +338 -0
  68. genblaze_core-0.1.0/genblaze_core/webhooks/__init__.py +6 -0
  69. genblaze_core-0.1.0/genblaze_core/webhooks/notifier.py +294 -0
  70. genblaze_core-0.1.0/genblaze_core/webhooks/sink.py +49 -0
  71. genblaze_core-0.1.0/pyproject.toml +73 -0
  72. genblaze_core-0.1.0/tests/__init__.py +0 -0
  73. genblaze_core-0.1.0/tests/conftest.py +116 -0
  74. genblaze_core-0.1.0/tests/golden/__init__.py +0 -0
  75. genblaze_core-0.1.0/tests/golden/test_canonical_hash_stability.py +71 -0
  76. genblaze_core-0.1.0/tests/golden/test_png_roundtrip.py +58 -0
  77. genblaze_core-0.1.0/tests/integration/__init__.py +0 -0
  78. genblaze_core-0.1.0/tests/integration/test_pipeline_chain_integration.py +193 -0
  79. genblaze_core-0.1.0/tests/integration/test_pipeline_embed_roundtrip.py +68 -0
  80. genblaze_core-0.1.0/tests/unit/__init__.py +0 -0
  81. genblaze_core-0.1.0/tests/unit/test_aac_handler.py +71 -0
  82. genblaze_core-0.1.0/tests/unit/test_agent_loop.py +282 -0
  83. genblaze_core-0.1.0/tests/unit/test_builders.py +68 -0
  84. genblaze_core-0.1.0/tests/unit/test_canonical.py +67 -0
  85. genblaze_core-0.1.0/tests/unit/test_canonical_properties.py +69 -0
  86. genblaze_core-0.1.0/tests/unit/test_compositor.py +296 -0
  87. genblaze_core-0.1.0/tests/unit/test_embedder.py +143 -0
  88. genblaze_core-0.1.0/tests/unit/test_ffmpeg_utils.py +123 -0
  89. genblaze_core-0.1.0/tests/unit/test_flac_handler.py +87 -0
  90. genblaze_core-0.1.0/tests/unit/test_jpeg.py +64 -0
  91. genblaze_core-0.1.0/tests/unit/test_mock_providers.py +216 -0
  92. genblaze_core-0.1.0/tests/unit/test_models.py +425 -0
  93. genblaze_core-0.1.0/tests/unit/test_moderation.py +318 -0
  94. genblaze_core-0.1.0/tests/unit/test_mp3.py +51 -0
  95. genblaze_core-0.1.0/tests/unit/test_mp4.py +238 -0
  96. genblaze_core-0.1.0/tests/unit/test_object_storage_sink.py +849 -0
  97. genblaze_core-0.1.0/tests/unit/test_observability.py +101 -0
  98. genblaze_core-0.1.0/tests/unit/test_parquet.py +101 -0
  99. genblaze_core-0.1.0/tests/unit/test_pipeline.py +1593 -0
  100. genblaze_core-0.1.0/tests/unit/test_pipeline_template.py +266 -0
  101. genblaze_core-0.1.0/tests/unit/test_png.py +45 -0
  102. genblaze_core-0.1.0/tests/unit/test_policy.py +104 -0
  103. genblaze_core-0.1.0/tests/unit/test_progress.py +46 -0
  104. genblaze_core-0.1.0/tests/unit/test_prompt_template.py +173 -0
  105. genblaze_core-0.1.0/tests/unit/test_provider_registry.py +70 -0
  106. genblaze_core-0.1.0/tests/unit/test_provider_retry.py +343 -0
  107. genblaze_core-0.1.0/tests/unit/test_runnable.py +21 -0
  108. genblaze_core-0.1.0/tests/unit/test_schema_compliance.py +74 -0
  109. genblaze_core-0.1.0/tests/unit/test_secret_redaction.py +82 -0
  110. genblaze_core-0.1.0/tests/unit/test_sidecar.py +92 -0
  111. genblaze_core-0.1.0/tests/unit/test_storage_backend.py +109 -0
  112. genblaze_core-0.1.0/tests/unit/test_streaming.py +361 -0
  113. genblaze_core-0.1.0/tests/unit/test_sync_provider.py +349 -0
  114. genblaze_core-0.1.0/tests/unit/test_tracer.py +245 -0
  115. genblaze_core-0.1.0/tests/unit/test_transfer.py +762 -0
  116. genblaze_core-0.1.0/tests/unit/test_transform.py +295 -0
  117. genblaze_core-0.1.0/tests/unit/test_unicode.py +30 -0
  118. genblaze_core-0.1.0/tests/unit/test_utils.py +135 -0
  119. genblaze_core-0.1.0/tests/unit/test_wav.py +59 -0
  120. genblaze_core-0.1.0/tests/unit/test_webhook.py +287 -0
  121. genblaze_core-0.1.0/tests/unit/test_webp.py +64 -0
@@ -0,0 +1,76 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ dist/
9
+ build/
10
+ *.egg-info/
11
+ *.egg
12
+ wheels/
13
+ /MANIFEST
14
+
15
+ # Virtual environments
16
+ .venv/
17
+ venv/
18
+ env/
19
+ ENV/
20
+
21
+ # IDE / editors
22
+ .vscode/
23
+ .idea/
24
+ *.swp
25
+ *.swo
26
+ *~
27
+ .project
28
+ .settings/
29
+
30
+ # Testing / coverage
31
+ .pytest_cache/
32
+ .coverage
33
+ .coverage.*
34
+ htmlcov/
35
+ coverage.xml
36
+ *.cover
37
+
38
+ # Type checking
39
+ .mypy_cache/
40
+ .pytype/
41
+ .pyre/
42
+
43
+ # Linting
44
+ .ruff_cache/
45
+
46
+ # OS files
47
+ .DS_Store
48
+ Thumbs.db
49
+ ehthumbs.db
50
+
51
+ # Environment / secrets
52
+ .env
53
+ .env.*
54
+ !.env.example
55
+ *.pem
56
+ *.key
57
+ credentials.json
58
+
59
+ # Jupyter
60
+ .ipynb_checkpoints/
61
+
62
+ # MkDocs build output
63
+ site/
64
+
65
+ # Claude Code — local/ephemeral
66
+ .claude/settings.local.json
67
+ .claude/worktrees/
68
+ CLAUDE.local.md
69
+
70
+ # Hypothesis test cache
71
+ .hypothesis/
72
+
73
+ # Temp files
74
+ *.tmp
75
+ *.bak
76
+ *.log
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: genblaze-core
3
+ Version: 0.1.0
4
+ Summary: Core SDK for genblaze media generation orchestration
5
+ Project-URL: Documentation, https://github.com/backblaze-labs/genblaze
6
+ Project-URL: Repository, https://github.com/backblaze-labs/genblaze
7
+ Project-URL: Changelog, https://github.com/backblaze-labs/genblaze/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/backblaze-labs/genblaze/issues
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Multimedia
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: pillow>=10.0
21
+ Requires-Dist: pydantic<3,>=2.0
22
+ Provides-Extra: audio
23
+ Requires-Dist: mutagen>=1.47; extra == 'audio'
24
+ Provides-Extra: dev
25
+ Requires-Dist: hypothesis>=6.0; extra == 'dev'
26
+ Requires-Dist: jsonschema>=4.0; extra == 'dev'
27
+ Requires-Dist: mutagen>=1.47; extra == 'dev'
28
+ Requires-Dist: mypy>=1.8; extra == 'dev'
29
+ Requires-Dist: pyarrow>=14.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
32
+ Requires-Dist: pytest>=7.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.4; extra == 'dev'
34
+ Provides-Extra: parquet
35
+ Requires-Dist: pyarrow>=14.0; extra == 'parquet'
36
+ Description-Content-Type: text/markdown
37
+
38
+ <!-- last_verified: 2026-04-22 -->
39
+ # genblaze-core
40
+
41
+ **Python SDK for building generative AI pipelines across video, image, and audio — with built-in SHA-256 provenance.**
42
+
43
+ `genblaze-core` is the core of [genblaze](https://github.com/backblaze-labs/genblaze), an open-source orchestration framework by [Backblaze](https://www.backblaze.com/cloud-storage?utm_source=github&utm_medium=referral&utm_campaign=ai_artifacts&utm_content=genblaze) for composing multi-step AI media generation workflows. It gives you a single, provider-agnostic `Pipeline` API for text-to-video, text-to-image, text-to-speech, image-to-video, and audio generation — so you can swap models (Sora, Veo, Runway, Luma, Flux, DALL·E, ElevenLabs, Stable Audio, LMNT, GMICloud) without rewriting pipeline logic.
44
+
45
+ Every pipeline run emits a canonical, hash-verified **provenance manifest** — a tamper-evident JSON document capturing the provider, model, prompt, parameters, timestamps, and SHA-256 hash of every generated asset. Manifests can be embedded directly into PNG, JPEG, WebP, MP4, MP3, and WAV files, uploaded alongside assets to S3-compatible storage, or exported to Parquet for analytics.
46
+
47
+ ## Why genblaze-core
48
+
49
+ - **One API for every generative AI provider** — Pipelines, not per-vendor SDK glue. Fluent, composable, chainable.
50
+ - **Built-in provenance** — Every asset gets a SHA-256–verified manifest. Prove how media was made; detect tampering.
51
+ - **Production-ready** — Retries, timeouts, progress streaming, moderation hooks, OpenTelemetry tracing, step caching.
52
+ - **Storage-agnostic sinks** — Drop into Backblaze B2, AWS S3, Cloudflare R2, MinIO, Parquet, or local disk.
53
+ - **Policy + privacy controls** — Redact prompts, strip params, pointer-mode for sensitive content.
54
+ - **Agent loops + templates** — Evaluator-driven iteration, reusable pipeline and step templates.
55
+ - **Zero lock-in** — MIT licensed, typed, lazy imports, provider adapters are separate packages.
56
+
57
+ ## Features
58
+
59
+ | Capability | What you get |
60
+ |---|---|
61
+ | `Pipeline` API | Fluent multi-step generation, fan-in (`input_from`), AV compositing via FFmpeg |
62
+ | Provider discovery | Entry-point–based registry — `pip install genblaze-<provider>` and it's available |
63
+ | Manifest (Pydantic) | `Run`, `Step`, `Asset` models with canonical JSON hashing and `.verify()` |
64
+ | Media embedding | `PngHandler`, `Mp4Handler`, `Mp3Handler`, etc. — embed + extract manifests in-file |
65
+ | Storage sink | `ObjectStorageSink` with hierarchical or content-addressable key layout |
66
+ | Parquet sink | Partitioned run/step/asset tables for downstream analytics |
67
+ | Observability | `OTelTracer`, `LoggingTracer`, `CompositeTracer`, structured events |
68
+ | Agents | `AgentLoop` with pluggable `Evaluator` for iterative refinement |
69
+ | Moderation | Pre/post moderation hooks, configurable embed policies |
70
+ | Testing | `MockProvider`, `MockVideoProvider`, `MockAudioProvider` for offline tests |
71
+
72
+ ## Install
73
+
74
+ ```bash
75
+ pip install genblaze-core
76
+ ```
77
+
78
+ Optional extras:
79
+
80
+ ```bash
81
+ pip install "genblaze-core[parquet]" # ParquetSink for analytics
82
+ pip install "genblaze-core[audio]" # Audio metadata embedding (mutagen)
83
+ ```
84
+
85
+ Add provider adapters separately:
86
+
87
+ ```bash
88
+ pip install genblaze-openai genblaze-google genblaze-runway genblaze-luma \
89
+ genblaze-decart genblaze-replicate genblaze-elevenlabs \
90
+ genblaze-stability-audio genblaze-lmnt genblaze-gmicloud
91
+
92
+ pip install genblaze-s3 # Storage backend for Backblaze B2 / AWS S3 / R2 / MinIO
93
+ pip install genblaze-cli # Extract / verify / replay / index manifests
94
+ ```
95
+
96
+ ## Quickstart — local, zero API keys
97
+
98
+ ```python
99
+ from genblaze_core import Modality, Pipeline
100
+ from genblaze_core.testing import MockVideoProvider
101
+
102
+ run, manifest = (
103
+ Pipeline("hello-genblaze")
104
+ .step(MockVideoProvider(), model="mock-v1",
105
+ prompt="A drone shot over a coastal city at golden hour",
106
+ modality=Modality.VIDEO)
107
+ .run()
108
+ )
109
+
110
+ print(manifest.canonical_hash) # deterministic SHA-256 of the run
111
+ print(manifest.verify()) # True
112
+ ```
113
+
114
+ ## Quickstart — Sora + Backblaze B2 storage
115
+
116
+ Generate a video, upload it + its manifest to [Backblaze B2](https://www.backblaze.com/cloud-storage?utm_source=github&utm_medium=referral&utm_campaign=ai_artifacts&utm_content=genblaze), verify the hash:
117
+
118
+ ```bash
119
+ pip install genblaze-core genblaze-openai genblaze-s3
120
+ export OPENAI_API_KEY="sk-..."
121
+ export B2_KEY_ID="..."
122
+ export B2_APP_KEY="..."
123
+ ```
124
+
125
+ ```python
126
+ from genblaze_core import KeyStrategy, Modality, ObjectStorageSink, Pipeline
127
+ from genblaze_openai import SoraProvider
128
+ from genblaze_s3 import S3StorageBackend
129
+
130
+ storage = ObjectStorageSink(
131
+ S3StorageBackend.for_backblaze("my-bucket"),
132
+ key_strategy=KeyStrategy.HIERARCHICAL,
133
+ )
134
+
135
+ result = (
136
+ Pipeline("hero-reel")
137
+ .step(SoraProvider(), model="sora-2",
138
+ prompt="Aerial flyover of a mountain lake at sunrise",
139
+ modality=Modality.VIDEO, seconds=4, size="1280x720")
140
+ .run(sink=storage, timeout=300)
141
+ )
142
+
143
+ print(result.run.steps[0].assets[0].url) # durable B2 URL
144
+ print(result.manifest.canonical_hash) # SHA-256 of the full run
145
+ assert result.manifest.verify()
146
+ ```
147
+
148
+ ## Storage — Backblaze B2 recommended
149
+
150
+ [Backblaze B2](https://www.backblaze.com/cloud-storage?utm_source=github&utm_medium=referral&utm_campaign=ai_artifacts&utm_content=genblaze) is the recommended default sink for genblaze — purpose-built for large AI-generated media with S3-compatible APIs, resilient multipart uploads, Object Lock for immutable manifests, and strong cost economics at scale. One-liner credentials from `B2_KEY_ID` / `B2_APP_KEY`. See the [`genblaze-s3`](https://pypi.org/project/genblaze-s3/) backend for the full recipe plus AWS S3, Cloudflare R2, and MinIO variants.
151
+
152
+ ## Documentation
153
+
154
+ - **Main repo**: https://github.com/backblaze-labs/genblaze
155
+ - **Architecture**: https://github.com/backblaze-labs/genblaze/blob/main/ARCHITECTURE.md
156
+ - **Feature docs**: https://github.com/backblaze-labs/genblaze/tree/main/docs/features
157
+ - **Runnable examples**: https://github.com/backblaze-labs/genblaze/tree/main/examples
158
+
159
+ ## Related packages
160
+
161
+ Provider adapters: [`genblaze-openai`](https://pypi.org/project/genblaze-openai/) · [`genblaze-google`](https://pypi.org/project/genblaze-google/) · [`genblaze-runway`](https://pypi.org/project/genblaze-runway/) · [`genblaze-luma`](https://pypi.org/project/genblaze-luma/) · [`genblaze-decart`](https://pypi.org/project/genblaze-decart/) · [`genblaze-replicate`](https://pypi.org/project/genblaze-replicate/) · [`genblaze-elevenlabs`](https://pypi.org/project/genblaze-elevenlabs/) · [`genblaze-stability-audio`](https://pypi.org/project/genblaze-stability-audio/) · [`genblaze-lmnt`](https://pypi.org/project/genblaze-lmnt/) · [`genblaze-gmicloud`](https://pypi.org/project/genblaze-gmicloud/)
162
+
163
+ Storage + tooling: [`genblaze-s3`](https://pypi.org/project/genblaze-s3/) · [`genblaze-cli`](https://pypi.org/project/genblaze-cli/) · [`genblaze-langsmith`](https://pypi.org/project/genblaze-langsmith/)
164
+
165
+ ## License
166
+
167
+ MIT
@@ -0,0 +1,130 @@
1
+ <!-- last_verified: 2026-04-22 -->
2
+ # genblaze-core
3
+
4
+ **Python SDK for building generative AI pipelines across video, image, and audio — with built-in SHA-256 provenance.**
5
+
6
+ `genblaze-core` is the core of [genblaze](https://github.com/backblaze-labs/genblaze), an open-source orchestration framework by [Backblaze](https://www.backblaze.com/cloud-storage?utm_source=github&utm_medium=referral&utm_campaign=ai_artifacts&utm_content=genblaze) for composing multi-step AI media generation workflows. It gives you a single, provider-agnostic `Pipeline` API for text-to-video, text-to-image, text-to-speech, image-to-video, and audio generation — so you can swap models (Sora, Veo, Runway, Luma, Flux, DALL·E, ElevenLabs, Stable Audio, LMNT, GMICloud) without rewriting pipeline logic.
7
+
8
+ Every pipeline run emits a canonical, hash-verified **provenance manifest** — a tamper-evident JSON document capturing the provider, model, prompt, parameters, timestamps, and SHA-256 hash of every generated asset. Manifests can be embedded directly into PNG, JPEG, WebP, MP4, MP3, and WAV files, uploaded alongside assets to S3-compatible storage, or exported to Parquet for analytics.
9
+
10
+ ## Why genblaze-core
11
+
12
+ - **One API for every generative AI provider** — Pipelines, not per-vendor SDK glue. Fluent, composable, chainable.
13
+ - **Built-in provenance** — Every asset gets a SHA-256–verified manifest. Prove how media was made; detect tampering.
14
+ - **Production-ready** — Retries, timeouts, progress streaming, moderation hooks, OpenTelemetry tracing, step caching.
15
+ - **Storage-agnostic sinks** — Drop into Backblaze B2, AWS S3, Cloudflare R2, MinIO, Parquet, or local disk.
16
+ - **Policy + privacy controls** — Redact prompts, strip params, pointer-mode for sensitive content.
17
+ - **Agent loops + templates** — Evaluator-driven iteration, reusable pipeline and step templates.
18
+ - **Zero lock-in** — MIT licensed, typed, lazy imports, provider adapters are separate packages.
19
+
20
+ ## Features
21
+
22
+ | Capability | What you get |
23
+ |---|---|
24
+ | `Pipeline` API | Fluent multi-step generation, fan-in (`input_from`), AV compositing via FFmpeg |
25
+ | Provider discovery | Entry-point–based registry — `pip install genblaze-<provider>` and it's available |
26
+ | Manifest (Pydantic) | `Run`, `Step`, `Asset` models with canonical JSON hashing and `.verify()` |
27
+ | Media embedding | `PngHandler`, `Mp4Handler`, `Mp3Handler`, etc. — embed + extract manifests in-file |
28
+ | Storage sink | `ObjectStorageSink` with hierarchical or content-addressable key layout |
29
+ | Parquet sink | Partitioned run/step/asset tables for downstream analytics |
30
+ | Observability | `OTelTracer`, `LoggingTracer`, `CompositeTracer`, structured events |
31
+ | Agents | `AgentLoop` with pluggable `Evaluator` for iterative refinement |
32
+ | Moderation | Pre/post moderation hooks, configurable embed policies |
33
+ | Testing | `MockProvider`, `MockVideoProvider`, `MockAudioProvider` for offline tests |
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install genblaze-core
39
+ ```
40
+
41
+ Optional extras:
42
+
43
+ ```bash
44
+ pip install "genblaze-core[parquet]" # ParquetSink for analytics
45
+ pip install "genblaze-core[audio]" # Audio metadata embedding (mutagen)
46
+ ```
47
+
48
+ Add provider adapters separately:
49
+
50
+ ```bash
51
+ pip install genblaze-openai genblaze-google genblaze-runway genblaze-luma \
52
+ genblaze-decart genblaze-replicate genblaze-elevenlabs \
53
+ genblaze-stability-audio genblaze-lmnt genblaze-gmicloud
54
+
55
+ pip install genblaze-s3 # Storage backend for Backblaze B2 / AWS S3 / R2 / MinIO
56
+ pip install genblaze-cli # Extract / verify / replay / index manifests
57
+ ```
58
+
59
+ ## Quickstart — local, zero API keys
60
+
61
+ ```python
62
+ from genblaze_core import Modality, Pipeline
63
+ from genblaze_core.testing import MockVideoProvider
64
+
65
+ run, manifest = (
66
+ Pipeline("hello-genblaze")
67
+ .step(MockVideoProvider(), model="mock-v1",
68
+ prompt="A drone shot over a coastal city at golden hour",
69
+ modality=Modality.VIDEO)
70
+ .run()
71
+ )
72
+
73
+ print(manifest.canonical_hash) # deterministic SHA-256 of the run
74
+ print(manifest.verify()) # True
75
+ ```
76
+
77
+ ## Quickstart — Sora + Backblaze B2 storage
78
+
79
+ Generate a video, upload it + its manifest to [Backblaze B2](https://www.backblaze.com/cloud-storage?utm_source=github&utm_medium=referral&utm_campaign=ai_artifacts&utm_content=genblaze), verify the hash:
80
+
81
+ ```bash
82
+ pip install genblaze-core genblaze-openai genblaze-s3
83
+ export OPENAI_API_KEY="sk-..."
84
+ export B2_KEY_ID="..."
85
+ export B2_APP_KEY="..."
86
+ ```
87
+
88
+ ```python
89
+ from genblaze_core import KeyStrategy, Modality, ObjectStorageSink, Pipeline
90
+ from genblaze_openai import SoraProvider
91
+ from genblaze_s3 import S3StorageBackend
92
+
93
+ storage = ObjectStorageSink(
94
+ S3StorageBackend.for_backblaze("my-bucket"),
95
+ key_strategy=KeyStrategy.HIERARCHICAL,
96
+ )
97
+
98
+ result = (
99
+ Pipeline("hero-reel")
100
+ .step(SoraProvider(), model="sora-2",
101
+ prompt="Aerial flyover of a mountain lake at sunrise",
102
+ modality=Modality.VIDEO, seconds=4, size="1280x720")
103
+ .run(sink=storage, timeout=300)
104
+ )
105
+
106
+ print(result.run.steps[0].assets[0].url) # durable B2 URL
107
+ print(result.manifest.canonical_hash) # SHA-256 of the full run
108
+ assert result.manifest.verify()
109
+ ```
110
+
111
+ ## Storage — Backblaze B2 recommended
112
+
113
+ [Backblaze B2](https://www.backblaze.com/cloud-storage?utm_source=github&utm_medium=referral&utm_campaign=ai_artifacts&utm_content=genblaze) is the recommended default sink for genblaze — purpose-built for large AI-generated media with S3-compatible APIs, resilient multipart uploads, Object Lock for immutable manifests, and strong cost economics at scale. One-liner credentials from `B2_KEY_ID` / `B2_APP_KEY`. See the [`genblaze-s3`](https://pypi.org/project/genblaze-s3/) backend for the full recipe plus AWS S3, Cloudflare R2, and MinIO variants.
114
+
115
+ ## Documentation
116
+
117
+ - **Main repo**: https://github.com/backblaze-labs/genblaze
118
+ - **Architecture**: https://github.com/backblaze-labs/genblaze/blob/main/ARCHITECTURE.md
119
+ - **Feature docs**: https://github.com/backblaze-labs/genblaze/tree/main/docs/features
120
+ - **Runnable examples**: https://github.com/backblaze-labs/genblaze/tree/main/examples
121
+
122
+ ## Related packages
123
+
124
+ Provider adapters: [`genblaze-openai`](https://pypi.org/project/genblaze-openai/) · [`genblaze-google`](https://pypi.org/project/genblaze-google/) · [`genblaze-runway`](https://pypi.org/project/genblaze-runway/) · [`genblaze-luma`](https://pypi.org/project/genblaze-luma/) · [`genblaze-decart`](https://pypi.org/project/genblaze-decart/) · [`genblaze-replicate`](https://pypi.org/project/genblaze-replicate/) · [`genblaze-elevenlabs`](https://pypi.org/project/genblaze-elevenlabs/) · [`genblaze-stability-audio`](https://pypi.org/project/genblaze-stability-audio/) · [`genblaze-lmnt`](https://pypi.org/project/genblaze-lmnt/) · [`genblaze-gmicloud`](https://pypi.org/project/genblaze-gmicloud/)
125
+
126
+ Storage + tooling: [`genblaze-s3`](https://pypi.org/project/genblaze-s3/) · [`genblaze-cli`](https://pypi.org/project/genblaze-cli/) · [`genblaze-langsmith`](https://pypi.org/project/genblaze-langsmith/)
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1,110 @@
1
+ """genblaze-core — orchestration framework for media generation."""
2
+
3
+ from genblaze_core._version import __version__
4
+
5
+ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
6
+ # pipeline (primary entry point)
7
+ "Pipeline": ("genblaze_core.pipeline.pipeline", "Pipeline"),
8
+ "PipelineResult": ("genblaze_core.pipeline.result", "PipelineResult"),
9
+ "StepCompleteEvent": ("genblaze_core.pipeline.result", "StepCompleteEvent"),
10
+ # pipeline cache
11
+ "StepCache": ("genblaze_core.pipeline.cache", "StepCache"),
12
+ # pipeline templates
13
+ "PipelineTemplate": ("genblaze_core.pipeline.template", "PipelineTemplate"),
14
+ "StepTemplate": ("genblaze_core.pipeline.template", "StepTemplate"),
15
+ # moderation
16
+ "ModerationHook": ("genblaze_core.pipeline.moderation", "ModerationHook"),
17
+ "ModerationResult": ("genblaze_core.pipeline.moderation", "ModerationResult"),
18
+ # builders
19
+ "RunBuilder": ("genblaze_core.builders.run_builder", "RunBuilder"),
20
+ "StepBuilder": ("genblaze_core.builders.step_builder", "StepBuilder"),
21
+ # providers
22
+ "BaseProvider": ("genblaze_core.providers.base", "BaseProvider"),
23
+ "ProviderCapabilities": ("genblaze_core.providers.base", "ProviderCapabilities"),
24
+ "SubmitResult": ("genblaze_core.providers.base", "SubmitResult"),
25
+ "SyncProvider": ("genblaze_core.providers.base", "SyncProvider"),
26
+ "validate_asset_url": ("genblaze_core.providers.base", "validate_asset_url"),
27
+ "validate_chain_input_url": ("genblaze_core.providers.base", "validate_chain_input_url"),
28
+ "FFmpegCompositor": ("genblaze_core.providers.compositor", "FFmpegCompositor"),
29
+ "FFmpegTransform": ("genblaze_core.providers.transform", "FFmpegTransform"),
30
+ "ProgressEvent": ("genblaze_core.providers.progress", "ProgressEvent"),
31
+ # observability
32
+ "StreamEvent": ("genblaze_core.observability.events", "StreamEvent"),
33
+ "Tracer": ("genblaze_core.observability.tracer", "Tracer"),
34
+ "NoOpTracer": ("genblaze_core.observability.tracer", "NoOpTracer"),
35
+ "LoggingTracer": ("genblaze_core.observability.tracer", "LoggingTracer"),
36
+ "OTelTracer": ("genblaze_core.observability.tracer", "OTelTracer"),
37
+ "CompositeTracer": ("genblaze_core.observability.tracer", "CompositeTracer"),
38
+ "StructuredLogger": ("genblaze_core.observability.logger", "StructuredLogger"),
39
+ # agents
40
+ "AgentLoop": ("genblaze_core.agents.loop", "AgentLoop"),
41
+ "AgentContext": ("genblaze_core.agents.loop", "AgentContext"),
42
+ "AgentIteration": ("genblaze_core.agents.loop", "AgentIteration"),
43
+ "AgentResult": ("genblaze_core.agents.loop", "AgentResult"),
44
+ "Evaluator": ("genblaze_core.agents.evaluator", "Evaluator"),
45
+ "EvaluationResult": ("genblaze_core.agents.evaluator", "EvaluationResult"),
46
+ "CallableEvaluator": ("genblaze_core.agents.evaluator", "CallableEvaluator"),
47
+ "ThresholdEvaluator": ("genblaze_core.agents.evaluator", "ThresholdEvaluator"),
48
+ # models
49
+ "Manifest": ("genblaze_core.models.manifest", "Manifest"),
50
+ "Run": ("genblaze_core.models.run", "Run"),
51
+ "Step": ("genblaze_core.models.step", "Step"),
52
+ "Asset": ("genblaze_core.models.asset", "Asset"),
53
+ "AudioMetadata": ("genblaze_core.models.asset", "AudioMetadata"),
54
+ "Track": ("genblaze_core.models.asset", "Track"),
55
+ "VideoMetadata": ("genblaze_core.models.asset", "VideoMetadata"),
56
+ "WordTiming": ("genblaze_core.models.asset", "WordTiming"),
57
+ # enums
58
+ "Modality": ("genblaze_core.models.enums", "Modality"),
59
+ "StepType": ("genblaze_core.models.enums", "StepType"),
60
+ "RunStatus": ("genblaze_core.models.enums", "RunStatus"),
61
+ "StepStatus": ("genblaze_core.models.enums", "StepStatus"),
62
+ "PromptVisibility": ("genblaze_core.models.enums", "PromptVisibility"),
63
+ "ProviderErrorCode": ("genblaze_core.models.enums", "ProviderErrorCode"),
64
+ "EmbedPolicy": ("genblaze_core.models.policy", "EmbedPolicy"),
65
+ "PromptTemplate": ("genblaze_core.models.prompt_template", "PromptTemplate"),
66
+ # provider discovery
67
+ "discover_providers": ("genblaze_core.providers.registry", "discover_providers"),
68
+ # sinks
69
+ "BaseSink": ("genblaze_core.sinks.base", "BaseSink"),
70
+ "ParquetSink": ("genblaze_core.sinks.parquet", "ParquetSink"),
71
+ # storage
72
+ "StorageBackend": ("genblaze_core.storage.base", "StorageBackend"),
73
+ "ObjectStorageSink": ("genblaze_core.storage.sink", "ObjectStorageSink"),
74
+ "AssetTransfer": ("genblaze_core.storage.transfer", "AssetTransfer"),
75
+ "KeyStrategy": ("genblaze_core.storage.base", "KeyStrategy"),
76
+ "ObjectLockConfig": ("genblaze_core.storage.base", "ObjectLockConfig"),
77
+ # testing
78
+ "MockProvider": ("genblaze_core.testing", "MockProvider"),
79
+ "MockVideoProvider": ("genblaze_core.testing", "MockVideoProvider"),
80
+ "MockAudioProvider": ("genblaze_core.testing", "MockAudioProvider"),
81
+ "ProviderComplianceTests": ("genblaze_core.testing", "ProviderComplianceTests"),
82
+ # exceptions
83
+ "GenblazeError": ("genblaze_core.exceptions", "GenblazeError"),
84
+ "PipelineTimeoutError": ("genblaze_core.exceptions", "PipelineTimeoutError"),
85
+ "EmbeddingError": ("genblaze_core.exceptions", "EmbeddingError"),
86
+ "StorageError": ("genblaze_core.exceptions", "StorageError"),
87
+ "ProviderError": ("genblaze_core.exceptions", "ProviderError"),
88
+ "ManifestError": ("genblaze_core.exceptions", "ManifestError"),
89
+ "SinkError": ("genblaze_core.exceptions", "SinkError"),
90
+ "WebhookError": ("genblaze_core.exceptions", "WebhookError"),
91
+ # webhooks
92
+ "WebhookNotifier": ("genblaze_core.webhooks.notifier", "WebhookNotifier"),
93
+ "WebhookConfig": ("genblaze_core.webhooks.notifier", "WebhookConfig"),
94
+ "WebhookEvent": ("genblaze_core.webhooks.notifier", "WebhookEvent"),
95
+ "WebhookSink": ("genblaze_core.webhooks.sink", "WebhookSink"),
96
+ }
97
+
98
+ __all__ = [*_LAZY_IMPORTS.keys(), "__version__"]
99
+
100
+
101
+ def __getattr__(name: str):
102
+ if name in _LAZY_IMPORTS:
103
+ module_path, attr = _LAZY_IMPORTS[name]
104
+ import importlib
105
+
106
+ mod = importlib.import_module(module_path)
107
+ val = getattr(mod, attr)
108
+ globals()[name] = val
109
+ return val
110
+ raise AttributeError(f"module 'genblaze_core' has no attribute {name!r}")
@@ -0,0 +1,158 @@
1
+ """Internal utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import hashlib
7
+ import ipaddress
8
+ import random
9
+ import re
10
+ import socket
11
+ import tempfile
12
+ import uuid
13
+ from collections.abc import Coroutine
14
+ from concurrent.futures import ThreadPoolExecutor
15
+ from datetime import UTC, datetime
16
+ from pathlib import Path
17
+ from typing import Any
18
+ from urllib.parse import urlparse as _urlparse
19
+
20
+
21
+ def new_id() -> str:
22
+ """Generate a new UUID4 string."""
23
+ return str(uuid.uuid4())
24
+
25
+
26
+ def utc_now() -> datetime:
27
+ """Return current UTC datetime."""
28
+ return datetime.now(UTC)
29
+
30
+
31
+ def compute_sha256(data: bytes) -> str:
32
+ """Compute SHA-256 hex digest of raw bytes."""
33
+ return hashlib.sha256(data).hexdigest()
34
+
35
+
36
+ def _run_async(coro: Coroutine) -> Any:
37
+ """Run an async coroutine from sync code safely.
38
+
39
+ If there's already a running event loop (e.g. inside Jupyter or an async provider
40
+ called from BaseProvider.invoke), runs in a new thread to avoid RuntimeError.
41
+ Otherwise uses asyncio.run().
42
+ """
43
+ try:
44
+ loop = asyncio.get_running_loop()
45
+ except RuntimeError:
46
+ loop = None
47
+
48
+ if loop is not None and loop.is_running():
49
+ # Already inside an event loop — run in a separate thread
50
+ with ThreadPoolExecutor(max_workers=1) as pool:
51
+ return pool.submit(asyncio.run, coro).result()
52
+ return asyncio.run(coro)
53
+
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # SSRF protection — shared by storage/transfer.py and webhooks/notifier.py
57
+ # ---------------------------------------------------------------------------
58
+
59
+ # Private/reserved IP ranges blocked to prevent SSRF
60
+ BLOCKED_NETWORKS = [
61
+ ipaddress.ip_network("0.0.0.0/8"), # "This host" — resolves to loopback on Linux
62
+ ipaddress.ip_network("10.0.0.0/8"),
63
+ ipaddress.ip_network("172.16.0.0/12"),
64
+ ipaddress.ip_network("192.168.0.0/16"),
65
+ ipaddress.ip_network("127.0.0.0/8"),
66
+ ipaddress.ip_network("169.254.0.0/16"), # Link-local / IMDS
67
+ ipaddress.ip_network("100.64.0.0/10"), # Carrier-grade NAT
68
+ ipaddress.ip_network("::1/128"), # IPv6 loopback
69
+ ipaddress.ip_network("fc00::/7"), # IPv6 unique local
70
+ ipaddress.ip_network("fe80::/10"), # IPv6 link-local
71
+ ]
72
+
73
+
74
+ # Allowed parent directories for file:// inputs (shared by storage + ffmpeg providers)
75
+ # Deduplicate: /tmp and gettempdir() may resolve to the same or different paths
76
+ ALLOWED_FILE_ROOTS: tuple[Path, ...] = tuple(
77
+ {Path(tempfile.gettempdir()).resolve(), Path("/tmp").resolve()} # noqa: S108
78
+ )
79
+
80
+
81
+ def check_ssrf(url: str, *, exc_type: type[Exception] = ValueError) -> None:
82
+ """Reject non-HTTPS URLs and hostnames resolving to private IP ranges.
83
+
84
+ Shared SSRF guard used by storage transfers and webhook dispatch.
85
+ Callers pass their domain-specific exception type via ``exc_type``.
86
+ """
87
+ parsed = _urlparse(url)
88
+ if parsed.scheme not in ("https",):
89
+ raise exc_type(f"Only HTTPS URLs are allowed, got: {parsed.scheme}://")
90
+
91
+ host = parsed.hostname or ""
92
+ if host.lower() == "localhost":
93
+ raise exc_type(f"Private/loopback URLs are not allowed: {host}")
94
+
95
+ try:
96
+ addrinfos = socket.getaddrinfo(host, None, proto=socket.IPPROTO_TCP)
97
+ except socket.gaierror as exc:
98
+ raise exc_type(f"Cannot resolve hostname: {host}") from exc
99
+
100
+ for _, _, _, _, sockaddr in addrinfos:
101
+ try:
102
+ ip = ipaddress.ip_address(str(sockaddr[0]))
103
+ except ValueError:
104
+ continue
105
+ if any(ip in net for net in BLOCKED_NETWORKS):
106
+ raise exc_type(f"Private/loopback URLs are not allowed: {host}")
107
+
108
+
109
+ # ---------------------------------------------------------------------------
110
+ # Manifest size cap — bounds the JSON payload accepted from disk/media
111
+ # ---------------------------------------------------------------------------
112
+ # Real manifests are O(KB). 16 MiB is generous and bounds OOM blast from
113
+ # malicious media or sidecars that declare absurd payload sizes.
114
+ MAX_MANIFEST_BYTES = 16 * 1024 * 1024
115
+
116
+
117
+ # ---------------------------------------------------------------------------
118
+ # Credential pattern detection — used by error sanitization (providers/base.py)
119
+ # AND by Pipeline.step build-time rejection of secret-shaped params values.
120
+ # Centralized here so both call sites share one regex of record.
121
+ # ---------------------------------------------------------------------------
122
+ _SECRET_PATTERNS = re.compile(
123
+ r"(r8_[A-Za-z0-9]{20,})" # Replicate tokens
124
+ r"|(sk-ant-[A-Za-z0-9\-]{20,})" # Anthropic API keys (before generic sk-)
125
+ r"|(sk-[A-Za-z0-9]{20,})" # OpenAI-style keys
126
+ r"|(AIza[A-Za-z0-9_\-]{30,})" # Google API keys
127
+ r"|(AKIA[A-Z0-9]{16})" # AWS access key IDs
128
+ r"|(Bearer\s+[A-Za-z0-9._\-]{20,})" # Bearer tokens
129
+ r"|(Token\s+[A-Za-z0-9._\-]{20,})" # Token auth headers
130
+ r"|(\bapi[_-]key[=:]\s*[A-Za-z0-9._\-]{20,})", # api_key=... / api-key:...
131
+ re.IGNORECASE,
132
+ )
133
+
134
+
135
+ def jittered_backoff(attempt: int) -> float:
136
+ """Compute exponential backoff with jitter to avoid thundering herd.
137
+
138
+ Starts at 1s, doubles per attempt, capped at 30s base with up to 25% jitter.
139
+ """
140
+ base = min(2**attempt, 30)
141
+ return base * (1 + random.uniform(0, 0.25)) # noqa: S311 — jitter, not crypto
142
+
143
+
144
+ def probe_audio_duration(path: str | Any) -> float | None:
145
+ """Try to read audio duration from a file using mutagen (optional dep).
146
+
147
+ Returns duration in seconds, or None if mutagen is not installed or
148
+ the file format is not recognized.
149
+ """
150
+ try:
151
+ from mutagen import File as MutagenFile
152
+
153
+ audio = MutagenFile(str(path))
154
+ if audio is not None and audio.info is not None:
155
+ return audio.info.length
156
+ except Exception: # noqa: S110 — mutagen is optional, fail gracefully
157
+ pass
158
+ return None
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,20 @@
1
+ """Agent / reasoning layer — evaluate outputs, refine prompts, retry."""
2
+
3
+ from genblaze_core.agents.evaluator import (
4
+ CallableEvaluator,
5
+ EvaluationResult,
6
+ Evaluator,
7
+ ThresholdEvaluator,
8
+ )
9
+ from genblaze_core.agents.loop import AgentContext, AgentIteration, AgentLoop, AgentResult
10
+
11
+ __all__ = [
12
+ "AgentContext",
13
+ "AgentIteration",
14
+ "AgentLoop",
15
+ "AgentResult",
16
+ "CallableEvaluator",
17
+ "EvaluationResult",
18
+ "Evaluator",
19
+ "ThresholdEvaluator",
20
+ ]