Skip to content

v0.4.0

Latest

Choose a tag to compare

@jeronimodeleon jeronimodeleon released this 25 Jun 17:37
7301e80

Note: the umbrella genblaze package publishes as 0.4.1 in this wave (the v0.4.0 tag is the CHANGELOG wave name, not the umbrella version). Run make post-release VERSION=0.4.1 after publish.

[0.4.0] - 2026-06-25

Security hardening (SSRF, URL-only asset verification), two new providers
(Hume Octave TTS, AssemblyAI STT), and a batch of connector patch-republishes
to update the genblaze-core>=0.3.4 floor.

Released package versions

  • genblaze (umbrella) 0.4.0 → 0.4.1 — patch republish to refresh extras
    and connector floors. PyPI cannot overwrite an existing version; all changed
    pins ship only when a new wheel is published.
  • genblaze-core 0.3.2 → 0.3.4 (URL-only asset verification hardening,
    SSRF DNS pinning + redirect guard, fan-in failure propagation, sink lifecycle
    fix, async preflight offload, error sanitiser, tenant-scoped step cache)
  • genblaze-s3 0.3.2 → 0.3.4 (bumps genblaze-core floor to 0.3.4;
    region-probe close on S3StorageBackend, backend close() shuts boto3
    client to release thread pool connections on run() teardown)
  • genblaze-cli 0.3.0 → 0.3.2 (bumps genblaze-core floor to 0.3.4;
    exposes verify_hash() and output-asset sha256 diagnostics)
  • genblaze-replicate 0.3.0 → 0.3.2 (bumps genblaze-core floor to 0.3.4)
  • genblaze-gmicloud 0.3.1 → 0.3.2 (bumps genblaze-core floor to 0.3.4)
  • genblaze-humenew at 0.3.1 (Hume Octave TTS provider)
  • genblaze-assemblyainew at 0.3.0 (AssemblyAI speech-to-text provider)
  • genblaze-openai 0.3.0 → 0.3.1 (DALL-E URL outputs materialised locally;
    pinned-DNS download; bumps genblaze-core floor to 0.3.4)
  • genblaze-google 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-decart 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-elevenlabs 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-langsmith 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-lmnt 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-luma 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-nvidia 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-runway 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)
  • genblaze-stability-audio 0.3.0 → 0.3.1 (bumps genblaze-core floor to 0.3.4)

