Skip to content

v0.3.0 feat: audio and video for Cloudflare's clef-omni - #23

Merged
obie merged 4 commits into
mainfrom
feat/clef-omni-media
Oct 12, 2026
Merged

obie merged 4 commits into
mainfrom
feat/clef-omni-media

Conversation

@obie

@obie obie commented Oct 12, 2026

Copy link
Copy Markdown
Owner

Summary

0.3.0 lets Client#ask send audio and video to Cloudflare's clef-omni, the multimodal Clef model Cloudflare released on Workers AI. Text and image calls to clef-omni already worked on 0.2.0; audio and video could not be sent at all.

  • Audio and video: ask(audio:, videos:) next to images:, as base64 data URLs. On Cloudflare they go in the top-level audio and videos fields.
  • Media helper: RubyDecisionModel::Media.from_file / .data_url build data URLs for images, audio (WAV, MP3), and video (MP4, WebM). Images is unchanged.
  • Refused before sending: audio or video for clef, clef-flash, or any other provider raises RequestError and nothing goes over the wire. Only clef-omni is allowed, because Cloudflare does not document what the other models do with those fields.
  • Aliases: cloudflare/clef-omni and @cf/cloudflare/clef-omni resolve on the Cloudflare provider. In 0.2.0 they raised ConfigurationError.
  • Provider hooks: supports_audio?(model) and supports_videos?(model), false by default. Base#request_body accepts audio: and videos:.
  • Release tooling: rake smoke takes SMOKE_AUDIO and SMOKE_VIDEO, and prints skipped <kind> when a target does not read a supplied file.
  • Docs: README covers the above, how far the client-side check goes, retries replaying the whole body, and timeouts for long clips. It also records that hosted clef-flash now has a 24,576-token context window and that Cloudflare cuts a longer text state to fit without an error.
  • 0.1.0 and 0.2.0 custom providers keep working: a call with no media still passes exactly three keywords to request_body, and supports_images? is still called with no argument.

Not changed on purpose: default timeouts (5 seconds for Cloudflare), the :system_one provider (no confirmed wire format for a self-hosted Clef-omni), and OpenRouter aliases (OpenRouter does not list clef-omni).

Not verified live against Cloudflare. The audio and videos field names come from Cloudflare's model page, not from a request that returned answers; no Cloudflare credentials were available where this was built. A live rake smoke TARGETS=open_router,open_router:clef on this branch answered through Jev and Clef (text path). To check the new path: rake smoke TARGETS=cloudflare:clef-omni SMOKE_AUDIO=clip.mp3 SMOKE_VIDEO=clip.mp4.

Merging this publishes 0.3.0 to RubyGems through the Release workflow.

Test Coverage

35 changed code paths and caller flows, 35 tested (100%).
Client#build_body: no media keeps three keywords, single String wrapped,
  refusal per kind, wrong prefix per kind, mixed and non-String elements  ★★★
Client#accepts? and unsupported_message: all three kinds, image
  messages pinned byte for byte                                            ★★★
Base: supports_audio? / supports_videos? default false                     ★★
Base#request_body: audio and videos keys                                   ★★★
Cloudflare: clef-omni aliases                                              ★★
Cloudflare: supports_audio? / supports_videos? per model and nil           ★★★
Media: data_url valid and invalid, from_file infer, explicit type,
  unknown extension, missing file                                          ★★★
0.2.0-style custom provider: images-only request_body still works,
  audio refused, zero-argument supports_images?                            ★★★
Rakefile smoke: excluded (live-only)

Tests: 199 → 216 (+17), 613 → 699 assertions, 7 test files. Zero warnings on Ruby 3.3.4; also green on 3.2.1.
The coverage audit wrote 8 of the 17, including three regression tests for behaviour 0.2.0 callers rely on. It found no bugs.

Pre-Landing Review

Five specialists, a red team, and two adversarial passes (Claude and Codex) reviewed the diff, plus a Codex structured review. No critical findings. Codex structured review: no actionable defects.

Fixed before this PR:

  • README did not say that a provider answering true from supports_audio? or supports_videos? must accept audio: / videos: in request_body (api-contract).
  • README implied more validation than the client does. It now says the check is a data-URL prefix check, that from_file reads the whole file, and that format, size, and count are the provider's to enforce (both adversarial passes).
  • README did not say a retry sends the whole body again. It now does, with retry: { max_retries: 0 } for large clips (red team).
  • rake smoke printed a plain ok for a target that skipped the supplied audio or video. It now prints skipped audio / skipped video (red team).

Left as is, all of them true of images: since 0.2.0:

  • Media is checked by prefix only, so malformed base64 or an unsupported subtype reaches the provider and fails there.
  • No client-side size or count limit.
  • A media string with invalid bytes raises RequestError with a message that names "state or questions".
  • A media string in a non-ASCII-compatible encoding (UTF-16) raises Encoding::CompatibilityError, not RequestError.
  • audio: {} is treated like an empty array.

Testing, maintainability, security, and performance specialists: no findings.

Design Review

No frontend files changed. Design review skipped.

Eval Results

No prompt-related files changed. Evals skipped.

Scope Drift

Scope Check: CLEAN. Intent: audio and video for clef-omni in a 0.3.0 release. Delivered: that, its docs, and the smoke options needed to verify it.

Plan Completion

No plan file detected.

Test plan

  • bundle exec rake: 216 runs, 699 assertions, 0 failures, 0 errors, 0 warnings (Ruby 3.3.4)
  • Same suite on Ruby 3.2.1
  • rake release:build with --strict: ruby_decision_model-0.3.0.gem builds and carries all 19 files under lib/
  • Live rake smoke TARGETS=open_router,open_router:clef: both answered
  • Live rake smoke TARGETS=cloudflare:clef-omni SMOKE_AUDIO=clip.mp3 SMOKE_VIDEO=clip.mp4 (needs Cloudflare credentials)

🤖 Generated with Claude Code

obie and others added 4 commits October 12, 2026 08:42
Client#ask takes audio: and videos: next to images:, as base64 data URLs.
Providers opt in through supports_audio?(model) and supports_videos?(model),
false by default; Cloudflare says yes for clef-omni only, so audio or video
sent to clef, clef-flash, or any other provider raises RequestError before
anything is sent. Base#request_body carries the two new fields.

Media.data_url and Media.from_file build data URLs for images, audio, and
video. Images is unchanged.

cloudflare/clef-omni and @cf/cloudflare/clef-omni resolve on the Cloudflare
provider; in 0.2.0 they raised ConfigurationError.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sends an audio clip and a video to targets whose model reads them, the way
SMOKE_IMAGE already does for images. A target that skips a given file now
says so on its ok line, so a run against a model that does not read audio
cannot pass for one that exercised it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README covers audio: and videos:, the Media helper, how far the client-side
check goes, retries replaying the whole body, the clef-omni aliases, the new
provider hooks, and timeouts for long clips. Hosted clef-flash now has a
24,576-token context window, and Cloudflare cuts a longer text state to fit
without an error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@obie
obie merged commit ca7f3d2 into main Oct 12, 2026
4 checks passed
@obie
obie deleted the feat/clef-omni-media branch October 12, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant