Skip to content

Commit 11b290f

Browse files
committed
Confirmed gemini tool call worked
1 parent 01b6b96 commit 11b290f

2 files changed

Lines changed: 68 additions & 42 deletions

File tree

src/BE/DB/ModelProviderInfo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Chats.BE.DB.Enums;
2+
using Chats.BE.Services.Models.ChatServices.GoogleAI;
23

34
namespace Chats.BE.DB;
45

@@ -97,7 +98,7 @@ private record ProviderInfo(
9798
[DBModelProvider.GoogleAI] = new(
9899
DBModelProvider.GoogleAI,
99100
"Google AI",
100-
"https://generativelanguage.googleapis.com/v1beta/openai/",
101+
GoogleAI2ChatService.DefaultEndpoint,
101102
""
102103
),
103104
[DBModelProvider.Ollama] = new(

src/BE/Services/Models/ChatServices/GoogleAI/GoogleAI2ChatService.cs

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
using System.Collections.Generic;
21
using Chats.BE.Controllers.Chats.Chats;
32
using Chats.BE.DB;
43
using Chats.BE.DB.Enums;
54
using Chats.BE.Services.Models.ChatServices.OpenAI;
65
using Chats.BE.Services.Models.Dtos;
76
using Chats.BE.Services.Models.Neutral;
87
using System.Diagnostics;
9-
using System.Linq;
10-
using System.Net.Http;
118
using System.Net.Http.Headers;
129
using System.Runtime.CompilerServices;
1310
using System.Text;
@@ -29,7 +26,7 @@ private static readonly (string Category, string Threshold)[] DefaultSafetySetti
2926
("HARM_CATEGORY_HARASSMENT", "BLOCK_NONE"),
3027
];
3128

32-
private const string DefaultEndpoint = "https://generativelanguage.googleapis.com";
29+
internal const string DefaultEndpoint = "https://generativelanguage.googleapis.com";
3330

3431
public bool AllowImageGeneration(Model model) => model.DeploymentName.Contains("gemini-2.0-flash-exp") ||
3532
model.DeploymentName.Contains("gemini-2.0-flash-exp-image-generation") ||
@@ -79,7 +76,7 @@ public override async IAsyncEnumerable<ChatSegment> ChatStreamed(ChatRequest req
7976
throw new RawChatServiceException(200, errorMessage);
8077
}
8178

82-
List<ChatSegment> items = new();
79+
List<ChatSegment> items = [];
8380
DBFinishReason? finishReason = null;
8481

8582
if (chunk.TryGetProperty("candidates", out JsonElement candidatesElement) &&
@@ -299,7 +296,7 @@ private JsonObject BuildRequestBody(ChatRequest request, bool allowImageGenerati
299296

300297
private static JsonArray BuildSafetySettings()
301298
{
302-
JsonArray safety = new();
299+
JsonArray safety = [];
303300
foreach ((string Category, string Threshold) setting in DefaultSafetySettings)
304301
{
305302
safety.Add(new JsonObject
@@ -313,11 +310,11 @@ private static JsonArray BuildSafetySettings()
313310

314311
private static JsonObject? BuildGenerationConfig(ChatRequest request, bool allowImageGeneration)
315312
{
316-
JsonObject config = new();
317-
JsonArray modalities = new()
318-
{
313+
JsonObject config = [];
314+
JsonArray modalities =
315+
[
319316
"TEXT"
320-
};
317+
];
321318
if (allowImageGeneration)
322319
{
323320
modalities.Add("IMAGE");
@@ -388,7 +385,7 @@ var effort when effort.IsLowOrMinimal() => 1024,
388385

389386
private static JsonArray? BuildTools(ChatRequest request)
390387
{
391-
JsonArray tools = new();
388+
JsonArray tools = [];
392389

393390
JsonObject? functionTool = BuildFunctionDeclarations(request);
394391
if (functionTool != null)
@@ -428,7 +425,7 @@ var effort when effort.IsLowOrMinimal() => 1024,
428425
return null;
429426
}
430427

431-
JsonArray declarations = new();
428+
JsonArray declarations = [];
432429
foreach (FunctionTool tool in functionTools)
433430
{
434431
JsonObject declaration = new()
@@ -480,7 +477,7 @@ private static JsonObject BuildGoogleSchema(string? openAiSchema)
480477

481478
if (root.TryGetPropertyValue("properties", out JsonNode? propsNode) && propsNode is JsonObject props)
482479
{
483-
JsonObject properties = new();
480+
JsonObject properties = [];
484481
foreach ((string key, JsonNode? value) in props)
485482
{
486483
if (value is not JsonObject propertyObject)
@@ -509,7 +506,7 @@ private static JsonObject BuildGoogleSchema(string? openAiSchema)
509506

510507
if (root.TryGetPropertyValue("required", out JsonNode? requiredNode) && requiredNode is JsonArray requiredArray)
511508
{
512-
JsonArray required = new();
509+
JsonArray required = [];
513510
foreach (JsonNode? node in requiredArray)
514511
{
515512
if (node is JsonValue value && value.TryGetValue(out string? str) && str != null)
@@ -543,7 +540,7 @@ private static string ConvertSchemaType(string? type)
543540

544541
private static JsonArray ConvertMessages(IList<NeutralMessage> messages)
545542
{
546-
JsonArray result = new();
543+
JsonArray result = [];
547544
foreach (NeutralMessage message in messages)
548545
{
549546
JsonObject content = new()
@@ -561,7 +558,7 @@ private static JsonArray ConvertMessages(IList<NeutralMessage> messages)
561558
{
562559
NeutralChatRole.User => BuildUserParts(message),
563560
NeutralChatRole.Assistant => BuildAssistantParts(message),
564-
NeutralChatRole.Tool => new JsonArray { ToolCallMessageToPart(message) },
561+
NeutralChatRole.Tool => [ToolCallMessageToPart(message)],
565562
_ => throw new NotSupportedException($"Unsupported message role: {message.Role} in {nameof(GoogleAI2ChatService)}"),
566563
};
567564

@@ -573,7 +570,7 @@ private static JsonArray ConvertMessages(IList<NeutralMessage> messages)
573570

574571
private static JsonArray BuildUserParts(NeutralMessage message)
575572
{
576-
JsonArray parts = new();
573+
JsonArray parts = [];
577574
foreach (NeutralContent content in message.Contents)
578575
{
579576
JsonObject? part = NeutralContentToGooglePart(content);
@@ -587,40 +584,68 @@ private static JsonArray BuildUserParts(NeutralMessage message)
587584

588585
private static JsonArray BuildAssistantParts(NeutralMessage message)
589586
{
590-
JsonArray parts = new();
591-
592-
foreach (NeutralToolCallContent toolCall in message.Contents.OfType<NeutralToolCallContent>())
593-
{
594-
JsonObject args = ParseJson(toolCall.Parameters) as JsonObject ?? new JsonObject();
595-
JsonObject functionCall = new()
596-
{
597-
["functionCall"] = new JsonObject
598-
{
599-
["id"] = toolCall.Id,
600-
["name"] = toolCall.Name,
601-
["args"] = args
602-
}
603-
};
604-
parts.Add(functionCall);
605-
}
587+
JsonArray parts = [];
588+
string? pendingThoughtSignature = null;
606589

607590
foreach (NeutralContent content in message.Contents)
608591
{
609-
if (content is NeutralToolCallContent)
610-
{
611-
continue;
612-
}
592+
switch (content)
593+
{
594+
case NeutralThinkContent think:
595+
// https://ai.google.dev/gemini-api/docs/thought-signatures?hl=zh-cn
596+
// Don't need to care about the thinking content
597+
//if (!string.IsNullOrEmpty(think.Content))
598+
//{
599+
// parts.Add(new JsonObject
600+
// {
601+
// ["text"] = think.Content,
602+
// ["thought"] = true
603+
// });
604+
//}
605+
if (!string.IsNullOrEmpty(think.Signature))
606+
{
607+
pendingThoughtSignature = think.Signature;
608+
}
609+
break;
613610

614-
JsonObject? part = NeutralContentToGooglePart(content);
615-
if (part != null)
616-
{
617-
parts.Add(part);
611+
case NeutralToolCallContent toolCall:
612+
JsonObject args = ParseJson(toolCall.Parameters) as JsonObject ?? [];
613+
JsonObject functionCall = new()
614+
{
615+
["functionCall"] = new JsonObject
616+
{
617+
["id"] = toolCall.Id,
618+
["name"] = toolCall.Name,
619+
["args"] = args
620+
}
621+
};
622+
AttachPendingThoughtSignature(functionCall, ref pendingThoughtSignature);
623+
parts.Add(functionCall);
624+
break;
625+
626+
default:
627+
JsonObject? part = NeutralContentToGooglePart(content);
628+
if (part != null)
629+
{
630+
AttachPendingThoughtSignature(part, ref pendingThoughtSignature);
631+
parts.Add(part);
632+
}
633+
break;
618634
}
619635
}
620636

621637
return parts;
622638
}
623639

640+
private static void AttachPendingThoughtSignature(JsonObject part, ref string? pendingThoughtSignature)
641+
{
642+
if (pendingThoughtSignature != null)
643+
{
644+
part["thoughtSignature"] = pendingThoughtSignature;
645+
pendingThoughtSignature = null;
646+
}
647+
}
648+
624649
private static JsonObject ToolCallMessageToPart(NeutralMessage message)
625650
{
626651
NeutralToolCallResponseContent? responseContent = message.Contents.OfType<NeutralToolCallResponseContent>().FirstOrDefault();

0 commit comments

Comments
 (0)