Skip to content

Added Image Support in SortedAgents - #330

Merged
sanskaraggarwal2025 merged 9 commits into
mainfrom
sortedagents-multimodal
May 30, 2026
Merged

sanskaraggarwal2025 merged 9 commits into
mainfrom
sortedagents-multimodal

Conversation

@sanskaraggarwal2025

@sanskaraggarwal2025 sanskaraggarwal2025 commented May 25, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Multimodal support (text + image parts) in agentic chat messages.
  • Refactor

    • Agent message handling upgraded to use structured message bodies across chat flows and streaming.
  • Tests

    • Updated tests to exercise the new structured message format and content handling.
  • Documentation

    • Docs updated to describe image support and structured message payloads.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@sanskaraggarwal2025, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 53 minutes and 54 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c804704-7418-46b4-9f5a-67d5a1fbbf0f

📥 Commits

Reviewing files that changed from the base of the PR and between 6fc4699 and 2c53add.

📒 Files selected for processing (1)
  • backend/chatservice/service/agentic_chat.go
📝 Walkthrough

Walkthrough

The PR refactors agentic chat message handling to use a structured MessageContent interface instead of plain strings, enabling multimodal support (text and images). The Message.Content field changes from string to the new MessageContent interface, with concrete types TextContent and ContentParts for composite content. All message construction sites are updated to wrap content with TextContent(...), and new helpers normalize messages and convert content back to text for output.

Changes

Structured Message Content for Multimodal Agentic Chat

Layer / File(s) Summary
Message Content Model & Type Contracts
backend/chatservice/sortedagents/llm.go
Message.Content refactored from string to MessageContent interface. New types TextContent, ContentParts, ContentPart, and ImageURLPart enable composite message bodies with text and optional image URLs.
Runner Interface & Message Normalization Helpers
backend/chatservice/sortedagents/runner.go
Runner.Run() and RunStream() now accept Message instead of string. Helpers normalizeInputMessage, contentToFlatString, and contentPartsToFlatString convert and normalize content throughout execution. All message construction wraps content with TextContent(...).
Service Layer: Content Building & Session Management
backend/chatservice/service/agentic_chat.go
New buildSortedAgentsMessage helper converts nil, string, and []types.ContentPart inputs into structured sortedagents.Message content, including image URL mapping. buildAgenticSession parses JSON-encoded content arrays and image data from chat history, then constructs structured messages. runAgenticChat now builds and passes structured user content to the runner.
Chat Service Wiring
backend/chatservice/service/service.go
ChatService.Chat passes currentMessageContent (structured content with enhanced prompts and multimodal parts) to runAgenticChat, replacing the text-only currentUserPrompt variable.
Agent API Integration
backend/chatservice/api/agent.go
runAgentWithCallbacks wraps system prompts and user messages with TextContent. convertDBMessageToSortedAgentsMessage wraps all text, tool result, and default message content with TextContent to align database records with the structured model.
Test Updates & Documentation
backend/chatservice/sortedagents/agent_test.go, runner_test.go, session_test.go, test_build.go, docs/features/agentic_chat.md
All test call sites updated to use Message{Role: "user", Content: TextContent(...)} instead of plain strings. Assertions convert content via contentToFlatString(...) before comparison. Documentation explains the MessageContent interface change and how LLM payloads switch between plain text and structured content parts based on image presence.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • sortedstartup/sortedchat#179: Adds multi-modal content-parts plumbing (content_image / contents) that this PR’s parsing and ContentParts support directly complements.
  • sortedstartup/sortedchat#324: Overlaps in agentic chat flow changes; both modify agentic_chat.go wiring though #324 focuses on the Brave web-search tool/session.
  • sortedstartup/sortedchat#327: Related edits to chat-history preprocessing and input message construction that may intersect with this PR’s structured-content logic.

Suggested reviewers

  • xask00
  • janakiramg

Poem

🐰 Once a prompt was only text and plain,
Now parts and pictures hop into the frame.
I nibble on bytes and stitch them neat,
Text and image together — what a treat! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% 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 Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Added Image Support in SortedAgents' directly aligns with the main change: refactoring the Message.Content field from string to MessageContent interface to enable structured multimodal content handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sortedagents-multimodal

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.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces agentic chat capabilities, allowing models with tool-calling support to perform web searches via Brave Search and web scraping via Cloudflare. Key changes include the implementation of a new agentic chat flow, the addition of BraveSearchTool and BrowserScrapeTool, and updates to the DAO layer to track search-related costs. The sortedagents package was refactored to support structured message content and token usage tracking. Feedback identifies a type mismatch in the agentic session builder that could break conversational context, suggests reorganizing service helper functions, and points out several inconsistencies in the design documentation regarding current implementation details like cost tracking and image support.

