Skip to content

Commit

Permalink
add Grounding with Google Search
Browse files Browse the repository at this point in the history
  • Loading branch information
jochenkirstaetter committed Nov 1, 2024
1 parent beca6ce commit 2d82227
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 3 deletions.
22 changes: 22 additions & 0 deletions src/Mscc.GenerativeAI/Enums/DynamicRetrievalConfigMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#if NET472_OR_GREATER || NETSTANDARD2_0
using System.Text.Json.Serialization;
#endif

namespace Mscc.GenerativeAI
{
/// <summary>
/// The mode of the predictor to be used in dynamic retrieval.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<DynamicRetrievalConfigMode>))]
public enum DynamicRetrievalConfigMode
{
/// <summary>
/// Always trigger retrieval.
/// </summary>
ModeUnspecified = 0,
/// <summary>
/// Run retrieval only when system decides it is necessary.
/// </summary>
ModeDynamic
}
}
4 changes: 3 additions & 1 deletion src/Mscc.GenerativeAI/Types/Generative/Candidate.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
#if NET472_OR_GREATER || NETSTANDARD2_0
using System.Collections.Generic;
#endif

namespace Mscc.GenerativeAI
{
Expand Down
17 changes: 17 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/DynamicRetrievalConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Mscc.GenerativeAI
{
/// <summary>
/// Describes the options to customize dynamic retrieval.
/// </summary>
public class DynamicRetrievalConfig
{
/// <summary>
/// The mode of the predictor to be used in dynamic retrieval.
/// </summary>
public DynamicRetrievalConfigMode? Mode { get; set; }
/// <summary>
/// The threshold to be used in dynamic retrieval. If not set, a system default value is used.
/// </summary>
public float? DynamicThreshold { get; set; }
}
}
23 changes: 23 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/GoogleSearchRetrieval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,33 @@ namespace Mscc.GenerativeAI
/// </summary>
public class GoogleSearchRetrieval
{
/// <summary>
/// Specifies the dynamic retrieval configuration for the given source.
/// </summary>
public DynamicRetrievalConfig? DynamicRetrievalConfig { get; set; }
/// <summary>
/// Optional. Disable using the result from this tool in detecting grounding attribution.
/// </summary>
/// <remarks>This does not affect how the result is given to the model for generation.</remarks>
public bool? DisableAttribution { get; set; }

/// <summary>
/// Creates an instance of <see cref="GoogleSearchRetrieval"/>
/// </summary>
public GoogleSearchRetrieval() { }

/// <summary>
/// Creates an instance of <see cref="GoogleSearchRetrieval"/> with Mode and DynamicThreshold.
/// </summary>
/// <param name="mode">The mode of the predictor to be used in dynamic retrieval.</param>
/// <param name="dynamicThreshold">The threshold to be used in dynamic retrieval. If not set, a system default value is used.</param>
public GoogleSearchRetrieval(DynamicRetrievalConfigMode mode, float dynamicThreshold)
{
DynamicRetrievalConfig = new DynamicRetrievalConfig
{
Mode = mode,
DynamicThreshold = dynamicThreshold
};
}
}
}
13 changes: 13 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/GroundingChunk.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Mscc.GenerativeAI
{
/// <summary>
/// Grounding chunk.
/// </summary>
public class GroundingChunk
{
/// <summary>
/// Grounding chunk from the web.
/// </summary>
public Web? Web { get; set; }
}
}
29 changes: 28 additions & 1 deletion src/Mscc.GenerativeAI/Types/Generative/GroundingMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
using System.Collections.Generic;
#if NET472_OR_GREATER || NETSTANDARD2_0
using System.Collections.Generic;
#endif