Security

  • genblaze-core 0.3.2 → 0.3.4: Manifest.verify() now rejects output
    assets that lack sha256 for every supported schema version, preventing a
    schema downgrade from bypassing declared output sha256 coverage. This is an
    intentional security exception to the normal patch-release compatibility
    policy; use verify_hash() for legacy hash-only checks against historical
    URL-only media (#77).
  • genblaze-core: Asset.sha256 values remain loadable even when malformed,
    so historical and cross-producer manifests can still be inspected with
    verify=False or allow_unverified_assets=True. Manifest.verify() and
    genblaze verify treat missing, uppercase, or otherwise malformed sha256
    as unverified output coverage. They do not fetch remote asset URLs; consumers
    must independently hash fetched bytes before trusting those bytes (#77).
  • genblaze-core: schema 1.6 URL-only hash markers are Python read-supported.
    The canonical marker URL strips known credential, expiry, and response
    override query parameters for AWS SigV4/SigV2, GCS, B2, CloudFront, Azure SAS,
    and GCS V2 signed URLs (including bare authorization tokens) while retaining
    resource-identifying query parameters. Default manifest emission, storage
    writes, media embedding, and the published JSON Schema/TypeScript spec stay on
    schema 1.5 for an expand-contract rollout (#77).
  • genblaze-openai: dall-e-2 / dall-e-3 URL responses are now downloaded
    immediately to a local file:// asset with a populated sha256 and
    size_bytes, instead of being stored as the raw (credential-bearing,
    ~1-hour) Azure SAS URL. The signed URL is used only for that fetch and never
    reaches the manifest, step cache, or any sink. This fixes the .run(sink=...)
    transfer path (which previously received a credential-stripped, unfetchable
    URL) and lets DALL-E outputs verify without a storage sink (#77).
  • genblaze-cli 0.3.0 → 0.3.2: raises its genblaze-core floor to the first
    core version that exposes verify_hash() and output-asset sha256 diagnostics
    (#77).
  • genblaze umbrella package: raises its genblaze-core floor to 0.3.4 so
    umbrella installs receive the verification hardening (#77).
  • Provider and storage connector packages now require genblaze-core>=0.3.4,<0.4
    and carry patch version bumps so adapter-only installs receive republished
    wheels with the URL-only asset verification fix (#77).
  • ObjectStorageSink.read_manifest_for_asset() now requires tenant_id, stores
    tenant-scoped asset index entries, validates asset_id as a UUID, falls back
    to legacy flat index entries during migration, rejects manifest pointer
    substitution unless the recovered manifest references the requested asset,
    and applies the same staged verification behavior as read_manifest() (#77).
  • ObjectStorageSink.write_run() now fails the write when an asset transfer
    fails, instead of uploading a success-path manifest that later fails strict
    verification. Successful transfers from a partial failure are reused on
    retry and the in-memory manifest hash is recomputed before raising (#77).

Changed

  • ObjectStorageSink.read_manifest(verify=True) verifies canonical hashes by
    default and logs output assets whose sha256 is missing or malformed.
    Hard-failing those unverified outputs is staged behind
    strict_manifest_reads=True or GENBLAZE_STRICT_MANIFEST_READS=true so
    operators can backfill historical URL-only manifests before enabling strict
    read failures on hot paths (#77).
  • genblaze-core [audio] extra: mutagen capped at <1.49. Mutagen 1.48
    changed m4a timescale handling in a way that broke the AAC handler test
    fixture; the cap prevents a future mutagen release from reintroducing
    the same break before it can be vetted (#108).

Fixed

  • tools/check_pin_parity.py: extend the pre-publish drift guard to
    compare [project.optional-dependencies] as well as base
    [project.dependencies]. Previously the gate reported
    0 drift for the genblaze umbrella even when connector pins in
    the all, video, image, or audio extras diverged from the
    published wheel, because Requires-Dist extras entries were
    silently filtered out. The gate now groups PyPI extras by name and
    diffs each extra independently, naming the extra in the drift
    report (e.g. [all]). Adds tools/tests/test_check_pin_parity.py
    and wires pytest tools/tests/ into make test. Updates
    RELEASING.md to accurately describe the gate's scope (#23).

Added

  • genblaze-hume: new provider adapter for Hume AI Octave TTS (audio /
    text-to-speech). Synchronous SyncProvider that decodes the API's base64
    audio to a local file:// asset; step.model (octave-1 / octave-2)
    maps to the Octave version field. Ships a pattern-keyed octave-* model
    family with a permissive fallback, DiscoverySupport.NONE, and no hardcoded
    pricing (register per-character rates via the recipe in
    docs/reference/pricing-recipes.md). Available as pip install genblaze-hume
    or the genblaze[hume] / genblaze[audio] extras.
  • genblaze-assemblyai: new provider adapter for AssemblyAI speech-to-text
    / transcription — the first connector that consumes audio and produces
    text. Async BaseProvider (submit / poll / fetch_output) that resolves an
    audio URL (step.inputs[0]params["audio_url"]prompt, SSRF-validated
    via validate_chain_input_url) and emits a hash-verified TEXT asset
    (text:{sha256}, media_type="text/plain", transcript in metadata["text"])
    with word-level timings on AudioMetadata.word_timings (converted ms → s).
    step.model is sent on the SDK's plural speech_models field (the live API
    has deprecated the singular speech_model field and the legacy best/nano
    aliases). Ships a pattern-keyed assemblyai-speech family (universal-3-pro
    / universal-2) with a permissive TEXT fallback,
    DiscoverySupport.NONE, and no hardcoded pricing (register
    per-minute-of-input-audio rates via the recipe in
    docs/reference/pricing-recipes.md). Available as
    pip install genblaze-assemblyai or the genblaze[assemblyai] extra.

Security

  • genblaze-core: DNS pinning closes the rebinding / TOCTOU window on all
    outbound HTTP paths. resolve_ssrf resolves the hostname once, validates
    every returned IP, and returns the pinned address. Callers connect to that
    IP directly — the HTTP client never performs a second independent resolution.
    TLS SNI and cert verification continue to use the original hostname. Affects
    storage/transfer.py (urllib3 HTTPSConnectionPool per hop), and
    webhooks/notifier.py and genblaze_openai/dalle.py (direct
    http.client.HTTPSConnection with pre-connected pinned socket) (#9).
  • genblaze-core: SSRF guard now validates every HTTP redirect hop in the
    asset transfer path (storage/transfer.py). Previously, check_ssrf ran
    only on the initial URL; a CDN redirect to a private/loopback/IMDS address
    bypassed the guard entirely. Redirects are now followed manually with a
    bounded loop (max 5 hops); each Location is re-resolved and re-pinned
    before following, and downgrade to non-HTTPS is rejected (#9).
  • genblaze-core: Webhook delivery (webhooks/notifier.py) switched from
    urllib.request to http.client.HTTPSConnection directly. http.client
    has no redirect handler, so a 3xx response is treated as a delivery failure
    rather than following the Location header, preventing a server-side
    redirect to an internal host from bypassing the SSRF guard (#9).
  • genblaze-openai: _download_https_to_temp now uses http.client
    directly with a pinned DNS connection, closing both the redirect-bypass and
    DNS rebinding vectors for edit-input downloads (#9).
  • genblaze-core: IPv4-mapped IPv6 addresses (::ffff:169.254.x.x,
    ::ffff:10.x.x.x, etc.) are now normalized to their IPv4 form before the
    SSRF blocklist check. Previously, a DNS response returning an IPv4-mapped
    address bypassed all IPv4 BLOCKED_NETWORKS entries (#9).
  • genblaze-core: HTTP Location headers in redirect chains are now resolved
    with urljoin before re-validation, so RFC-legal relative redirects work
    and relative redirects to private targets are still rejected (#9).
  • Egress proxy note: DNS pinning requires a direct TCP connection to the
    validated IP; HTTP_PROXY / HTTPS_PROXY / NO_PROXY env vars are ignored
    by design on all pinned outbound paths (transfer, webhook, dalle). Deployments
    that require an egress proxy should allowlist the target hosts at the proxy
    instead of relying on env-var forwarding.

Fixed

  • genblaze-core: Pipeline.arun() no longer blocks the event loop during
    model preflight. The network-bound phase (_validate_models, which uses a
    ThreadPoolExecutor for provider discovery fetches) is now offloaded via
    asyncio.to_thread, allowing concurrent coroutines to keep running during
    the preflight window. Cheap capability checks (modality, chain-input) still
    run synchronously. Pipeline.run() behavior is unchanged (#56).
  • genblaze-core: Pipeline.run()/arun() now release a sink's run-scoped
    resources in their finally block (and on early preflight/validation
    failure), shutting down the ObjectStorageSink eager-upload
    ThreadPoolExecutor and releasing the backend connection pool —
    S3StorageBackend.close() now closes the boto3 client. Previously non-daemon
    worker threads stayed alive after run() returned and in-flight eager-upload
    futures leaked on error paths that bypassed write_run. Sinks declare
    lifecycle ownership via BaseSink._close_with_run (default True):
    run-scoped sinks (ObjectStorageSink) are closed by the pipeline and are
    single-use, while fire-and-forget WebhookSink opts out (False) so it is
    never closed — keeping webhook delivery non-blocking and the sink reusable
    across runs. batch_run()/abatch_run() close their shared sink once after
    the whole batch rather than after the first item. BaseSink gains
    __enter__/__exit__ for callers that manage the lifecycle outside a
    run(). Behavior change: a run-scoped sink passed to run()/arun() is
    closed afterward — construct a fresh one per run rather than reusing it (#57).
  • genblaze-openai, genblaze-google: remove hardcoded USD per-token
    rate tables (_RATES) from the standalone chat() helpers and return
    cost_usd=None unconditionally, consistent with the 0.3.0 contract that
    connector modules ship zero static rate tables (Pipeline-Step providers
    register rates via PricingContext/ModelSpec). The standalone chat()
    helpers have no model registry, so callers that relied on
    ChatResponse.cost_usd being non-None for known models must now compute
    cost from tokens_in/tokens_out with their own rates (see
    docs/reference/pricing-recipes.md). Adds
    test_pricing_phaseout.py to genblaze-core — a lint-style CI guard that
    fails if any future connector reintroduces a _RATES/_PRICING constant (#13).
  • genblaze-core: post-submit step-level retries now resume the existing
    upstream prediction instead of submitting a new one, including transient
    checkpoint failures after submit() returns by replaying idempotent
    on_submit(step_id, prediction_id) callbacks before retry resume (#70).
  • genblaze-core: fan-in consumers using input_from now fail before provider
    invocation when a referenced producer step failed, produced no assets, or
    points at an out-of-range prior step, preventing a downstream step from
    reporting success with empty declared inputs (#69). Affected pipelines that
    previously appeared green can now report FAILED/INVALID_INPUT, which may
    increase status-based alerts and change manifest hashes once during rollout.
    Route or re-baseline those alerts using
    metadata.failure_reason="input_resolution"; these pre-failed steps also
    carry metadata.provider_invoked=false for telemetry filtering.
  • genblaze-core: failed Step.error values now use the shared sanitizer for
    OpenAI/Anthropic/Google/Replicate keys, AWS access key IDs and secret access
    keys, Backblaze B2 application keys, JWTs, bearer/token headers, API-key
    assignments, and basic-auth URL credentials, then truncate to 500 characters
    before manifest, log, or stream-event emission. This redaction hardening ships
    with #69 because fan-in pre-fail telemetry can otherwise re-surface untrusted
    upstream provider errors on dependent steps.
  • genblaze-core: StepCache now partitions the step cache key by tenant_id
    when a tenant is set (via Pipeline(tenant_id=...), or passed to
    StepCache.get/put), so a cache shared across tenants no longer serves one
    tenant's cached output asset to another for an otherwise-identical step.
    Single-tenant keys are unchanged. A tenant_id in RunnableConfig is now
    rejected at runtime (it was never honored), so a dynamic caller passing it via
    config() / invoke(config=...) fails loudly instead of silently bypassing
    cache isolation (#68).
  • genblaze-replicate (0.3.0 → 0.3.1): declare httpx>=0.24 as a
    direct dependency. ReplicateProvider._get_client() imports httpx
    directly to build the client timeout, but the package declared only
    replicate, which pins httpx>=0.21 — below the 0.22 floor where the
    httpx.Timeout(connect=...) kwarg landed. Pin aligned with the
    httpx>=0.24 already used by the nvidia, gmicloud, and stability-audio
    connectors. Version bumped so a corrected wheel can publish past the
    skip-existing pin-parity gate (#37).
  • genblaze-core (0.3.2 → 0.3.3): declare urllib3>=1.26,<3 as a
    direct dependency. storage/transfer.py imports urllib3 unguarded at module load
    (the shared PoolManager behind AssetTransfer) and storage/__init__
    imports it eagerly, so import genblaze_core.storage hard-required urllib3 on
    a clean install while core declared only pydantic + pillow. It previously
    arrived only transitively via a connector's boto3 stack — the same
    clean-install crash class as the replicate/httpx fix above.
  • genblaze-s3 (0.3.2 → 0.3.3): declare botocore>=1.31 (imported
    directly in backend.py) and aiobotocore>=2.7 in the async extra
    (imported directly in async_backend.py). Both always shipped transitively
    via boto3/aioboto3, which pin their exact versions — so these declarations add
    honesty for the dependency gate without changing what resolves.

Added

  • genblaze-core: http (httpx), otel (opentelemetry-api), and
    testing (pytest) extras. These advertise previously-undeclared
    guarded/soft imports. In particular genblaze_core.testing (the public
    MockProvider / ProviderComplianceTests harness) imports pytest at module
    load and ships in the wheel, so it now installs via
    pip install "genblaze-core[testing]".

Changed

  • Repo tooling: new make deptry dependency-hygiene gate
    (backed by per-package [tool.deptry] config) fails on undeclared imports
    (DEP001), shipped imports of dev-only deps (DEP004), and misclassified
    transitive deps — the clean-install crash class above. Wired into make lint,
    make pre-release, and a new deptry CI job that also runs pip check
    against the editable workspace. libs/meta is excluded (umbrella metapackage:
    its deps are install-time bundles, not imports).