Skip to content

.Net: feat(connectors): Support ImageContent in tool/function results#13431

Merged
rogerbarreto merged 12 commits intomicrosoft:mainfrom
Cozmopolit:fix/multimodal-tool-results
May 7, 2026
Merged

.Net: feat(connectors): Support ImageContent in tool/function results#13431
rogerbarreto merged 12 commits intomicrosoft:mainfrom
Cozmopolit:fix/multimodal-tool-results

Conversation

@Cozmopolit
Copy link
Copy Markdown
Contributor

Motivation and Context

Fixes #13430

Currently, when a Semantic Kernel function returns ImageContent, it gets serialized to JSON - losing the binary image data and preventing multimodal-capable models from processing the image.

This PR enables ImageContent preservation in tool/function results, allowing connectors with multimodal capabilities (Gemini 3+, Anthropic) to pass images natively to the model. This is essential for agentic workflows where tools generate or process images that the model needs to analyze.

Description

FunctionCallsProcessor (shared infrastructure)

  • Changed ProcessFunctionResult() return type from string to object
  • Added early return for ImageContent to preserve it for multimodal-capable connectors
  • Added ImageContentNotSupportedErrorMessage constant for consistent error messaging

Google Gemini Connector (native support)

  • Extended FunctionResponsePart with Parts property for nested multimodal content
  • Added FunctionResponsePartContent class with InlineData support
  • Implemented CreateImageFunctionResponsePart() to convert ImageContent to Gemini's native inlineData format

OpenAI Connector (error handling)

  • Added ImageContent check with clear error message (API does not support images in tool results)

OpenAI Agents (error handling)

  • Added GetFunctionResultAsString() helper with ImageContent error handling

Amazon Bedrock Agents (error handling)

  • Added GetFunctionResultAsString() helper with ImageContent error handling

New Unit Tests

Test File
ItShouldPreserveImageContentWithoutSerialization FunctionCallsProcessorTests.cs
FromChatHistoryImageContentInToolResultCreatesInlineDataPart GeminiRequestTests.cs
FromChatHistoryImageContentWithoutDataThrowsInvalidOperationException GeminiRequestTests.cs
FromChatHistoryImageContentWithoutMimeTypeThrowsInvalidOperationException GeminiRequestTests.cs
VerifyAssistantMessageAdapterGetMessageWithImageContentInFunctionResult AssistantMessageFactoryTests.cs

Notes

  • No breaking changes: All changes are to internal APIs. The public FunctionResultContent.Result property is already object?.
  • Gemini version detection: The connector does not check the model version. If an older Gemini model does not support functionResponse.parts, the API will return an appropriate error.
  • URI-based ImageContent: Only ImageContent with binary data is supported. URI-based ImageContent will throw InvalidOperationException.
  • MistralAI: Out of scope - has its own ProcessFunctionResult implementation.
  • Related: Prepares infrastructure for PR .Net: feat: Add Anthropic Connector for Claude models #13419 (Anthropic Connector multimodal support)

Contribution Checklist

sk-pr-MultimodalToolResults.md

Enable ImageContent preservation in function results for multimodal-capable
connectors (Gemini 3+). Non-supporting connectors return clear error message.

Changes:
- FunctionCallsProcessor: Return object instead of string, preserve ImageContent
- Gemini: Native support via FunctionResponse.Parts with inlineData
- OpenAI/Bedrock Agents: Error handling with ImageContentNotSupportedErrorMessage

Includes 5 new unit tests for ImageContent handling.

Fixes microsoft#13430
@Cozmopolit Cozmopolit requested a review from a team as a code owner December 22, 2025 21:08
@moonbox3 moonbox3 added .NET Issue or Pull requests regarding .NET code kernel Issues or pull requests impacting the core kernel labels Dec 22, 2025
@github-actions github-actions Bot changed the title feat(connectors): Support ImageContent in tool/function results .Net: feat(connectors): Support ImageContent in tool/function results Dec 22, 2025
@markwallace-microsoft markwallace-microsoft self-assigned this Jan 14, 2026
@rogerbarreto
Copy link
Copy Markdown
Member

