0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-14 13:58:53 +08:00
discourse/plugins/discourse-ai/spec/lib/completions
Takao Yokoyama cea4780cb3
FIX: AI: AWS Bedrock Converse image uploads were doubly base64-encoded (#39880)
## Summary

Image uploads delivered through the `aws_bedrock_converse` LLM provider
were rejected by Bedrock with `Could not process image` whenever an
agent / LLM had `vision_enabled` set to true.

Two related bugs are fixed:

### 1. `Dialects::Converse#upload_node` — base64 string passed where raw
bytes expected

In `plugins/discourse-ai/lib/completions/dialects/converse.rb`, image
content was emitted as:

```ruby
source: { bytes: details[:base64] }
```

`details[:base64]` is the upload's base64-encoded string (as produced by
`UploadEncoder`), but `Aws::BedrockRuntime::Client#converse` expects
**raw bytes** on the `:bytes` key — the SDK then base64-encodes them on
the wire. Passing the already-base64-encoded string causes Bedrock to
receive **doubly-encoded** data, which it cannot decode into a valid
image. Decoding back to raw bytes via
`Base64.decode64(details[:base64])` resolves the round-trip.

### 2. `AwsBedrockConverse#perform_completion!` — JSON-logging fails on
binary payloads

With raw bytes now flowing through `sdk_params`, the subsequent
`sdk_params.to_json` call (used to record the request in `start_log`)
raises `EncodingError` because PNG/JPEG bytes are not valid UTF-8. The
call is wrapped in `begin / rescue EncodingError` so the request can
still proceed; a placeholder string is recorded in the audit log instead
of the binary payload.

## Test plan

- A new spec case in
`plugins/discourse-ai/spec/lib/completions/dialects/converse_spec.rb`
asserts that `details[:base64]` is decoded back to raw bytes before
being emitted as `source: { bytes: ... }`. This guards against
regression.
- Verified end-to-end against `us.anthropic.claude-sonnet-4-6` via
Bedrock Converse on `ap-northeast-1` → `us-east-1` cross-region
inference profile: with this patch the model correctly describes
uploaded PNG attachments (a Loupe Browser version warning dialog)
instead of returning `Could not process image`.

## Reproduction (before the fix)

1. Configure an `aws_bedrock_converse` LLM in Discourse and assign it to
an `AiAgent` with `vision_enabled: true`.
2. Wire up `llm_triage` (or any path that goes through
`Dialects::Converse#upload_node`) to reply to a topic that contains an
image upload.
3. Observe:
`DiscourseAi::Completions::Endpoints::Base::CompletionFailed: The model
returned the following errors: Could not process image`

## Discovered while

Standing up a Discourse instance with Bedrock-backed AI as part of an
internal forum spike. Happy to iterate on the patch (e.g. tighten the
log fallback or extract a helper) if reviewers prefer a different shape.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Rafael Silva <xfalcox@gmail.com>
2026-05-26 15:16:49 -03:00
..
dialects FIX: AI: AWS Bedrock Converse image uploads were doubly base64-encoded (#39880) 2026-05-26 15:16:49 -03:00
endpoints FIX: AI: AWS Bedrock Converse image uploads were doubly base64-encoded (#39880) 2026-05-26 15:16:49 -03:00
anthropic_message_processor_spec.rb FEATURE: support thinking summary on responses API (#36013) 2025-11-18 07:54:14 +11:00
cancel_manager_spec.rb DEV: Enable Style/RedundantBegin rubocop rule (#40096) 2026-05-19 18:44:54 +02:00
doc_to_text_spec.rb FEATURE: extract text from document uploads for LLM prompts (#39634) 2026-05-05 08:16:23 +10:00
docx_to_text_spec.rb FEATURE: extract text from document uploads for LLM prompts (#39634) 2026-05-05 08:16:23 +10:00
json_stream_decoder_spec.rb DEV: Enable Style/RedundantParentheses rubocop rule (#40095) 2026-05-19 15:48:09 +02:00
llm_metric_spec.rb FEATURE: Add Prometheus metrics for LLM API calls (#35636) 2025-10-28 14:10:42 -03:00
llm_spec.rb FEATURE: gate temperature/top_p behind setting (#38479) 2026-03-12 07:40:29 +11:00
ods_to_text_spec.rb FEATURE: extract text from ODT and ODS document uploads (#39711) 2026-05-05 12:04:13 -03:00
odt_to_text_spec.rb FEATURE: extract text from ODT and ODS document uploads (#39711) 2026-05-05 12:04:13 -03:00
open_ai_message_processor_spec.rb FIX: clear partial flag when streaming tool calls finish (#35605) 2025-10-27 12:47:14 +11:00
prompt_messages_builder_spec.rb FIX: Enforce secure-upload ACL in AI bot prompt path (#39903) 2026-05-13 09:55:32 +08:00
prompt_spec.rb FEATURE: extract text from document uploads for LLM prompts (#39634) 2026-05-05 08:16:23 +10:00
report_spec.rb DEV: Adjustments in usage page for LLM's with credit allocations (#36566) 2025-12-09 09:55:29 -08:00
rtf_to_text_spec.rb FEATURE: extract text from document uploads for LLM prompts (#39634) 2026-05-05 08:16:23 +10:00
structured_output_spec.rb FIX: Harden JSON streaming tracker for arrays of objects (#36047) 2025-11-14 15:46:06 -03:00
token_usage_tracker_spec.rb FEATURE: add agentic execution mode for AI personas (#38230) 2026-03-05 15:06:54 +11:00
tool_definition_spec.rb FEATURE: improve image tool presets and tool editor (#38166) 2026-03-04 07:21:31 +11:00
upload_encoder_spec.rb FEATURE: extract text from ODT and ODS document uploads (#39711) 2026-05-05 12:04:13 -03:00
xls_to_text_spec.rb FEATURE: extract text from document uploads for LLM prompts (#39634) 2026-05-05 08:16:23 +10:00
xlsx_to_text_spec.rb FEATURE: extract text from document uploads for LLM prompts (#39634) 2026-05-05 08:16:23 +10:00
xml_tag_stripper_spec.rb
xml_tool_processor_spec.rb DEV: Enable Style/RedundantParentheses rubocop rule (#40095) 2026-05-19 15:48:09 +02:00