Skip to content

Commit bb24743

Browse files
author
Jochen Kirstätter
committed
🎶 add methods ListModels and GetModel
1 parent 337bff6 commit bb24743

7 files changed

+142
-5
lines changed

src/GenerativeModel.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
#endif
99
using System.Text.RegularExpressions;
1010
using System.Linq;
11+
using System.Net;
1112

1213
namespace Mscc.GenerativeAI
1314
{
1415
public class GenerativeModel
1516
{
16-
private readonly string urlGoogleAI = "https://generativelanguage.googleapis.com/{version}/models/{model}:{method}?key={apiKey}";
17+
private readonly string endpointGoogleAI = "generativelanguage.googleapis.com";
18+
private readonly string urlGoogleAI = "https://{endpointGoogleAI}/{version}/models/{model}:{method}?key={apiKey}";
1719
private readonly string urlVertexAI = "https://{region}-aiplatform.googleapis.com/{version}/projects/{projectId}/locations/{region}/publishers/{publisher}/models/{model}:{method}";
1820
private readonly string model;
1921
private readonly string apiKey = default;
@@ -116,6 +118,38 @@ public GenerativeModel(string projectId, string region, string model, Generation
116118
this.safetySettings = safetySettings;
117119
}
118120

121+
/// <summary>
122+
/// Get a list of available models and description.
123+
/// </summary>
124+
/// <returns></returns>
125+
public async Task<List<ModelResponse>> ListModels()
126+
{
127+
if (string.IsNullOrEmpty(apiKey))
128+
throw new NotSupportedException();
129+
130+
var url = $"https://{endpointGoogleAI}/{Version}/models?key={apiKey}";
131+
var response = await Client.GetAsync(url);
132+
response.EnsureSuccessStatusCode();
133+
var models = await Deserialize<ListModelsResponse>(response);
134+
return models?.Models!;
135+
}
136+
137+
/// <summary>
138+
/// Get information about the model, including default values.
139+
/// </summary>
140+
/// <param name="model">The model to query</param>
141+
/// <returns></returns>
142+
public async Task<ModelResponse> GetModel(string model = Model.GeminiPro)
143+
{
144+
if (string.IsNullOrEmpty(apiKey))
145+
throw new NotSupportedException();
146+
147+
var url = $"https://{endpointGoogleAI}/{Version}/models/{model}?key={apiKey}";
148+
var response = await Client.GetAsync(url);
149+
response.EnsureSuccessStatusCode();
150+
return await Deserialize<ModelResponse>(response);
151+
}
152+
119153
/// <summary>
120154
/// Produces a single request and response.
121155
/// </summary>
@@ -264,7 +298,7 @@ public ChatSession StartChat(List<Content>? history = null, GenerationConfig? ge
264298
/// <param name="url"></param>
265299
/// <param name="method"></param>
266300
/// <returns></returns>
267-
private string ParseUrl(string url, string? method)
301+
private string ParseUrl(string url, string? method = default)
268302
{
269303
var replacements = GetReplacements();
270304
replacements.Add("method", method);
@@ -278,6 +312,7 @@ Dictionary<string, string> GetReplacements()
278312
{
279313
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
280314
{
315+
{ "endpointGoogleAI", endpointGoogleAI },
281316
{ "version", Version },
282317
{ "model", model },
283318
{ "apikey", apiKey },

src/ListModelsResponse.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#if NET472_OR_GREATER || NETSTANDARD2_0
2+
using System.Collections.Generic;
3+
#endif
4+
5+
namespace Mscc.GenerativeAI
6+
{
7+
internal class ListModelsResponse
8+
{
9+
public List<ModelResponse>? Models { get; set; }
10+
}
11+
}

src/ModelResponse.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#if NET472_OR_GREATER || NETSTANDARD2_0
2+
using System.Collections.Generic;
3+
#endif
4+
5+
using System.Diagnostics;
6+
7+
namespace Mscc.GenerativeAI
8+
{
9+
[DebuggerDisplay("{DisplayName} ({Name})")]
10+
public class ModelResponse
11+
{
12+
public string? Name { get; set; } = default;
13+
public string? Version { get; set; } = default;
14+
public string? DisplayName { get; set; } = default;
15+
public string? Description { get; set; } = default;
16+
public int? InputTokenLimit { get; set; } = default;
17+
public int? OutputTokenLimit { get; set; } = default;
18+
public List<string>? SupportedGenerationMethods { get; set; }
19+
public float? Temperature { get; set; } = default;
20+
public float? TopP { get; set; } = default;
21+
public int? TopK { get; set; } = default;
22+
}
23+
}

src/Mscc.GenerativeAI.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
<RepositoryUrl>https://github.com/mscraftsman/generative-ai</RepositoryUrl>
2020
<PackageLicenseFile>LICENSE</PackageLicenseFile>
2121
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
22-
<PackageReleaseNotes>- Initial Release</PackageReleaseNotes>
23-
<Version>0.2.1</Version>
22+
<PackageReleaseNotes>- Add methods ListModels and GetModel</PackageReleaseNotes>
23+
<Version>0.3.1</Version>
2424
</PropertyGroup>
2525

2626
<PropertyGroup Condition="$(TargetFramework.StartsWith('net6.0')) or $(TargetFramework.StartsWith('net7.0')) or $(TargetFramework.StartsWith('net8.0'))">

tests/GoogleAi_GeminiPro_Should.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,45 @@ public void Initialize_Model()
4646
model.Name().Should().Be(Model.GeminiPro);
4747
}
4848

49+
[Fact]
50+
public async void List_Models()
51+
{
52+
// Arrange
53+
var model = new GenerativeModel(apiKey: fixture.ApiKey);
54+
55+
// Act
56+
var sut = await model.ListModels();
57+
58+
// Assert
59+
sut.Should().NotBeNull();
60+
sut.Should().NotBeNull().And.HaveCountGreaterThanOrEqualTo(1);
61+
sut.ForEach(x =>
62+
{
63+
output.WriteLine($"Model: {x.DisplayName} ({x.Name})");
64+
x.SupportedGenerationMethods.ForEach(m => output.WriteLine($" Method: {m}"));
65+
});
66+
}
67+
68+
[Theory]
69+
[InlineData(Model.GeminiPro)]
70+
[InlineData(Model.GeminiProVision)]
71+
[InlineData(Model.BisonText)]
72+
[InlineData(Model.BisonChat)]
73+
public async void Get_Model_Information(string modelName)
74+
{
75+
// Arrange
76+
var model = new GenerativeModel(apiKey: fixture.ApiKey);
77+
78+
// Act
79+
var sut = await model.GetModel(model: modelName);
80+
81+
// Assert
82+
sut.Should().NotBeNull();
83+
sut.Name.Should().Be($"models/{modelName}");
84+
output.WriteLine($"Model: {sut.DisplayName} ({sut.Name})");
85+
sut.SupportedGenerationMethods.ForEach(m => output.WriteLine($" Method: {m}"));
86+
}
87+
4988
[Fact]
5089
public async void Generate_Content()
5190
{

tests/VertexAi_GeminiPro_Should.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using FluentAssertions;
22
using Mscc.GenerativeAI;
3+
using System;
34
using System.Collections.Generic;
45
using System.Linq;
56
using Xunit;
@@ -60,6 +61,34 @@ public void Initialize_Model()
6061
model.Name().Should().Be(Model.Gemini10Pro);
6162
}
6263

64+
[Fact]
65+
public async void List_Models()
66+
{
67+
// Arrange
68+
var vertex = new VertexAI(projectId: fixture.ProjectId, region: fixture.Region);
69+
var model = vertex.GenerativeModel(model: this.model);
70+
model.AccessToken = fixture.AccessToken;
71+
72+
// Act & Assert
73+
await Assert.ThrowsAsync<NotSupportedException>(() => model.ListModels());
74+
}
75+
76+
[Theory]
77+
[InlineData(Model.GeminiPro)]
78+
[InlineData(Model.GeminiProVision)]
79+
[InlineData(Model.BisonText)]
80+
[InlineData(Model.BisonChat)]
81+
public async void Get_Model_Information(string modelName)
82+
{
83+
// Arrange
84+
var vertex = new VertexAI(projectId: fixture.ProjectId, region: fixture.Region);
85+
var model = vertex.GenerativeModel(model: this.model);
86+
model.AccessToken = fixture.AccessToken;
87+
88+
// Act & Assert
89+
await Assert.ThrowsAsync<NotSupportedException>(() => model.GetModel(model: modelName));
90+
}
91+
6392
[Fact]
6493
public async void Generate_Content()
6594
{

tests/appsettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"api_key": "",
33
"project_id": "",
4-
"region": "",
4+
"region": "us-central1",
55
"access_token": ""
66
}

0 commit comments

Comments
 (0)