namespace Mscc.GenerativeAI
{
/// <summary>
/// Metadata returned to client when grounding is enabled.
/// </summary>
public class GroundingMetadata
{
/// <summary>
/// Optional. Google search entry for the following-up web searches.
/// </summary>
public SearchEntryPoint? SearchEntryPoint { get; set; }
/// <summary>
///
/// </summary>
public List<GroundingAttribution>? GroundingAttributions { get; set; }
/// <summary>
/// Web search queries for the following-up web search.
/// </summary>
public List<string>? WebSearchQueries { get; set; }
/// <summary>
/// List of grounding support.
/// </summary>
public List<GroundingSupport>? GroundingSupports { get; set; }
/// <summary>
/// Metadata related to retrieval in the grounding flow.
/// </summary>
public RetrievalMetadata? RetrievalMetadata { get; set; }
/// <summary>
/// List of supporting references retrieved from specified grounding source.
/// </summary>
public List<GroundingChunk>? GroundingChunks { get; set; }
}
}
29 changes: 29 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/GroundingSupport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#if NET472_OR_GREATER || NETSTANDARD2_0
using System.Collections.Generic;
#endif

namespace Mscc.GenerativeAI
{
/// <summary>
/// Grounding support.
/// </summary>
public class GroundingSupport
{
/// <summary>
/// Segment of the content this support belongs to.
/// </summary>
public Segment? Segment { get; set; }
/// <summary>
/// A list of indices (into 'grounding_chunk') specifying the citations associated with the claim.
/// </summary>
/// <remarks>
/// For instance [1,3,4] means that grounding_chunk[1], grounding_chunk[3], grounding_chunk[4] are the retrieved content attributed to the claim.
/// </remarks>
public List<int>? GroundingChunkIndices { get; set; }
/// <summary>
/// Confidence score of the support references. Ranges from 0 to 1. 1 is the most confident.
/// This list must have the same size as the grounding_chunk_indices.
/// </summary>
public List<float>? ConfidenceScores { get; set; }
}
}
18 changes: 18 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/RetrievalMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Mscc.GenerativeAI
{
/// <summary>
/// Metadata related to retrieval in the grounding flow.
/// </summary>
public class RetrievalMetadata
{
/// <summary>
/// Optional. Score indicating how likely information from google search could help answer the prompt.
/// </summary>
/// <remarks>
/// The score is in the range [0, 1], where 0 is the least likely and 1 is the most likely.
/// This score is only populated when google search grounding and dynamic retrieval is enabled.
/// It will be compared to the threshold to determine whether to trigger google search.
/// </remarks>
public float? GoogleSearchDynamicRetrievalScore { get; set; }
}
}
17 changes: 17 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/SearchEntryPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Mscc.GenerativeAI
{
/// <summary>
/// Google search entry point.
/// </summary>
public class SearchEntryPoint
{
/// <summary>
/// Optional. Web content snippet that can be embedded in a web page or an app webview.
/// </summary>
public string? RenderedContent { get; set; }
/// <summary>
/// Optional. Base64 encoded JSON representing array of tuple.
/// </summary>
public byte[]? SdkBlob { get; set; }
}
}
25 changes: 25 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/Segment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Mscc.GenerativeAI
{
/// <summary>
/// Segment of the content.
/// </summary>
public class Segment
{
/// <summary>
/// Output only. The text corresponding to the segment from the response.
/// </summary>
public string? Text { get; set; }
/// <summary>
/// Output only. Start index in the given Part, measured in bytes. Offset from the start of the Part, inclusive, starting at zero.
/// </summary>
public int? StartIndex { get; set; }
/// <summary>
/// Output only. The index of a Part object within its parent Content object.
/// </summary>
public int? PartIndex { get; set; }
/// <summary>
/// Output only. End index in the given Part, measured in bytes. Offset from the start of the Part, exclusive, starting at zero.
/// </summary>
public int? EndIndex { get; set; }
}
}
17 changes: 17 additions & 0 deletions src/Mscc.GenerativeAI/Types/Generative/Web.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Mscc.GenerativeAI
{
/// <summary>
/// Chunk from the web.
/// </summary>
public class Web
{
/// <summary>
/// URI reference of the chunk.
/// </summary>
public string? Uri { get; set; }
/// <summary>
/// Title of the chunk.
/// </summary>
public string? Title { get; set; }
}
}
68 changes: 67 additions & 1 deletion tests/Mscc.GenerativeAI/GoogleAi_GeminiPro_Should.cs
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ public async Task Start_Chat_Streaming()

