Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,8 @@ private async Task<GeminiResponse> SendRequestAndReturnValidGeminiResponseAsync(
}

/// <summary>Checks if a tool call is for a function that was defined.</summary>
private static bool IsRequestableTool(IEnumerable<GeminiTool.FunctionDeclaration> functions, GeminiFunctionToolCall ftc)
=> functions.Any(geminiFunction =>
private static bool IsRequestableTool(IEnumerable<GeminiTool.FunctionDeclaration>? functions, GeminiFunctionToolCall ftc)
=> (functions ?? []).Any(geminiFunction =>
string.Equals(geminiFunction.Name, ftc.FullyQualifiedName, StringComparison.OrdinalIgnoreCase));

private void AddToolResponseMessage(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.

using System;

namespace Microsoft.SemanticKernel.Connectors.Google.Core.Gemini;
internal static class GeminiNativeToolExtensions
{
public static bool ValidateGeminiNativeTools(this GeminiNativeToolCallConfig? callConfig, GeminiRequest request)
{
if (callConfig is null)
{
return false;
}

bool hasAnyTool = callConfig.FileSearch is not null;
// ... || callConfig.Grounding is not null;

if (!hasAnyTool)
{
return false;
}

// Example: If FileSearch is present, check that its required fields adhere to the schema.
if (callConfig.FileSearch is not null)
{
// Placeholder for future format checking (e.g., regex against store names)
if (callConfig.FileSearch.FileReferences?.Count == 0)
{
// throw an exception or return false here.
throw new InvalidOperationException("You need to supply at least one FileReference for a FileSearch");
}
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Text.Json.Serialization.Metadata;
using Microsoft.Extensions.AI;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.Google.Core.Gemini;

namespace Microsoft.SemanticKernel.Connectors.Google.Core;

Expand Down Expand Up @@ -50,16 +51,23 @@ internal sealed class GeminiRequest
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IDictionary<string, string>? Labels { get; set; }

// These were added to keep a devide from native tools and functionDeclarations
// I am currently unsure if this is needed as currently you cannot have both enabled in a
// turn. Maybe this will be use in the future.
[JsonIgnore]
private GeminiTool? _functionDefinitionsBlook { get; set; }

public void AddFunction(GeminiFunction function)
{
// NOTE: Currently Gemini only supports one tool i.e. function calling.
// NOTE: Adding in additional tool types has forced me to keep a reference to the functionDeclarations
this.Tools ??= [];
if (this.Tools.Count == 0)
if (this._functionDefinitionsBlook is null)
{
this.Tools.Add(new GeminiTool());
this._functionDefinitionsBlook = new();
this.Tools.Add(this._functionDefinitionsBlook);
}

this.Tools[0].Functions.Add(function.ToFunctionDeclaration());
this._functionDefinitionsBlook.Functions ??= [];
this._functionDefinitionsBlook.Functions!.Add(function.ToFunctionDeclaration());
}

/// <summary>
Expand All @@ -76,6 +84,7 @@ public static GeminiRequest FromPromptAndExecutionSettings(
AddSafetySettings(executionSettings, obj);
AddConfiguration(executionSettings, obj);
AddAdditionalBodyFields(executionSettings, obj);
AddNativeTools(executionSettings, obj);
return obj;
}

Expand All @@ -93,6 +102,7 @@ public static GeminiRequest FromChatHistoryAndExecutionSettings(
AddSafetySettings(executionSettings, obj);
AddConfiguration(executionSettings, obj);
AddAdditionalBodyFields(executionSettings, obj);
AddNativeTools(executionSettings, obj);
return obj;
}

Expand Down Expand Up @@ -480,6 +490,27 @@ internal static JsonSerializerOptions GetDefaultOptions()
return s_options;
}

private static void AddNativeTools(GeminiPromptExecutionSettings executionSettings, GeminiRequest request)
{
if (!executionSettings.NativeToolCalls.ValidateGeminiNativeTools(request))
{
return;
}
// initialize Tools if needed
request.Tools ??= [];

// Add all the tools to the GeminiTool seperately
var settings = executionSettings.NativeToolCalls;
if (settings?.FileSearch is not null)
{
GeminiTool tool = new()
{
FileSearch = settings.FileSearch
};
request.Tools.Add(tool);
}
}

private static void AddSafetySettings(GeminiPromptExecutionSettings executionSettings, GeminiRequest request)
{
request.SafetySettings = executionSettings.SafetySettings?.Select(s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
using static Microsoft.SemanticKernel.Connectors.Google.GeminiNativeToolCallConfig;

namespace Microsoft.SemanticKernel.Connectors.Google.Core;

Expand All @@ -23,7 +24,15 @@ internal sealed class GeminiTool
/// a [FunctionResponse][content.part.function_response] with the [content.role] "function" generation context for the next model turn.
/// </remarks>
[JsonPropertyName("functionDeclarations")]
public IList<FunctionDeclaration> Functions { get; set; } = [];
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IList<FunctionDeclaration>? Functions { get; set; } = null;

/// <summary>
/// The specific configuration for FileSearch
/// </summary>
[JsonPropertyName("fileSearch")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public GeminiFileSearchNativeTool? FileSearch { get; set; } = null;

/// <summary>
/// Structured representation of a function declaration as defined by the OpenAPI 3.03 specification.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Text.Json.Serialization;
using Microsoft.SemanticKernel.Connectors.Google.Core;
using Microsoft.SemanticKernel.Connectors.Google.Core.Gemini;

namespace Microsoft.SemanticKernel.Connectors.Google;

/// <summary>
/// Represents the configuration for a Gemini native tool call, specifically for file search operations.
/// </summary>
/// <remarks>This class is used to configure the parameters for invoking a Gemini native tool that performs file
/// searches. It includes settings for specifying file references to be searched. Additional tool configuration can be
/// added here. NOTE: you will need to update both <see cref="GeminiTool"/> and AddNativeTools in <see cref="GeminiRequest"/>
/// as well as add validation to <see cref="GeminiNativeToolExtensions"/></remarks>
public class GeminiNativeToolCallConfig
{
/// <summary>
/// Gets or sets the file search tool configuration for the Gemini application.
/// </summary>
[JsonPropertyName("fileSearch")]
public GeminiFileSearchNativeTool? FileSearch { get; set; } = null;

/// <summary>
/// Represents a tool for searching files using the Gemini native interface.
/// </summary>
/// <remarks>This class is designed to work with the Gemini system to facilitate file searches by
/// maintaining a list of file references.
/// As the Google Connector as this moment doesn't support file uplaod or file search import
/// Operation, you will need to use a seperate library to upload or import files to FileSearch
/// </remarks>
/// <param name="fileReferences"></param>
public sealed class GeminiFileSearchNativeTool(IList<string> fileReferences)
{
/// <summary>
/// Gets or sets the list of file reference names used for searching.
/// </summary>
/// <remarks>The file reference is formated as "fileSearchStores/filenane" this should be retrieved from
/// response of the filesearch import operation. As the Google Connector as this moment doesn't support this
/// Operation, you will need to use a seperate library to upload or import files to FileSearch</remarks>
[JsonPropertyName("fileSearchStoreNames")]
public IList<string> FileReferences { get; set; } = fileReferences;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public sealed class GeminiPromptExecutionSettings : PromptExecutionSettings
private IDictionary<string, string>? _labels;
private IList<GeminiSafetySetting>? _safetySettings;
private GeminiToolCallBehavior? _toolCallBehavior;
private GeminiNativeToolCallConfig? _nativeToolCallConfig;
private GeminiThinkingConfig? _thinkingConfig;

/// <summary>
Expand Down Expand Up @@ -199,6 +200,21 @@ public GeminiToolCallBehavior? ToolCallBehavior
}
}

/// <summary>
/// Gets or sets the configuration for native tool calls.
/// </summary>
/// <remarks>Modifying this property will throw an exception if the object is in a frozen state.</remarks>
public GeminiNativeToolCallConfig? NativeToolCalls
{
get => this._nativeToolCallConfig;

set
{
this.ThrowIfFrozen();
this._nativeToolCallConfig = value;
}
}

/// <summary>
/// Indicates if the audio response should include timestamps.
/// if enabled, audio timestamp will be included in the request to the model.
Expand Down
Loading