Comment thread docs/design decisions/WebSearch_tool.md Resolved

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/chatservice/sortedagents/llm.go (1)

25-55: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix JSON decoding for Message.Content (MessageContent interface)

backend/chatservice/sortedagents/llm.go decodes ChatResponse directly into Message, but Message.Content is a non-empty interface and there’s no UnmarshalJSON for Message, so decoding will fail when the API returns content as a string, array, or null.

🛠️ Suggested fix
 type Message struct {
 	Role string `json:"role"`
 
 	Content MessageContent `json:"content"`
@@
 	ExtraContent *ExtraContent `json:"extra_content,omitempty"`
 }
+
+func (m *Message) UnmarshalJSON(data []byte) error {
+	var raw struct {
+		Role         string          `json:"role"`
+		Content      json.RawMessage `json:"content"`
+		ToolCallID   string          `json:"tool_call_id,omitempty"`
+		ToolCalls    []ToolCall      `json:"tool_calls,omitempty"`
+		ExtraContent *ExtraContent   `json:"extra_content,omitempty"`
+	}
+	if err := json.Unmarshal(data, &raw); err != nil {
+		return err
+	}
+
+	*m = Message{
+		Role:         raw.Role,
+		ToolCallID:   raw.ToolCallID,
+		ToolCalls:    raw.ToolCalls,
+		ExtraContent: raw.ExtraContent,
+	}
+
+	switch {
+	case len(raw.Content) == 0 || bytes.Equal(raw.Content, []byte("null")):
+		m.Content = nil
+	case raw.Content[0] == '"':
+		var text string
+		if err := json.Unmarshal(raw.Content, &text); err != nil {
+			return err
+		}
+		m.Content = TextContent(text)
+	case raw.Content[0] == '[':
+		var parts ContentParts
+		if err := json.Unmarshal(raw.Content, &parts); err != nil {
+			return err
+		}
+		m.Content = parts
+	default:
+		return fmt.Errorf("unsupported message content shape: %s", raw.Content)
+	}
+
+	return nil
+}
🤖 Prompt for 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.

In `@backend/chatservice/sortedagents/llm.go` around lines 25 - 55, The JSON
decoder fails because Message.Content is an interface (MessageContent) with no
custom unmarshaler; add an UnmarshalJSON implementation that parses the raw
object, decodes all normal fields, and handles the "content" RawMessage
specially: if null set Message.Content = nil, if it decodes as a string set
Message.Content = TextContent(value), otherwise try decoding as []ContentPart
and set Message.Content = ContentParts(parts). Implement this on *Message
(referencing the Message type, MessageContent, TextContent and ContentParts) so
the decoder can accept "content" as string, array, or null while preserving the
rest of Message fields.
backend/chatservice/service/service.go (1)

460-471: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Image-bearing requests still bypass the agentic path.

runAgenticChat now accepts structured content, but this !hasImages guard means multimodal requests never reach it. Users with tool-calling + image-capable models still lose the sortedagents/web-search flow whenever they attach an image, so the new multimodal wiring is effectively dead for the target case.