[Fact]
// Ref: https://ai.google.dev/api/generate-content#code-execution
public async Task Code_Execution()
public async Task Generate_Content_Code_Execution()
{
// Arrange
var prompt = "What is the sum of the first 50 prime numbers?";
Expand All @@ -791,6 +791,72 @@ public async Task Code_Execution()
// .Where(t => !string.IsNullOrEmpty(t))
.ToArray()));
}

[Fact]
// Ref: https://ai.google.dev/gemini-api/docs/grounding
public async Task Generate_Content_Grounding_Search()
{
// Arrange
var prompt = "Who won Wimbledon this year?";
var genAi = new GoogleAI(_fixture.ApiKey);
var model = genAi.GenerativeModel("gemini-1.5-pro-002",
tools: [new Tool { GoogleSearchRetrieval = new() }]);

// Act
var response = await model.GenerateContent(prompt);

// Assert
response.Should().NotBeNull();
response.Candidates.Should().NotBeNull().And.HaveCount(1);
response.Candidates![0].GroundingMetadata.Should().NotBeNull();
response.Candidates![0].GroundingMetadata!.SearchEntryPoint.Should().NotBeNull();
response.Candidates![0].GroundingMetadata!.WebSearchQueries.Should().NotBeNull();
_output.WriteLine(string.Join(Environment.NewLine,
response.Candidates![0].Content!.Parts
.Select(x => x.Text)
// .Where(t => !string.IsNullOrEmpty(t))
.ToArray()));
response.Candidates![0].GroundingMetadata!.GroundingChunks!
.ForEach(c =>
_output.WriteLine($"{c!.Web!.Title} - {c!.Web!.Uri}"));
_output.WriteLine(string.Join(Environment.NewLine,
response.Candidates![0].GroundingMetadata!.WebSearchQueries!
.Select(w => w)
.ToArray()));
_output.WriteLine(response.Candidates![0].GroundingMetadata!.SearchEntryPoint!.RenderedContent);
}

[Fact]
// Ref: https://ai.google.dev/gemini-api/docs/grounding
public async Task Generate_Content_Grounding_Search_Dictionary()
{
// Arrange
var prompt = "Who won Wimbledon this year?";
var genAi = new GoogleAI(_fixture.ApiKey);
var model = genAi.GenerativeModel("gemini-1.5-pro-002",
tools: [new Tool { GoogleSearchRetrieval =
new(DynamicRetrievalConfigMode.ModeUnspecified, 0.06f) }]);

// Act
var response = await model.GenerateContent(prompt);

// Assert
response.Should().NotBeNull();
response.Candidates.Should().NotBeNull().And.HaveCount(1);
_output.WriteLine(string.Join(Environment.NewLine,
response.Candidates![0].Content!.Parts
.Select(x => x.Text)
// .Where(t => !string.IsNullOrEmpty(t))
.ToArray()));
response.Candidates![0].GroundingMetadata!.GroundingChunks!
.ForEach(c =>
_output.WriteLine($"{c!.Web!.Title} - {c!.Web!.Uri}"));
_output.WriteLine(string.Join(Environment.NewLine,
response.Candidates![0].GroundingMetadata!.WebSearchQueries!
.Select(w => w)
.ToArray()));
_output.WriteLine(response.Candidates![0].GroundingMetadata!.SearchEntryPoint!.RenderedContent);
}

[Fact]
// Ref: https://ai.google.dev/docs/function_calling
Expand Down

0 comments on commit 2d82227

Please sign in to comment.