Skip to content

Commit

Permalink
feat(GoogleGemini): added api usage with costs
Browse files Browse the repository at this point in the history
  • Loading branch information
gunpal5 committed May 22, 2024
1 parent 520a8c8 commit 40f8aa9
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="1.1.1" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.3.3" />
<PackageVersion Include="Google_GenerativeAI" Version="0.1.20" />
<PackageVersion Include="Google_GenerativeAI" Version="1.0.0" />
<PackageVersion Include="GroqSharp" Version="1.1.2" />
<PackageVersion Include="H.Generators.Extensions" Version="1.22.0" />
<PackageVersion Include="H.NSwag.Generator" Version="14.0.7.76">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static string GetString(this IDictionary<string, object>? arguments)
if (arguments == null)
return string.Empty;

return JsonSerializer.Serialize(arguments, SourceGenerationContext.Default.DictionaryStringObject);
return JsonSerializer.Serialize(arguments);

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.

Check warning on line 30 in src/Providers/Google/src/Extensions/GoogleGeminiExtensions.cs

View workflow job for this annotation

GitHub Actions / Build and test / Build, test and publish

Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.
}
}

Expand Down
60 changes: 48 additions & 12 deletions src/Providers/Google/src/GoogleChatModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ namespace LangChain.Providers.Google;
/// </summary>
public partial class GoogleChatModel(
GoogleProvider provider,
string id)
string id,
int contextLength = 0,
double inputTokenPriceUsd = 0,
double outputTokenPriceUsd = 0,
double secondaryInputTokenPrice = 0,
double secondaryOutputTokenPrice = 0)
: ChatModel(id)
{
#region Properties

/// <inheritdoc />
public override int ContextLength => 0;
public override int ContextLength => contextLength;


private GenerativeModel Api { get; } = new(
provider.ApiKey,
Expand Down Expand Up @@ -138,11 +144,13 @@ public override async Task<ChatResponse> GenerateAsync(
OnPartialResponseGenerated(Environment.NewLine);
OnCompletedResponseGenerated(response.Text() ?? string.Empty);

// Unsupported
var usage2 = Usage.Empty with

var usage2 = GetUsage(response) with
{
Time = watch.Elapsed
};

//Add Usage
AddUsage(usage2);
provider.AddUsage(usage2);

Expand Down Expand Up @@ -176,7 +184,8 @@ public override async Task<ChatResponse> GenerateAsync(

messages.Add(message);

usage2 = Usage.Empty with
//Add Usage
usage2 = GetUsage(response) with
{
Time = watch.Elapsed
};
Expand All @@ -185,12 +194,8 @@ public override async Task<ChatResponse> GenerateAsync(
}
}
}


//Function Call


// Unsupported

//Add Usage
var usage = Usage.Empty with
{
Time = watch.Elapsed
Expand All @@ -201,11 +206,42 @@ public override async Task<ChatResponse> GenerateAsync(
return new ChatResponse
{
Messages = messages,
Usage = usage,
Usage = Usage,
UsedSettings = ChatSettings.Default
};
}
private Usage GetUsage(EnhancedGenerateContentResponse response)
{
var outputTokens = response.UsageMetadata?.CandidatesTokenCount ?? 0;
var inputTokens = response.UsageMetadata?.PromptTokenCount ?? 0;
var priceInUsd = CalculatePriceInUsd(
outputTokens: outputTokens,
inputTokens: inputTokens);

return Usage.Empty with
{
InputTokens = inputTokens,
OutputTokens = outputTokens,
Messages = 1,
PriceInUsd = priceInUsd,
};
}
/// <inheritdoc/>
public double CalculatePriceInUsd(int inputTokens, int outputTokens)
{
if (inputTokens < 128 * 1024)
{
var inputCost = inputTokenPriceUsd * inputTokens;
var outputCost = outputTokenPriceUsd * outputTokens;
return inputCost + outputCost;
}
else
{
var inputCost = secondaryInputTokenPrice * inputTokens;
var outputCost = secondaryOutputTokenPrice * outputTokens;
return inputCost + outputCost;
}
}
private static Message ToFunctionCallMessage(string jsonResult, string functionName)
{
//var result = JsonSerializer.Deserialize<JsonNode>(jsonResult, SerializerOptions);
Expand Down
16 changes: 14 additions & 2 deletions src/Providers/Google/src/Predefined/GeminiModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ namespace LangChain.Providers.Google.Predefined;
public class GeminiProModel(GoogleProvider provider)
: GoogleChatModel(
provider,
GoogleAIModels.GeminiPro);
GoogleAIModels.GeminiPro, 32 * 1024, 0.5 * 0.000001, 1.5 * 0.000001);

/// <inheritdoc cref="GoogleAIModels.GeminiProVision" />
public class GeminiProVisionModel(GoogleProvider provider)
: GoogleChatModel(
provider,
GoogleAIModels.GeminiProVision);
GoogleAIModels.GeminiProVision, 32 * 1024, 0.5 * 0.000001, 1.5 * 0.000001);

/// <inheritdoc cref="GoogleAIModels.GeminiProVision" />
public class Gemini15FlashModel(GoogleProvider provider)
: GoogleChatModel(
provider,
GoogleAIModels.Gemini15Flash, 1024 * 1024, 0.35 * 0.000001, 1.05 * 0.000001, 0.70 * 0.000001, 2.1 * 0.000001);

/// <inheritdoc cref="GoogleAIModels.GeminiProVision" />
public class Gemini15ProModel(GoogleProvider provider)
: GoogleChatModel(
provider,
GoogleAIModels.Gemini15Flash, 2 * 1024 * 1024, 3.5 * 0.000001, 10.50 * 0.000001, 7.0 * 0.000001, 21.00 * 0.000001);
6 changes: 5 additions & 1 deletion src/Providers/Google/test/Tests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using GenerativeAI.Models;
using GeminiProModel = LangChain.Providers.Google.Predefined.GeminiProModel;

namespace LangChain.Providers.Google.Tests;

Expand All @@ -13,7 +14,7 @@ public async Task GetWeather()
throw new InvalidOperationException("Gemini_API_Key is not set");


var model = new GoogleChatModel(apiKey, GoogleAIModels.GeminiPro);
var model = new GeminiProModel(apiKey);//, GoogleAIModels.GeminiPro);

var service = new WeatherService();
model.AddGlobalTools(service.AsGoogleFunctions(), service.AsGoogleCalls());
Expand All @@ -25,6 +26,9 @@ public async Task GetWeather()
"Sure! Could you please provide me with your location?".AsAiMessage(),
"Dubai, UAE".AsHumanMessage(),
});
response.Usage.InputTokens.Should().BeGreaterThan(0);
response.Usage.OutputTokens.Should().BeGreaterThan(0);
response.Usage.PriceInUsd.Should().BeGreaterThan(0);

Console.WriteLine(response.Messages.AsHistory());
}
Expand Down

0 comments on commit 40f8aa9

Please sign in to comment.