@Cozmopolit Thanks for the contribution!

With the latest merges into the Google connector this PR is now with a conflict, appreciate if you can take a look.

@Cozmopolit
Copy link
Copy Markdown
Contributor Author

@rogerbarreto

Done, test file conflict resolved.

@RamType0
Copy link
Copy Markdown
Contributor

Why is this staling?

Copilot AI review requested due to automatic review settings May 6, 2026 11:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the .NET function-calling infrastructure so that kernel functions returning ImageContent can be preserved as native multimodal content (instead of being JSON-serialized), enabling connectors like Gemini (3+) to forward image tool results to multimodal models while returning a clear error for connectors/APIs that don’t support multimodal tool outputs.

Changes:

  • Updated shared function-result processing to preserve ImageContent (changing ProcessFunctionResult from string to object) and introduced a shared “not supported” error message constant.
  • Extended the Google Gemini request model to emit functionResponse.parts[].inlineData for ImageContent tool results, with validation and unit tests.
  • Added OpenAI and agent-specific handling that converts ImageContent tool results into a clear error message (plus targeted unit tests).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
dotnet/src/SemanticKernel.UnitTests/Utilities/AIConnectors/FunctionCallsProcessorTests.cs Adds unit coverage verifying ImageContent is preserved rather than serialized.
dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs Preserves ImageContent in processed function results; introduces a shared “not supported” error message constant.
dotnet/src/Connectors/Connectors.OpenAI/Core/ClientCore.ChatCompletion.cs Detects ImageContent in tool results and emits a connector-friendly error message instead of attempting to pass images.
dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiRequest.cs Converts ImageContent tool results into Gemini’s native functionResponse.parts[].inlineData format with validation.
dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiPart.cs Extends Gemini function-response modeling to support nested multimodal parts (parts).
dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/GeminiRequestTests.cs Adds tests for inlineData generation and required validation failures (missing data/mime type).
dotnet/src/Agents/UnitTests/OpenAI/Internal/AssistantMessageFactoryTests.cs Adds a unit test ensuring OpenAI Assistants path returns the expected error text for ImageContent in function results.
dotnet/src/Agents/OpenAI/Internal/AssistantMessageFactory.cs Adds helper to convert function results to string while handling ImageContent as “not supported”.
dotnet/src/Agents/Bedrock/Extensions/BedrockAgentInvokeExtensions.cs Adds helper to convert function results to string while handling ImageContent as “not supported”.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Followup to PR microsoft#13431 closing the only validated regression introduced by the
shared FunctionCallsProcessor.ProcessFunctionResult signature change (string to
object) plus small housekeeping items.

Changes:
- ResponseThreadActions: add internal GetFunctionResultAsString helper that
  returns ImageContentNotSupportedErrorMessage for ImageContent results;
  replace both fr.Result?.ToString() call sites. Without this, ImageContent
  tool results would surface as the literal type name string under the
  OpenAI Responses API path.
- GeminiRequest: extract the synthetic functionResponse envelope to a named
  s_imageFunctionResponseEnvelope constant for testability and single-source
  maintenance.
- FunctionCallsProcessor: tighten ProcessFunctionResult XML doc to enumerate
  the three possible return categories (string, ImageContent, JSON serialized).
- GeminiRequestTests: fix CS8602 nullability warning that was failing the
  dotnet-build-and-test pipeline.
- Add ResponseThreadActionsTests covering ImageContent rejection, string
  passthrough, and null handling.
Locks in the new ImageContent rejection branch in CreateRequestMessages: a tool message whose FunctionResultContent.Result is ImageContent must be sent as ImageContentNotSupportedErrorMessage rather than serialized JSON or the type name.
@rogerbarreto rogerbarreto added this pull request to the merge queue May 7, 2026
@rogerbarreto rogerbarreto moved this from Community PR to Done in Agent Framework May 7, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 7, 2026
pull Bot pushed a commit to ehtick/semantic-kernel that referenced this pull request May 7, 2026
GHSA-pggp-6c3x-2xmx) (microsoft#13960)

### Motivation and Context

`MongoDB.Driver 3.5.2` transitively introduces `Snappier 1.0.0`, which
carries a high-severity vulnerability (GHSA-pggp-6c3x-2xmx): infinite
loop during SnappyStream decompression of malformed framed input. All
Snappier versions ≤ 1.3.0 are affected; 1.3.1 is the first patched
release. This was blocking the merge queue via NU1903.

### Description

- **`dotnet/Directory.Packages.props`** — Add `PackageVersion` entry
pinning `Snappier` to `1.3.1`.
- **`dotnet/src/VectorData/MongoDB/MongoDB.csproj`** — Add explicit
`PackageReference` for `Snappier` (versionless, resolved via CPM) so
NuGet treats it as a direct dependency at 1.3.1, overriding the
transitive 1.0.0 from `MongoDB.Driver`.
- **`dotnet/src/VectorData/CosmosMongoDB/CosmosMongoDB.csproj`** — Same
override for the CosmosMongoDB connector.

In NuGet's resolution algorithm, a direct reference at depth 1 wins over
a transitive reference at depth 2, so this cleanly forces 1.3.1 without
changing the `MongoDB.Driver` pin itself.

### Contribution Checklist

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

Please investigate a solution to snappier vulnerability fix (update
version) and propose a PR with the bump, ideally following the first
immediate a non breaking version bump

<analysis>
**Chronological Review:**
1. User asked to analyze PR microsoft#13431 (Gemini multimodal tool results in
microsoft/semantic-kernel) for gaps
2. Initial analysis identified ~11 gaps with broad scope including
OpenAI Assistants, AzureAI, Responses API, MistralAI
3. User invoked ouroboros skill, asked to reassess scoping to Google
package only
4. Reassessment: Google connector bypasses FunctionCallsProcessor; only
validated regression is OpenAI Responses API
5. User invoked ouroboros to seed the observation; created seed file
`pr-13431-followup.seed.yaml`
6. User asked to execute seed + identify pipeline error
7. Implemented 5 file changes; identified pipeline error CS8602 in
GeminiRequestTests.cs:809
8. All builds/tests passed; user asked about string→object impact in
plan mode
9. Investigation revealed `FunctionCallsProcessor` is `internal sealed`,
source-distributed, so blast radius small
10. User confirmed all implemented; ran CI-parity dotnet format via
WSL2+Docker (all pass)
11. User asked to commit and push - committed `de08bce99` and pushed to
Cozmopolit fork
12. User asked to check PR comments - found 2 Copilot bot review
comments
13. User said: add OpenAI test (item 1), reply out-of-scope (item 2)
14. Added OpenAI ChatCompletion test, committed `e9f27d21a`, pushed,
replied to both bot comments
15. User asked PR number (13431)
16. User invoked /auto pr_task - PR was green, no action needed
17. User said merge queue failed, asked to investigate
18. Investigation found: NU1903 Snappier 1.0.0 vulnerability blocking
merge queue; not caused by our PR
19. **Most recent: User asked "Do we have a fix in main for this
already?"**
20. **Investigation confirmed: NO fix in main** - origin/main HEAD
`1a5065e5c` unchanged, `MongoDB.Driver 3.5.2` still pinned, no Snappier
override, no PRs/issues for Snappier or NU1903 in the repo recently
21. **Offered to open a small fix PR pinning Snappier to a patched
version**

**Intent Mapping:**
- User wants PR microsoft#13431 fully landed
- User wants to understand whether merge queue failure is in scope
- User now considering whether to fix the Snappier vulnerability
separately

**Technical Inventory:**
- .NET 10.0, semantic-kernel repo
- WSL2 + Docker (mcr.microsoft.com/dotnet/sdk:10.0) for CI parity
- gh CLI for PR/CI operations
- Git remotes: origin=microsoft, roger=rogerbarreto fork,
cozmopolit=Cozmopolit fork (added)
- Branch: `fix/multimodal-tool-results`
- PR head fork: Cozmopolit, maintainerCanModify=true

**Code Archaeology:**
Files changed in commits `de08bce99` + `e9f27d21a`:
- `dotnet/src/Agents/OpenAI/Internal/ResponseThreadActions.cs` — added
GetFunctionResultAsString helper
-
`dotnet/src/Agents/UnitTests/OpenAI/Internal/ResponseThreadActionsTests.cs`
— new (3 tests)
-
`dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiRequest.cs`
— extracted s_imageFunctionResponseEnvelope
-
`dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/GeminiRequestTests.cs`
— CS8602 fix
-
`dotnet/src/InternalUtilities/connectors/AI/FunctionCalling/FunctionCallsProcessor.cs`
— XML-doc tightening
-
`dotnet/src/Connectors/Connectors.OpenAI.UnitTests/Services/OpenAIChatCompletionServiceTests.cs`
— added
ItSendsImageContentNotSupportedErrorWhenToolResultIsImageContentAsync

Identified vulnerable package: `MongoDB.Driver 3.5.2` in
`dotnet/Directory.Packages.props:173` brings in transitive `Snappier
1.0.0`

**Progress Assessment:**
- ✅ All planned implementation completed
- ✅ Both bot review comments replied to
- ✅ All 7 todos done
- ⚠️ PR cannot merge due to environmental Snappier vulnerability (not
our PR's fault)
- 🔲 User considering whether to open a separate Snappier fix PR

**Context Validation:**
- PR state: OPEN, MERGEABLE, CLEAN, APPROVED (markwallace-microsoft)
- HEAD SHA: `e9f27d21a85a8b97ee5f29619b23b82a03e313ff`
- All required CI checks pass on PR head
- Merge queue attempt failed at 2026-05-07T10:01:31, removed by
github-merge-queue[bot] at 10:08:05
- Failed merge_group run: 25489152862

**Recent Commands Analysis:**
Most recent two tool batches:
1. `git fetch origin main; git log origin/main
--since="2026-05-06T15:00:00Z" --oneline` → only `1a5065e5c`
(yesterday's commit)
2. `git show origin/main:dotnet/Directory.Packages.props | grep
Snappier|MongoDB.Driver` → only `MongoDB.Driver 3.5.2`, no Snappier
override
3. `gh search prs/issues --state all "Snappier"` → command failed
(invalid `all` state)
4. `gh api search/issues?q=Snappier+repo:microsoft/semantic-kernel` →
empty results
5. `gh api search/issues?q=NU1903+repo:microsoft/semantic-kernel` → 3
historical results from 2024, all unrelated

Final response: confirmed NO fix in main, offered to open a small fix PR
pinning Snappier.
</analysis>

<summary>
1. Conversation Overview:
   - Primary Objectives:
     - Analyze SK PR microsoft#13431 (G...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

Created from Copilot CLI via the copilot delegate command.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com>
@rogerbarreto rogerbarreto enabled auto-merge May 7, 2026 15:11
@rogerbarreto rogerbarreto moved this from Done to In Review in Agent Framework May 7, 2026
@rogerbarreto rogerbarreto added this pull request to the merge queue May 7, 2026
Merged via the queue into microsoft:main with commit b7ae840 May 7, 2026
18 checks passed
@github-project-automation github-project-automation Bot moved this from In Review to Done in Agent Framework May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kernel Issues or pull requests impacting the core kernel .NET Issue or Pull requests regarding .NET code

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

.Net: Bug: ImageContent in tool/function results is serialized to JSON instead of native format

8 participants