Skip to content

feat: bedrock - support Titan image multimodal embedding model#3637

Open
serhiimeverc wants to merge 7 commits into
maximhq:devfrom
evercompliant:fix/bedrock-titan-multimodal-embeddings
Open

feat: bedrock - support Titan image multimodal embedding model#3637
serhiimeverc wants to merge 7 commits into
maximhq:devfrom
evercompliant:fix/bedrock-titan-multimodal-embeddings

Conversation

@serhiimeverc
Copy link
Copy Markdown

Summary

Bedrock embedding model detection only recognized Titan text and Cohere, causing the Titan image embedding model to be rejected as unsupported. Additionally, the existing inputImage passthrough was carried in ExtraParams (tagged json:"-") and only reached the wire body when the caller set BifrostContextKeyPassthroughExtraParams — which the Bedrock provider never sets — so image-only requests silently shipped without inputImage to AWS.

This PR extends model-type routing so Titan image embeddings reach the Titan request path, promotes inputImage to a first-class field so the wire body always includes it, and adjusts validation so image-only requests are accepted.

Changes

  • Model routing updates (embedding.go)

    • DetermineEmbeddingModelType now maps amazon.titan-embed-image*titan.
  • inputImage promoted to first-class field (types.go, embedding.go)

    • New InputImage field with json:"inputImage,omitempty" on BedrockTitanEmbeddingRequest.
    • InputText switched to omitempty (image-only requests legitimately have no text).
    • Converter lifts Params.ExtraParams["inputImage"] into the typed field when it's a string. Non-string values stay in ExtraParams for backward-safe passthrough.
    • Lifted key is excluded from the ExtraParams forwarding loop alongside normalize.
  • Validation accepts image-only requests (embedding.go)

    • Previous early Input == nil guard rejected Params.ExtraParams["inputImage"] before the hasImage check could run.
    • New guard requires text OR image, with nil-safe access to Input.Text / Input.Texts.
  • Targeted unit coverage (embedding_test.go)

    • DetermineEmbeddingModelType: titan text / titan image / cohere / unsupported.
    • TestToBedrockTitanEmbeddingRequest_ImageOnly: empty Input + image produces wire body with inputImage and without inputText.
    • TestToBedrockTitanEmbeddingRequest_TextAndImage: both fields appear in the wire body.
    • TestToBedrockTitanEmbeddingRequestLiftsInputImageAndNormalize: both ExtraParams keys are lifted to typed fields.
    • TestToBedrockTitanEmbeddingRequest_NonStringInputImage: non-string values stay in ExtraParams (backward-safe passthrough).
    • TestToBedrockTitanEmbeddingRequest_NilInputWithImage: nil Input + image succeeds with the expected wire body.
    • TestToBedrockTitanEmbeddingRequest_NilInputWithoutImage / _RejectsNoInput: unified error for no-input cases.
switch {
case strings.Contains(model, "amazon.titan-embed-text"):
    return "titan", nil
case strings.Contains(model, "cohere.embed"):
    return "cohere", nil
case strings.Contains(model, "amazon.titan-embed-image"):
    return "titan", nil
}

Notes

  • Wire format is byte-identical for all previously-successful callers; text-only requests still produce {"inputText":"..."} exactly as before.
  • The whitespace-only change in BedrockToolUse (struct tag column re-alignment) is a gofmt side-effect of adding the longer InputImage comment in the same file — column alignment is context-sensitive. Reverting would re-introduce a gofmt -l failure.
  • Nova multimodal embeddings (amazon.nova-*-multimodal-embeddings-v1:0) are intentionally out of scope — they use a messages-array envelope (similar to Converse), not the flat Titan shape, so they need their own request struct, converter, and response parser. Tracked separately.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

cd core
go test ./providers/bedrock/ -run 'TestToBedrockTitanEmbedding|TestDetermineEmbeddingModelType|TestToBedrockCohereEmbeddingRequest' -v

Expected: all cases pass, including titan image routing, image-only request acceptance, nil-Input handling, and inputImage first-class field lifting.

Breaking changes

  • Yes
  • No

Related issues

Closes #3632.

Security considerations

No new auth surfaces or secret handling changes. Change is limited to model-name routing, request serialization, and unit coverage.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

…dels

Promotes inputImage to a first-class field on BedrockTitanEmbeddingRequest
and routes amazon.titan-embed-image-* and amazon.nova-2-multimodal-embeddings-*
through the Titan converter. Accepts image-only requests when Input is nil.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Titan multimodal embedding support: makes Bedrock Titan embedding accept optional text or base64 image input, recognizes amazon.titan-embed-image models, implements presence validation and inputImage lifting/filtering in request conversion, and adds tests covering new behaviors.

Changes

Bedrock Titan Multimodal Embedding

