Skip to content

.Net: Fix Google Gemini Enums Schema definition #11617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -191,9 +191,26 @@ private struct Movie

public bool IsAvailableOnStreaming { get; set; }

public MovieGenre? Genre { get; set; }

public List<string> Tags { get; set; }
}

private enum MovieGenre
{
Action,
Adventure,
Comedy,
Drama,
Fantasy,
Horror,
Mystery,
Romance,
SciFi,
Thriller,
Western
}

private sealed class EmailResult
{
public List<Email> Emails { get; set; }
Expand Down Expand Up @@ -256,6 +273,7 @@ private void OutputResult(MovieResult movieResult)
Director: {movie.Director}
Release year: {movie.ReleaseYear}
Rating: {movie.Rating}
Genre: {movie.Genre}
Is available on streaming: {movie.IsAvailableOnStreaming}
Tags: {string.Join(",", movie.Tags ?? [])}
""");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,60 @@ public void ResponseSchemaConvertsNullableTypesToOpenApiFormat()
Assert.True(ageProperty.GetProperty("nullable").GetBoolean());
}

[Fact]
public void ResponseSchemaAddsTypeToEnumProperties()
{
// Arrange
var prompt = "prompt-example";
var schemaWithEnum = """
{
"properties" : {
"Movies": {
"type" : "array",
"items" : {
"type" : "object",
"properties" : {
"status": {
"enum": ["active", "inactive", null],
"description": "user status"
},
"role": {
"enum": ["admin", "user"],
"description": "user role"
}
}
}
}
}
}
""";

var executionSettings = new GeminiPromptExecutionSettings
{
ResponseMimeType = "application/json",
ResponseSchema = JsonSerializer.Deserialize<JsonElement>(schemaWithEnum)
};

// Act
var request = GeminiRequest.FromPromptAndExecutionSettings(prompt, executionSettings);

// Assert
Assert.NotNull(request.Configuration?.ResponseSchema);
var properties = request.Configuration.ResponseSchema.Value
.GetProperty("properties")
.GetProperty("Movies")
.GetProperty("items")
.GetProperty("properties");

var statusProperty = properties.GetProperty("status");
Assert.Equal("string", statusProperty.GetProperty("type").GetString());
Assert.Equal(3, statusProperty.GetProperty("enum").GetArrayLength());

var roleProperty = properties.GetProperty("role");
Assert.Equal("string", roleProperty.GetProperty("type").GetString());
Assert.Equal(2, roleProperty.GetProperty("enum").GetArrayLength());
}

private sealed class DummyContent(object? innerContent, string? modelId = null, IReadOnlyDictionary<string, object?>? metadata = null) :
KernelContent(innerContent, modelId, metadata);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,14 +361,29 @@ static void AdjustOpenApi3Object(JsonObject obj)
{
if (property.Value is JsonObject propertyObj)
{
if (propertyObj.TryGetPropertyValue("type", out JsonNode? typeNode) && typeNode is JsonArray typeArray)
// Handle enum properties - add "type": "enum" if missing
if (propertyObj.TryGetPropertyValue("enum", out JsonNode? enumNode) && !propertyObj.ContainsKey("type"))
{
var types = typeArray.Select(t => t?.GetValue<string>()).Where(t => t != null).ToList();
if (types.Contains("null"))
propertyObj["type"] = JsonValue.Create("string");
}
else if (propertyObj.TryGetPropertyValue("type", out JsonNode? typeNode))
{
if (typeNode is JsonArray typeArray)
{
var types = typeArray.Select(t => t?.GetValue<string>()).Where(t => t != null).ToList();
if (types.Contains("null"))
{
var mainType = types.First(t => t != "null");
propertyObj["type"] = JsonValue.Create(mainType);
propertyObj["nullable"] = JsonValue.Create(true);
}
}
else if (typeNode is JsonValue typeValue && typeValue.GetValue<string>() == "array")
{
var mainType = types.First(t => t != "null");
propertyObj["type"] = JsonValue.Create(mainType);
propertyObj["nullable"] = JsonValue.Create(true);
if (propertyObj.TryGetPropertyValue("items", out JsonNode? itemsNode) && itemsNode is JsonObject itemsObj)
{
AdjustOpenApi3Object(itemsObj);
}
}
}

Expand Down
Loading