Suggested fix
-	if !hasImages && capabilities.GetSupportToolCalling() && providerSettings != nil && strings.TrimSpace(providerSettings.ApiUrl) != "" {
+	if capabilities.GetSupportToolCalling() && providerSettings != nil && strings.TrimSpace(providerSettings.ApiUrl) != "" {
🤖 Prompt for 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.

In `@backend/chatservice/service/service.go` around lines 460 - 471, The current
conditional prevents agentic flow for image-bearing requests because of the
"!hasImages" guard; remove that "!hasImages" check so that when
capabilities.GetSupportToolCalling() is true, providerSettings.ApiUrl is set,
and webSearchSettings.APIKey exists the code calls runAgenticChat regardless of
hasImages (so multimodal requests can use the agentic/sorted-agents + web-search
path); ensure runAgenticChat invocation remains the same and that any downstream
handling of structured/multimodal content inside runAgenticChat is used for
images.
🤖 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 `@backend/chatservice/service/agentic_chat.go`:
- Around line 90-99: The reconstruction currently concatenates text parts then
image parts (using msg.ContentImage -> imageContents and then appending contents
to buildSortedAgentsMessage), which scrambles the original multimodal ordering;
modify the restore logic in agentic_chat.go so you parse per-part metadata
(e.g., include an index/sequence field in MessageContent or a single ordered
parts array) and merge text and image parts by that sequence before calling
buildSortedAgentsMessage; specifically, update how msg.ContentImage is
unmarshaled (MessageContent items should carry order IDs) and change the
contents assembly to sort/merge by that order so messages and
buildSortedAgentsMessage receive the original interleaved part sequence.
- Around line 77-83: The current logic in the message parsing block (checking
strings.HasPrefix/HasSuffix, calling json.Unmarshal into []*pb.MessageContent
and returning an error on unmarshal) treats any bracketed msg.Content as
serialized content and fails the session build on parse error; instead, catch
the json.Unmarshal error and fall back to treating msg.Content as a single plain
text part by appending one pb.MessageContent with Content set to msg.Content to
the contents slice (rather than returning fmt.Errorf), so
bracketed-but-not-serialized strings like "[1, 2, 3]" are replayed as raw text;
update the code around json.Unmarshal and contents append to implement this
fallback handling in the same function/block.

In `@docs/features/agentic_chat.md`:
- Around line 74-91: Update the docs to remove or reword the stale "unsupported"
statements about images in sortedagents and instead state that MessageContent
now supports images via the ContentParts/ContentPart and ImageURL types;
specifically, replace assertions like "We do not currently support images in
sortedagents" and "`sortedagents` does not yet support image input" and the
bullet "Add image support in `sortedagents`" with a concise note that
MessageContent is an interface implemented by TextContent and ContentParts and
that images are represented via ContentPart.Type == "image_url" with an
ImageURL; also fix the fenced code block to specify Go (use ```go) for the
ContentPart/ImageURL example.

---

Outside diff comments:
In `@backend/chatservice/service/service.go`:
- Around line 460-471: The current conditional prevents agentic flow for
image-bearing requests because of the "!hasImages" guard; remove that
"!hasImages" check so that when capabilities.GetSupportToolCalling() is true,
providerSettings.ApiUrl is set, and webSearchSettings.APIKey exists the code
calls runAgenticChat regardless of hasImages (so multimodal requests can use the
agentic/sorted-agents + web-search path); ensure runAgenticChat invocation
remains the same and that any downstream handling of structured/multimodal
content inside runAgenticChat is used for images.

In `@backend/chatservice/sortedagents/llm.go`:
- Around line 25-55: The JSON decoder fails because Message.Content is an
interface (MessageContent) with no custom unmarshaler; add an UnmarshalJSON
implementation that parses the raw object, decodes all normal fields, and
handles the "content" RawMessage specially: if null set Message.Content = nil,
if it decodes as a string set Message.Content = TextContent(value), otherwise
try decoding as []ContentPart and set Message.Content = ContentParts(parts).
Implement this on *Message (referencing the Message type, MessageContent,
TextContent and ContentParts) so the decoder can accept "content" as string,
array, or null while preserving the rest of Message fields.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f7434622-7b71-4508-afc2-82be7a824d3f

📥 Commits

Reviewing files that changed from the base of the PR and between 460b842 and 8ba99e3.

📒 Files selected for processing (10)
  • backend/chatservice/api/agent.go
  • backend/chatservice/service/agentic_chat.go
  • backend/chatservice/service/service.go
  • backend/chatservice/sortedagents/agent_test.go
  • backend/chatservice/sortedagents/llm.go
  • backend/chatservice/sortedagents/runner.go
  • backend/chatservice/sortedagents/runner_test.go
  • backend/chatservice/sortedagents/session_test.go
  • backend/chatservice/sortedagents/test_build.go
  • docs/features/agentic_chat.md

Comment thread docs/features/agentic_chat.md Resolved
Comment thread docs/features/agentic_chat.md Resolved

@coderabbitai coderabbitai Bot 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.

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 `@docs/features/agentic_chat.md`:
- Line 11: The sentence containing the phrase "websearch api key" should be
updated to use proper product/API capitalization and spacing—replace "websearch
api key" with "web search API key" so the sentence reads: "If the current
message supports tool calling, web search API key is present, and provider
settings are set, we route the request through the agentic chat flow." Locate
the sentence with the exact phrase "websearch api key" and apply this
replacement.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f53cbca2-a013-45c3-a248-8186b0ddaaeb

📥 Commits

Reviewing files that changed from the base of the PR and between 8ba99e3 and 6fc4699.

📒 Files selected for processing (6)
  • backend/chatservice/service/agentic_chat.go
  • backend/chatservice/service/service.go
  • backend/chatservice/sortedagents/runner.go
  • backend/chatservice/sortedagents/runner_test.go
  • backend/chatservice/sortedagents/session_test.go
  • docs/features/agentic_chat.md
💤 Files with no reviewable changes (1)
  • backend/chatservice/service/service.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/chatservice/sortedagents/runner_test.go
  • backend/chatservice/sortedagents/session_test.go
  • backend/chatservice/service/agentic_chat.go

Comment thread docs/features/agentic_chat.md Resolved
@sanskaraggarwal2025
sanskaraggarwal2025 merged commit 126717a into main May 30, 2026
6 checks passed
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.

2 participants