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
- Start an OpenFang instance with a vision-capable model (e.g., Qwen 3.5 Plus via OpenRouter)
- Upload an image:
POST /api/agents/{id}/upload with a PNG file
- Send a message with attachment:
POST /api/agents/{id}/message with {"message": "What color is this square?", "attachments": [{"file_id": "...", "content_type": "image/png"}]}
- 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
Bug Description
When a user sends a message with image attachments via the upload API (
POST /api/agents/{id}/uploadfollowed byPOST /api/agents/{id}/messagewithattachments), 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):When
user_content_blocksisSome, the code pushes the image blocks but ignoresuser_message. The text and images are treated as mutually exclusive when they should be combined.Reproduction
POST /api/agents/{id}/uploadwith a PNG filePOST /api/agents/{id}/messagewith{"message": "What color is this square?", "attachments": [{"file_id": "...", "content_type": "image/png"}]}Verification that the model supports vision: The same image sent directly to OpenRouter's
/v1/chat/completionswith 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_blocksare affected.Proposed Fix
Prepend the text message as a
ContentBlock::Textinto the blocks vector:A PR with this fix is forthcoming.
Test Results
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