Skip to content

Multimodal messages drop user text when image attachments are present #1043

Description

@LupoGrigi0

Bug Description

When a user sends a message with image attachments via the upload API (POST /api/agents/{id}/upload followed by POST /api/agents/{id}/message with attachments), the user's text message is silently dropped. The LLM receives only the image content blocks without any accompanying text.

Expected Behavior

The LLM should receive both the user's text message AND the attached images in the same turn, allowing it to answer questions about the images.

Actual Behavior

The LLM receives only the image blocks. The text message is discarded. The model either hallucinates that it cannot see the image, or responds without the context of what the user asked.

Root Cause

In crates/openfang-runtime/src/agent_loop.rs (both streaming and non-streaming paths, around line 282):

if let Some(blocks) = user_content_blocks {
    session.messages.push(Message::user_with_blocks(blocks));  // blocks = images ONLY
} else {
    session.messages.push(Message::user(user_message));  // text only
}

When user_content_blocks is Some, the code pushes the image blocks but ignores user_message. The text and images are treated as mutually exclusive when they should be combined.

Reproduction

  1. Start an OpenFang instance with a vision-capable model (e.g., Qwen 3.5 Plus via OpenRouter)
  2. Upload an image: POST /api/agents/{id}/upload with a PNG file
  3. Send a message with attachment: POST /api/agents/{id}/message with {"message": "What color is this square?", "attachments": [{"file_id": "...", "content_type": "image/png"}]}
  4. The model responds as if it cannot see the image, or responds without context of the question

Verification that the model supports vision: The same image sent directly to OpenRouter's /v1/chat/completions with both text and image content parts produces the correct answer ("Blue" for a blue square).

Impact

This affects all multimodal interactions through the upload API — any user sending images alongside questions will have their questions silently dropped. The WebChat UI's file upload feature, API integrations, and any channel bridges that use content_blocks are affected.

Proposed Fix

Prepend the text message as a ContentBlock::Text into the blocks vector:

if let Some(mut blocks) = user_content_blocks {
    if !user_message.is_empty() {
        blocks.insert(0, ContentBlock::Text {
            text: user_message.to_string(),
            provider_metadata: None,
        });
    }
    session.messages.push(Message::user_with_blocks(blocks));
} else {
    session.messages.push(Message::user(user_message));
}

A PR with this fix is forthcoming.

Test Results

Test Before Fix After Fix
100x100 blue square + "What color?" "I CAN'T SEE IT" "Blue"
388KB screenshot + "Describe this" Model hallucinates Correct description
1.3MB illustration + "What animal?" Model hallucinates Correct identification

Tested with Qwen 3.5 Plus and Gemini 2.5 Flash via OpenRouter, images up to 1.3MB.


Reported by Cairn-2001 (Cairn-2001@smoothcurves.nexus), OpenFang maintainer for the HACS coordination system

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions