diff --git a/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/ChatClientIntegrationTests.cs b/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/ChatClientIntegrationTests.cs
index 0863e31db37..e9c2bd57d65 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/ChatClientIntegrationTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/ChatClientIntegrationTests.cs
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@@ -132,6 +133,27 @@ public virtual async Task CompleteStreamingAsync_UsageDataAvailable()
Assert.Equal(usage.Details.InputTokenCount + usage.Details.OutputTokenCount, usage.Details.TotalTokenCount);
}
+ protected virtual string? GetModel_MultiModal_DescribeImage() => null;
+
+ [ConditionalFact]
+ public virtual async Task MultiModal_DescribeImage()
+ {
+ SkipIfNotEnabled();
+
+ var response = await _chatClient.CompleteAsync(
+ [
+ new(ChatRole.User,
+ [
+ new TextContent("What does this logo say?"),
+ new ImageContent(GetImageDataUri()),
+ ])
+ ],
+ new() { ModelId = GetModel_MultiModal_DescribeImage() });
+
+ Assert.Single(response.Choices);
+ Assert.True(response.Message.Text?.IndexOf("net", StringComparison.OrdinalIgnoreCase) >= 0, response.Message.Text);
+ }
+
[ConditionalFact]
public virtual async Task FunctionInvocation_AutomaticallyInvokeFunction_Parameterless()
{
@@ -714,6 +736,15 @@ private enum JobType
Unknown,
}
+ private static Uri GetImageDataUri()
+ {
+ using Stream? s = typeof(ChatClientIntegrationTests).Assembly.GetManifestResourceStream("Microsoft.Extensions.AI.dotnet.png");
+ Assert.NotNull(s);
+ MemoryStream ms = new();
+ s.CopyTo(ms);
+ return new Uri($"data:image/png;base64,{Convert.ToBase64String(ms.ToArray())}");
+ }
+
[MemberNotNull(nameof(_chatClient))]
protected void SkipIfNotEnabled()
{
diff --git a/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/Microsoft.Extensions.AI.Integration.Tests.csproj b/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/Microsoft.Extensions.AI.Integration.Tests.csproj
index e38ccd3268b..04d9bc6d29f 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/Microsoft.Extensions.AI.Integration.Tests.csproj
+++ b/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/Microsoft.Extensions.AI.Integration.Tests.csproj
@@ -15,6 +15,10 @@
true
+
+
+
+
diff --git a/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/dotnet.png b/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/dotnet.png
new file mode 100644
index 00000000000..fb00ecf91e4
Binary files /dev/null and b/test/Libraries/Microsoft.Extensions.AI.Integration.Tests/dotnet.png differ
diff --git a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs
index 891378c0e86..ac941623124 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Ollama.Tests/OllamaChatClientIntegrationTests.cs
@@ -30,6 +30,8 @@ public override Task FunctionInvocation_RequireAny() =>
public override Task FunctionInvocation_RequireSpecific() =>
throw new SkipTestException("Ollama does not currently support requiring function invocation.");
+ protected override string? GetModel_MultiModal_DescribeImage() => "llava";
+
[ConditionalFact]
public async Task PromptBasedFunctionCalling_NoArgs()
{