Skip to content

fix: raise ValueError for image/audio inputs in LocalHFBackend#1410

Open
markstur wants to merge 3 commits into
generative-computing:mainfrom
markstur:issue/1405
Open

fix: raise ValueError for image/audio inputs in LocalHFBackend#1410
markstur wants to merge 3 commits into
generative-computing:mainfrom
markstur:issue/1405

Conversation

@markstur

Copy link
Copy Markdown
Contributor

Pull Request

Issue

Fixes #1405

Description

LocalHFBackend silently dropped image and audio content on all three generation paths (_generate_from_context_standard, _generate_from_context_with_kv_cache, _generate_from_intrinsic). This encouraged hallucination when attempting image-text-to-text or audio-text-to-text with a query and an image/audio block that is dropped.

  • Add _check_no_multimodal_blocks() helper using get_images_from_component and get_audio_from_component so Instruction images/audio are caught as well as Message images/audio and future components
  • Call the check before to_chat() on all three paths so no formatting work is done on the error path
  • Add public .images and .audio properties to Instruction (Message already had them; required for get_*_from_component to work)
  • Export get_images_from_component from mellea.core (get_audio already was exported)

Testing

  • Tests added to the respective file if code was changed
  • New code has 100% coverage if code was added
  • Ensure existing tests and github automation passes (a maintainer will kick off the github automation when the rest of the PR is populated)

Attribution

  • AI coding assistants used

Adding a new component, requirement, sampling strategy, or tool?

If your PR adds or modifies one of the types below, check the matching box. A checklist of type-specific review items will be posted as a comment.

  • Component
  • Requirement
  • Sampling Strategy
  • Tool

NOTE: Please ensure you have an issue that has been acknowledged by a core contributor and routed you to open a pull request against this repository. Otherwise, please open an issue before continuing with this pull request.

LocalHFBackend silently dropped image and audio content on all three
generation paths (_generate_from_context_standard,
_generate_from_context_with_kv_cache, _generate_from_intrinsic).
This encouraged hallucination when attempting image-text-to-text or
audio-text-to-text with a query and an image/audio block that is dropped.

- Add _check_no_multimodal_blocks() helper using get_images_from_component
  and get_audio_from_component so Instruction images/audio are caught as
  well as Message images/audio and future components
- Call the check before to_chat() on all three paths so no formatting
  work is done on the error path
- Add public .images and .audio properties to Instruction
  (Message already had them; required for get_*_from_component to work)
- Export get_images_from_component from mellea.core (get_audio already
  was exported)

Signed-off-by: Mark Sturdevant <mark.sturdevant@ibm.com>
Assisted-by: IBM Bob
@markstur
markstur requested a review from a team as a code owner July 18, 2026 00:09
@github-actions github-actions Bot added the bug Something isn't working label Jul 18, 2026
@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

This comment is managed by a bot. Editing it is fine — checking off boxes, adding notes — but please leave the HTML comment marker on the first line alone, otherwise checklist updates will break.

This touches a component only to make Instruction and Message more consistent in
what is visible and to allow get_*_from_component() to work correctly and be used
in both cases (images and audio). This is an improvement in consistency and is relevant here because
those functions offer a clean solution vs using some redundant processing just to do this check.

Component PR Checklist

Use this checklist when adding or modifying components in mellea/stdlib/components/.

Protocol Compliance

  • parts() returns list of constituent parts (Components or CBlocks)
  • format_for_llm() returns TemplateRepresentation or string
  • _parse(computed: ModelOutputThunk) parses model output correctly into the specified Component return type

Content Blocks

  • CBlock used appropriately for text content
  • ImageBlock used for image content (if applicable)

Integration

  • Component exported in mellea/stdlib/components/__init__.py or, if you are adding a library of components, from your sub-module

@psschwei psschwei left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

One question (maybe for @jakelorocco): do we anticipate adding multimodal support for the HF backend in the future?

@markstur

Copy link
Copy Markdown
Contributor Author

LGTM

One question (maybe for @jakelorocco): do we anticipate adding multimodal support for the HF backend in the future?

FYI -- For audio, it might be a significant enough to need a new backend. I didn't look into it enough to be sure, but the model might need to be loaded by AutoModelForSpeechSeq2Seq (I do have an AI-generated prototype of that suggested solution). Personally, I think that ideally one backend would just figure out how to load different models differently with Auto... or a pipeline name param or something, but I'm aware that there are probably reasons to have more backends that are simpler (easy) vs try to make one backend do everything for HF (which can get ugly). Something to investigate in future.

In the meantime, our audio-text-to-text support in backends is limited (reflecting the current state of models/servers), but I think it useful that we get the API plumbing right and catch non-supported cleanly like this instead of silently dropping.

@ajbozarth ajbozarth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude review below, I'll leave final approval to others as I dont have the bandwidth to do a manual review.

Correct root-cause fix — using get_*_from_component catches Instruction/Message/future components, and placing the check before to_chat() avoids wasted work. Two non-blocking nits below.

_GENERATE_KWARGS_ALLOWLIST: frozenset[str] = _compute_generate_kwargs_allowlist()


def _check_no_multimodal_blocks(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper isn't called from _generate_from_raw, which formats actions directly and would still silently drop multimodal content — so the "silently dropped" gap is only partially closed. Either guard that path too or note the remaining gap in the PR description / a follow-up issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread mellea/backends/huggingface.py Outdated

Raises:
ValueError: If no adapter is registered for the requested intrinsic.
ValueError: If the context contains images or audio; `LocalHFBackend`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two Raises: ValueError entries — merge into one ValueError: with a combined description to match Google-style convention.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

markstur added 2 commits July 20, 2026 13:22
_generate_from_raw was silently dropping image/audio content in actions
by passing them to formatter.print() without any guard. Add a per-action
call to _check_no_multimodal_blocks (ctx=None, since ctx is never
rendered on the raw path). Also make ctx optional in the helper.

Signed-off-by: Mark Sturdevant <mark.sturdevant@ibm.com>
Assisted-by: IBM Bob
Signed-off-by: Mark Sturdevant <mark.sturdevant@ibm.com>

@planetf1 planetf1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice fix — a hard error is much better than silently dropping images/audio and letting the model hallucinate around them. Reviewed this thoroughly, no blockers. Two test gaps worth closing before merge, noted below. Everything else checked out fine.

def audio(self) -> list[AudioBlock | AudioUrlBlock] | None:
"""Returns the audio associated with this instruction."""
return self._audio

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These properties are what let the guard detect multimodal content on an Instruction (it relies on hasattr(c, "images")). All the new tests use Message, though — could you add a test that goes through Instruction instead, so this path is actually covered?

if not ctx.is_chat_context:
raise Exception("Does not yet support non-chat contexts.")

_check_no_multimodal_blocks(None, ctx)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_generate_from_intrinsic also got the guard, but nothing tests it. Could you add one for this path too?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: huggingface backend silently drops images and audio

4 participants