Layer / File(s) Summary
Titan Multimodal Request Type
core/providers/bedrock/types.go
BedrockTitanEmbeddingRequest.InputText is now optional (omitempty) and a new optional InputImage field (base64-encoded) is added to support image-only and combined text+image embedding requests.
Model Recognition for Titan Image
core/providers/bedrock/embedding.go
DetermineEmbeddingModelType recognizes amazon.titan-embed-image model names and maps them to the titan model type.
Multimodal Embedding Request Conversion
core/providers/bedrock/embedding.go
ToBedrockTitanEmbeddingRequest validates presence of text or image, sets InputText only when text exists, lifts string inputImage from ExtraParams into InputImage when appropriate, and filters ExtraParams to always exclude normalize and to exclude inputImage only when it was lifted. Includes a cosmetic spacing tweak in a Cohere struct comment.
Multimodal Embedding Test Suite
core/providers/bedrock/embedding_test.go
Adds tests for DetermineEmbeddingModelType (titan text, titan image, cohere, unsupported), and extensive ToBedrockTitanEmbeddingRequest tests covering image-only, text+image, missing-input validation, inputImage/normalize lifting and removal from ExtraParams, non-string inputImage passthrough, and nil Input/Params cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit found a payload bright,

Text and images both take flight,
Fields now optional, checks in place,
Titan answers with a brimming grace,
Hopping through tests with joyous pace.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive, covering all required template sections including summary, changes, type, affected areas, test instructions, related issues, and security considerations.
Linked Issues check ✅ Passed All objectives from issue #3632 are addressed: model routing for Titan image models, inputImage promoted to first-class field, validation accepts image-only requests, backward-compatible passthrough maintained, and comprehensive test coverage added.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to the linked issue #3632 objectives. The minor gofmt-driven whitespace change in BedrockToolUse is explicitly documented as a necessary side-effect.
Title check ✅ Passed The title directly and concisely summarizes the main change: adding support for Titan image multimodal embedding models in Bedrock, which is exactly what the changeset implements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@serhiimeverc serhiimeverc marked this pull request as ready for review May 20, 2026 15:34
@serhiimeverc
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Confidence Score: 5/5

Safe to merge — changes are narrowly scoped to Titan image embedding routing and serialization with no risk to existing text-only or Cohere paths.

The routing addition is a simple string-contains check that returns the same 'titan' value as the text case, leaving all previously working paths untouched. The inputImage promotion is wire-compatible: text-only callers still get {"inputText":"..."} because omitempty only drops the zero value, and image-only callers now correctly get {"inputImage":"..."} instead of an empty body. Validation strictly requires a non-empty string before accepting image input, preventing the silent empty-body scenario. Test coverage is thorough across all edge cases.

No files require special attention.

Important Files Changed

Filename Overview
core/providers/bedrock/embedding.go Adds amazon.titan-embed-image routing to DetermineEmbeddingModelType and promotes inputImage to a first-class field; validation correctly requires a non-empty string before accepting image-only requests.
core/providers/bedrock/types.go Adds InputImage string json:"inputImage,omitempty" to BedrockTitanEmbeddingRequest and switches InputText to omitempty; cosmetic gofmt struct-tag alignment change to BedrockToolUse.
core/providers/bedrock/embedding_test.go Adds comprehensive test coverage: model-type routing, image-only, text+image, nil-Input, non-string rejection, empty-string rejection, and ExtraParams lifting/passthrough scenarios.

Reviews (2): Last reviewed commit: "[fix] core/bedrock - require non-empty s..." | Re-trigger Greptile

Comment thread core/providers/bedrock/embedding.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/providers/bedrock/embedding.go`:
- Around line 17-24: The current validation treats inputImage as present if the
key exists in bifrostReq.Params.ExtraParams but later only accepts string
values, which can let non-usable inputs pass; update the checks around
bifrostReq.Params.ExtraParams["inputImage"] (and the subsequent extraction logic
referenced in the same file) to require the value is a non-empty string (type
assert to string and ensure len>0) before setting hasImage or using it as the
Titan input, and treat any missing, non-string, or empty-string value as absent
so the "no input text or image provided for embedding" error is correctly
raised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 32977117-e702-49cc-be50-e63fe5c90470

📥 Commits

Reviewing files that changed from the base of the PR and between 8596f7a and af5246d.

📒 Files selected for processing (3)
  • core/providers/bedrock/embedding.go
  • core/providers/bedrock/embedding_test.go
  • core/providers/bedrock/types.go

Comment thread core/providers/bedrock/embedding.go
Greptile/CodeRabbit P1: hasImage was set on key existence alone, but the
lifting code only accepts string values. A non-string or empty inputImage
with no text passed validation, then produced an empty {} wire body to
AWS instead of a clean Bifrost-level error.

Type-assert string and require non-empty at the validation point so
non-string/empty values are treated as absent, matching the lifting
contract. Adds tests for image-only + non-string and image-only +
empty-string rejection paths.
@serhiimeverc serhiimeverc changed the title [feat]: bedrock - support Titan image multimodal embedding model feat: bedrock - support Titan image multimodal embedding model May 20, 2026
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.

[bug]: bedrock - Titan multimodal embedding model rejected as unsupported

1 participant