Skip to content

Genblaze feedback #168

Description

@woadi-vector

Feedback from building a screenplay-to-previs pipeline (Backblaze Gen Media Hackathon)

Project: [Reel](https://github.com/woadi-vector/reel) — turns a screenplay scene
into a previsualized cut (shot breakdown → stills → motion clips → score → assembled
MP4), stored on B2 with provenance. Built during the hackathon.
SDK versions: genblaze 0.4.1, genblaze-core 0.3.4, genblaze-s3 0.3.4,
genblaze-replicate, genblaze-gmicloud 0.3.2

Thanks for the SDK — the Pipeline.step() model, ObjectStorageSink, and the
manifest provenance are genuinely good, and the hierarchical key strategy meant I
got a well-organized bucket for free. Below is friction I hit, roughly in order of
impact. Happy to split any of these into separate issues if that's more useful.


1. Cross-provider image→video handoff forces direct S3 calls (highest impact)

Image-to-video models require a publicly fetchable start image. Kling
(kwaivgi/kling-v2.1) takes a start_image URL that its servers fetch. But
assets written through ObjectStorageSink land in a private B2 bucket, so the
video provider can't retrieve them.

.from_result() chaining didn't bridge this — the video step failed with:

422 Input validation failed: - input: start_image is required

The working pattern turned out to be provider-to-provider: take the image
provider's own public delivery URL and hand it straight to the video model, then
persist the resulting clip to B2 afterward. That works, but it means the video
and audio stages leave the Genblaze sink path and use direct S3 calls
, which is
exactly what the reference sample (genblaze-gen-media-multi-provider-sample)
says not to do ("zero direct boto3 calls").

Suggestion: a first-class path for cross-provider chaining where the
downstream provider needs a fetchable URL — e.g. an opt-in presigned-URL handoff
from the sink, or a documented chain(public=True) mode. Right now the naive
approach (chain through the sink) fails in a way that's hard to diagnose, because
the error surfaces as a validation error on the video step rather than anything
pointing at storage visibility.

At minimum this deserves a documented note: private storage breaks image→video
chaining, and here's the pattern that works.
I lost real time to it.


2. Non-official Replicate models 404 on name:version strings

meta/musicgen is not an "official" Replicate model, so it needs a version. But:

replicate.run("meta/musicgen")                    # 404
replicate.run("meta/musicgen:671ac645...")        # 404

The second one 404s because the client splits on / into (owner, name) and the
:version suffix rides along in name. What works:

m = replicate.models.get("meta/musicgen")
v = m.versions.list()[0]
replicate.run(v, input={...})                     # works

I see v0.5.0 adds "automatic version resolution" for Replicate community models —
if that covers this case, great, and it'd be worth calling out explicitly in the
release notes since the failure mode (a bare 404 with no detail) gives you nothing
to work with.


3. Model IDs are the single biggest source of lost time

Nearly every failure in this build traced back to a model string that didn't
resolve. Doc/docstring examples that don't exist on a given account are especially
costly, e.g. genblaze_gmicloud.chat's docstring cites
"deepseek-ai/DeepSeek-V3", which 404'd for me:

GMICloud chat failed (404): No matching target server found for model deepseek-ai/DeepSeek-V3

What finally worked was introspecting the registries:

from genblaze_gmicloud.models import build_image_registry, build_video_registry
build_image_registry().known()

Suggestions:

  • Make registry introspection a documented, first-class thing — a
    genblaze models --provider gmicloud CLI would have saved me hours.
  • Prefer account-verified examples in docstrings, or annotate them as
    illustrative only.
  • ModelRegistry.known() being a method while the object also exposes items /
    families took some trial and error; a short registry doc page would help.

4. GMICloud chat: default base URL fails with an SSL error

genblaze_gmicloud/chat.py has _DEFAULT_BASE_URL = "https://api.gmi-serving.com/v1",
while _base.py uses https://console.gmicloud.ai/api/v1/ie/requestqueue/apikey.
Calling chat() against the default gave:

GMICloud chat failed: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1032)

on a normal home connection (no proxy/VPN). Two different base URLs in one package
is confusing on its own; if one of them is stale, that's worth a fix. Also relevant:
chat models on my GMICloud account appeared to be dedicated-instance (hourly GPU
deployment) rather than shared serverless inference, which may be why the shared
endpoint didn't serve them. Documenting that distinction would help — I nearly
started a $6/hr deployment by clicking through from a model card.


5. ParquetSink table/schema names aren't documented

I wired ParquetSink alongside the B2 sink to build a queryable provenance index
(the feature I most wanted: "which model made which shot, and what did it cost").
But I had to guess at the output layout — single files vs. partitioned dirs, and
table names (runs / steps / assets?). A short schema doc would make this a
much more discoverable feature, because it's a genuinely strong differentiator.


6. Throttling needs a documented retry story

Replicate throttles hard below a credit threshold (burst-of-1), which silently
killed multi-step runs mid-scene:

429 Request was throttled. Your rate limit ... is reduced to 6 requests per minute
with a burst of 1 requests while you have less than $5.0 in credit.

I wrapped every call in retry-with-backoff, which fixed it. Since Genblaze already
owns the execution loop, built-in retry-on-429 with backoff (or a documented
retries= on .step()) seems like a natural fit — this will hit anyone running a
multi-shot pipeline on a new account.


7. Corroborating the sample's chat() vs Pipeline.step() note

The multi-provider sample flags that genblaze-openai's chat() is a standalone
function rather than a BaseProvider, so planning steps can't ride
Pipeline.step(). I hit the same asymmetry with genblaze-gmicloud.chat() — the
scene-breakdown stage sits outside the pipeline and its provenance has to be
persisted by hand, which means my most important upstream step (the one that
determines every downstream asset) is the one not covered by manifests.

Making chat a first-class provider would close a real provenance gap, not just an
ergonomic one.


What worked well (so it doesn't get lost)

  • KeyStrategy.HIERARCHICAL — organized bucket for free, no key design needed.
  • Manifests: provider/model/prompt/hash per asset made "provenance-aware
    workflow" a real feature rather than a claim.
  • response_format=<PydanticModel> on GMICloud chat() was excellent — schema-
    enforced JSON with zero defensive parsing. When I later moved that step to a
    provider without it, I had to reintroduce fence-stripping and retry logic. That
    feature is worth propagating everywhere chat exists.
  • Draft-versioned lineage via from_result() / parent_run_id mapped cleanly onto
    screenplay drafts (v26 → v27 forks instead of overwriting).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions