From dc649bdc5975b2716e9d49cf7a076f5e0f4e9208 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Mon, 16 Mar 2026 13:14:56 -0700 Subject: [PATCH 1/7] feat: Add Gemini Realtime provider implementing IRealtimeClient/IRealtimeClientSession --- DemoApp/Files/packages.lock.json | 8 +- .../packages.lock.json | 8 +- .../packages.lock.json | 8 +- DemoApp/JsonParser/packages.lock.json | 8 +- .../packages.lock.json | 8 +- .../packages.lock.json | 8 +- DemoApp/LiveToolCall/packages.lock.json | 8 +- DemoApp/RegisterFiles/packages.lock.json | 95 + Directory.Packages.props | 2 +- .../Netstandard2_0Tests/packages.lock.json | 8 +- Google.GenAI.E2E.Tests/packages.lock.json | 32 +- Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs | 2619 +++++++++++++++++ .../Netstandard2_0Tests/packages.lock.json | 8 +- Google.GenAI.Tests/packages.lock.json | 8 +- Google.GenAI/GoogleGenAIExtensions.cs | 17 + Google.GenAI/GoogleGenAIRealtimeClient.cs | 219 ++ Google.GenAI/GoogleGenAIRealtimeSession.cs | 748 +++++ Google.GenAI/Live.cs | 6 +- Google.GenAI/packages.lock.json | 12 +- nuget.config | 6 + 20 files changed, 3767 insertions(+), 69 deletions(-) create mode 100644 DemoApp/RegisterFiles/packages.lock.json create mode 100644 Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs create mode 100644 Google.GenAI/GoogleGenAIRealtimeClient.cs create mode 100644 Google.GenAI/GoogleGenAIRealtimeSession.cs create mode 100644 nuget.config diff --git a/DemoApp/Files/packages.lock.json b/DemoApp/Files/packages.lock.json index 8b1628f9..e0388869 100644 --- a/DemoApp/Files/packages.lock.json +++ b/DemoApp/Files/packages.lock.json @@ -50,7 +50,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -67,9 +67,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/DemoApp/GenerateContentSimpleText/packages.lock.json b/DemoApp/GenerateContentSimpleText/packages.lock.json index 8b1628f9..e0388869 100644 --- a/DemoApp/GenerateContentSimpleText/packages.lock.json +++ b/DemoApp/GenerateContentSimpleText/packages.lock.json @@ -50,7 +50,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -67,9 +67,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/DemoApp/GenerateContentStreamSimpleText/packages.lock.json b/DemoApp/GenerateContentStreamSimpleText/packages.lock.json index 8b1628f9..e0388869 100644 --- a/DemoApp/GenerateContentStreamSimpleText/packages.lock.json +++ b/DemoApp/GenerateContentStreamSimpleText/packages.lock.json @@ -50,7 +50,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -67,9 +67,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/DemoApp/JsonParser/packages.lock.json b/DemoApp/JsonParser/packages.lock.json index 8b1628f9..e0388869 100644 --- a/DemoApp/JsonParser/packages.lock.json +++ b/DemoApp/JsonParser/packages.lock.json @@ -50,7 +50,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -67,9 +67,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json b/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json index 8b1628f9..e0388869 100644 --- a/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json +++ b/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json @@ -50,7 +50,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -67,9 +67,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/DemoApp/LiveTextToTextClientContent/packages.lock.json b/DemoApp/LiveTextToTextClientContent/packages.lock.json index 8b1628f9..e0388869 100644 --- a/DemoApp/LiveTextToTextClientContent/packages.lock.json +++ b/DemoApp/LiveTextToTextClientContent/packages.lock.json @@ -50,7 +50,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -67,9 +67,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/DemoApp/LiveToolCall/packages.lock.json b/DemoApp/LiveToolCall/packages.lock.json index 8b1628f9..e0388869 100644 --- a/DemoApp/LiveToolCall/packages.lock.json +++ b/DemoApp/LiveToolCall/packages.lock.json @@ -50,7 +50,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -67,9 +67,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/DemoApp/RegisterFiles/packages.lock.json b/DemoApp/RegisterFiles/packages.lock.json new file mode 100644 index 00000000..e0388869 --- /dev/null +++ b/DemoApp/RegisterFiles/packages.lock.json @@ -0,0 +1,95 @@ +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } +} \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index c8b8d735..7b6ba1e2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,7 +13,7 @@ - + diff --git a/Google.GenAI.E2E.Tests/Netstandard2_0Tests/packages.lock.json b/Google.GenAI.E2E.Tests/Netstandard2_0Tests/packages.lock.json index 4bfece9f..a3505287 100644 --- a/Google.GenAI.E2E.Tests/Netstandard2_0Tests/packages.lock.json +++ b/Google.GenAI.E2E.Tests/Netstandard2_0Tests/packages.lock.json @@ -283,7 +283,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -300,9 +300,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/Google.GenAI.E2E.Tests/packages.lock.json b/Google.GenAI.E2E.Tests/packages.lock.json index 79753e82..b17583b7 100644 --- a/Google.GenAI.E2E.Tests/packages.lock.json +++ b/Google.GenAI.E2E.Tests/packages.lock.json @@ -214,6 +214,14 @@ "resolved": "7.0.0", "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "5.0.0", @@ -269,10 +277,8 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", - "MimeTypes": "[2.5.2, )", - "System.Collections.Immutable": "[9.0.0, )", - "System.Net.ServerSentEvents": "[9.0.0, )" + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )" } }, "Google.Apis.Auth": { @@ -288,9 +294,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } @@ -300,18 +306,6 @@ "requested": "[2.5.2, )", "resolved": "2.5.2", "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Collections.Immutable": { - "type": "CentralTransitive", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==" - }, - "System.Net.ServerSentEvents": { - "type": "CentralTransitive", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "VTWjeyx9nPb4+hkjGcAaDw1nOckypMtvABmxSWm6PPYwrXoIiVG3jwtNlAGhaGVjDkBrERABox67wYTAcHxg7Q==" } } } diff --git a/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs new file mode 100644 index 00000000..4edf8346 --- /dev/null +++ b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs @@ -0,0 +1,2619 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma warning disable MEAI001 // Experimental AI API + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Google.GenAI.Types; +using Microsoft.Extensions.AI; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +#pragma warning disable MEAI001 + +namespace Google.GenAI.Tests; + +[TestClass] +public class GoogleGenAIRealtimeClientTest +{ + #region Extension Method Tests + + [TestMethod] + public void AsIRealtimeClient_NullClient_ThrowsArgumentNullException() + { + Client? client = null; + Assert.ThrowsException(() => client!.AsIRealtimeClient("model")); + } + + [TestMethod] + public void AsIRealtimeClient_ValidClient_ReturnsNonNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = client.AsIRealtimeClient("model"); + Assert.IsNotNull(realtimeClient); + } + + [TestMethod] + public void AsIRealtimeClient_NullModelId_ReturnsNonNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = client.AsIRealtimeClient(); + Assert.IsNotNull(realtimeClient); + } + + #endregion + + #region Client Constructor Tests + + [TestMethod] + public void RealtimeClient_NullClient_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new GoogleGenAIRealtimeClient(null!, "model")); + } + + [TestMethod] + public void RealtimeClient_NullModelId_Succeeds() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client); + Assert.IsNotNull(realtimeClient); + } + + #endregion + + #region Client GetService Tests + + [TestMethod] + public void RealtimeClient_GetService_ReturnsMetadata() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "my-model"); + + var metadata = realtimeClient.GetService(typeof(ChatClientMetadata)) as ChatClientMetadata; + Assert.IsNotNull(metadata); + Assert.AreEqual("google-genai", metadata.ProviderName); + Assert.AreEqual("my-model", metadata.DefaultModelId); + } + + [TestMethod] + public void RealtimeClient_GetService_ReturnsSelf() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var self = realtimeClient.GetService(typeof(GoogleGenAIRealtimeClient)); + Assert.AreSame(realtimeClient, self); + } + + [TestMethod] + public void RealtimeClient_GetService_ReturnsInnerClient() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var inner = realtimeClient.GetService(typeof(Client)); + Assert.AreSame(client, inner); + } + + [TestMethod] + public void RealtimeClient_GetService_NullServiceType_ThrowsArgumentNullException() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + Assert.ThrowsException( + () => realtimeClient.GetService(null!)); + } + + [TestMethod] + public void RealtimeClient_GetService_WithKey_ReturnsNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var result = realtimeClient.GetService(typeof(ChatClientMetadata), serviceKey: "some-key"); + Assert.IsNull(result); + } + + [TestMethod] + public void RealtimeClient_GetService_UnknownType_ReturnsNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var result = realtimeClient.GetService(typeof(string)); + Assert.IsNull(result); + } + + #endregion + + #region Client Dispose Tests + + [TestMethod] + public void RealtimeClient_Dispose_IsIdempotent() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + // Should not throw + realtimeClient.Dispose(); + realtimeClient.Dispose(); + } + + #endregion + + #region Client CreateSession Tests + + [TestMethod] + public async Task RealtimeClient_CreateSession_NoModel_ThrowsInvalidOperationException() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client); + + await Assert.ThrowsExceptionAsync( + () => realtimeClient.CreateSessionAsync(new RealtimeSessionOptions())); + } + + [TestMethod] + public async Task RealtimeClient_CreateSession_Cancelled_ThrowsOperationCanceledException() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsExceptionAsync( + () => realtimeClient.CreateSessionAsync(new RealtimeSessionOptions(), cts.Token)); + } + + #endregion + + #region ToGoogleFunctionDeclaration Tests + + [TestMethod] + public void ToGoogleFunctionDeclaration_MapsNameAndDescription() + { + var function = AIFunctionFactory.Create(() => "result", "myFunc", "A test function"); + + var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); + + Assert.AreEqual("myFunc", declaration.Name); + Assert.AreEqual("A test function", declaration.Description); + } + + [TestMethod] + public void ToGoogleFunctionDeclaration_WithJsonSchema_MapsParameters() + { + var function = AIFunctionFactory.Create((string name, int age) => $"{name} is {age}", + "greet", "Greets a person"); + + var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); + + Assert.AreEqual("greet", declaration.Name); + Assert.AreEqual("Greets a person", declaration.Description); + + // The ParametersJsonSchema should be set since the function has parameters + Assert.IsNotNull(declaration.ParametersJsonSchema); + } + + [TestMethod] + public void ToGoogleFunctionDeclaration_NoParameters_HasNoParameterSchema() + { + var function = AIFunctionFactory.Create(() => "hello", "noParams", "No parameters"); + + var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); + + Assert.AreEqual("noParams", declaration.Name); + } + + #endregion +} + +[TestClass] +public class GoogleGenAIRealtimeSessionTest +{ + private Mock _mockWebSocket = null!; + private AsyncSession _asyncSession = null!; + private GoogleGenAIRealtimeSession _session = null!; + + [TestInitialize] + public void TestInitialize() + { + _mockWebSocket = new Mock(); + _mockWebSocket.Setup(ws => ws.State).Returns(WebSocketState.Open); + _asyncSession = new AsyncSession(_mockWebSocket.Object, new TestApiClient(false)); + _session = new GoogleGenAIRealtimeSession(_asyncSession, "test-model", null); + } + + [TestCleanup] + public async Task TestCleanup() + { + await _session.DisposeAsync(); + } + + #region Constructor Tests + + [TestMethod] + public void Session_NullAsyncSession_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new GoogleGenAIRealtimeSession(null!, "model", null)); + } + + [TestMethod] + public void Session_ValidConstruction_SetsOptions() + { + var options = new RealtimeSessionOptions { Model = "my-model" }; + var session = new GoogleGenAIRealtimeSession( + _asyncSession, "my-model", options); + + Assert.AreSame(options, session.Options); + } + + [TestMethod] + public void Session_NullOptions_OptionsIsNull() + { + Assert.IsNull(_session.Options); + } + + #endregion + + #region Session GetService Tests + + [TestMethod] + public void Session_GetService_ReturnsMetadata() + { + var metadata = _session.GetService(typeof(ChatClientMetadata)) as ChatClientMetadata; + Assert.IsNotNull(metadata); + Assert.AreEqual("google-genai", metadata.ProviderName); + Assert.AreEqual("test-model", metadata.DefaultModelId); + } + + [TestMethod] + public void Session_GetService_ReturnsSelf() + { + var self = _session.GetService(typeof(GoogleGenAIRealtimeSession)); + Assert.AreSame(_session, self); + } + + [TestMethod] + public void Session_GetService_ReturnsInnerAsyncSession() + { + var inner = _session.GetService(typeof(AsyncSession)); + Assert.AreSame(_asyncSession, inner); + } + + [TestMethod] + public void Session_GetService_NullServiceType_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => _session.GetService(null!)); + } + + [TestMethod] + public void Session_GetService_WithKey_ReturnsNull() + { + var result = _session.GetService(typeof(ChatClientMetadata), serviceKey: "key"); + Assert.IsNull(result); + } + + [TestMethod] + public void Session_GetService_UnknownType_ReturnsNull() + { + var result = _session.GetService(typeof(string)); + Assert.IsNull(result); + } + + #endregion + + #region Session DisposeAsync Tests + + [TestMethod] + public async Task Session_DisposeAsync_IsIdempotent() + { + var session = new GoogleGenAIRealtimeSession(_asyncSession, "model", null); + + // Should not throw on double dispose + await session.DisposeAsync(); + await session.DisposeAsync(); + } + + #endregion + + #region SendAsync Guard Tests + + [TestMethod] + public async Task SendAsync_NullMessage_ThrowsArgumentNullException() + { + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(null!)); + } + + [TestMethod] + public async Task SendAsync_AfterDispose_ThrowsObjectDisposedException() + { + await _session.DisposeAsync(); + + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(new CreateResponseRealtimeClientMessage())); + } + + [TestMethod] + public async Task SendAsync_CancelledToken_ThrowsOperationCanceledException() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(new CreateResponseRealtimeClientMessage(), cts.Token)); + } + + [TestMethod] + public async Task SendAsync_SessionUpdate_DoesNotUpdateOptions() + { + var initialOptions = new RealtimeSessionOptions { Model = "initial-model" }; + var session = new GoogleGenAIRealtimeSession(_asyncSession, "initial-model", initialOptions); + + var newOptions = new RealtimeSessionOptions { Model = "updated-model" }; + var msg = new SessionUpdateRealtimeClientMessage(newOptions); + + await session.SendAsync(msg); + + // Gemini does not support mid-session updates; options should remain unchanged. + Assert.AreSame(initialOptions, session.Options); + + await session.DisposeAsync(); + } + + [TestMethod] + public void SendAsync_SessionUpdate_NullOptions_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new SessionUpdateRealtimeClientMessage(null!)); + } + + [TestMethod] + public async Task SendAsync_UnknownMessageType_DoesNotThrow() + { + // A generic RealtimeClientMessage should just be ignored (default case) + var msg = new RealtimeClientMessage(); + await _session.SendAsync(msg); + } + + #endregion + + #region Audio Buffering Tests + + [TestMethod] + public void SendAsync_AudioAppend_NoContent_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new InputAudioBufferAppendRealtimeClientMessage(null!)); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_NonAudioContent_DoesNotThrow() + { + var msg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent("data:text/plain;base64,SGVsbG8=", "text/plain")); + await _session.SendAsync(msg); + } + + [TestMethod] + public async Task SendAsync_AudioAppendThenCommit_SendsFrames() + { + // Prepare audio content + byte[] audioData = new byte[100]; + new Random(42).NextBytes(audioData); + string base64 = Convert.ToBase64String(audioData); + var dataUri = $"data:audio/pcm;base64,{base64}"; + + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); + + // Track SendAsync calls on the WebSocket + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + var copy = new byte[buffer.Count]; + Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); + sentMessages.Add(copy); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + + // Nothing sent yet (just buffered) + Assert.AreEqual(0, sentMessages.Count); + + // Commit triggers actual send + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Should have sent: ActivityStart + audio frame(s) + ActivityEnd + Assert.IsTrue(sentMessages.Count >= 3, + $"Expected at least 3 WebSocket messages (ActivityStart + audio + ActivityEnd), got {sentMessages.Count}"); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_EmptyBuffer_DoesNothing() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + var copy = new byte[buffer.Count]; + Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); + sentMessages.Add(copy); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + Assert.AreEqual(0, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_ExceedsBufferLimit_ThrowsInvalidOperationException() + { + // Create audio data close to the 10MB limit + byte[] largeAudio = new byte[10 * 1024 * 1024 + 1]; + string base64 = Convert.ToBase64String(largeAudio); + var dataUri = $"data:audio/pcm;base64,{base64}"; + + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); + + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(appendMsg)); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_LargeFrame_SplitsIntoMultipleFrames() + { + // Create audio data larger than 32KB frame limit (e.g. 70KB) + byte[] audioData = new byte[70_000]; + new Random(42).NextBytes(audioData); + string base64 = Convert.ToBase64String(audioData); + var dataUri = $"data:audio/pcm;base64,{base64}"; + + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + var copy = new byte[buffer.Count]; + Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); + sentMessages.Add(copy); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // 70KB audio = 3 frames (32K + 32K + 6K) + ActivityStart + ActivityEnd = 5 messages + Assert.AreEqual(5, sentMessages.Count, + $"Expected 5 WebSocket messages (ActivityStart + 3 audio frames + ActivityEnd), got {sentMessages.Count}"); + } + + #endregion + + #region GetStreamingResponseAsync Tests + + [TestMethod] + public async Task GetStreamingResponseAsync_AfterDispose_ThrowsObjectDisposedException() + { + await _session.DisposeAsync(); + + await Assert.ThrowsExceptionAsync(async () => + { + await foreach (var _ in _session.GetStreamingResponseAsync()) + { + } + }); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_NullMessage_YieldsBreak() + { + // AsyncSession.ReceiveAsync returns null on close + var closeResult = new WebSocketReceiveResult( + 0, WebSocketMessageType.Close, true, + WebSocketCloseStatus.NormalClosure, "done"); + + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ReturnsAsync(closeResult); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_CallerCancelled_YieldsNoResults() + { + // A pre-cancelled token causes the while-loop condition to be false immediately, + // so the iterator completes without throwing. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync(cts.Token)) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_WebSocketException_YieldsBreak() + { + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ThrowsAsync(new WebSocketException("connection closed")); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + #endregion + + #region MapServerMessage Tests + + [TestMethod] + public async Task GetStreamingResponseAsync_SetupComplete_IsSkipped() + { + SetupReceiveOnce(new LiveServerMessage { SetupComplete = new LiveServerSetupComplete() }); + + var messages = await CollectMessages(); + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_TextResponse_EmitsResponseCreatedAndTextDelta() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Hello world" } } + } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + OutputTextDelta + Assert.AreEqual(2, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual("Hello world", ((OutputTextAudioRealtimeServerMessage)messages[1]).Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_AudioResponse_EmitsAudioDelta() + { + byte[] audioBytes = new byte[] { 1, 2, 3, 4 }; + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List + { + new Part + { + InlineData = new Blob + { + Data = audioBytes, + MimeType = "audio/pcm", + } + } + } + } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + OutputAudioDelta + Assert.AreEqual(2, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputAudioDelta, messages[1].Type); + var audioMsg = (OutputTextAudioRealtimeServerMessage)messages[1]; + Assert.AreEqual(Convert.ToBase64String(audioBytes), audioMsg.Audio); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_TurnComplete_EmitsResponseDone() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Hi" } } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent { TurnComplete = true } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + OutputTextDelta + ResponseDone + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_InputTranscription_EmitsTranscriptionCompleted() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + InputTranscription = new Transcription { Text = "what is the weather?" } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.InputAudioTranscriptionCompleted, messages[0].Type); + var transcription = (InputAudioTranscriptionRealtimeServerMessage)messages[0]; + Assert.AreEqual("what is the weather?", transcription.Transcription); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_OutputTranscription_EmitsTranscriptionDelta() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + OutputTranscription = new Transcription { Text = "The weather is sunny." } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.OutputAudioTranscriptionDelta, messages[0].Type); + var outputMsg = (OutputTextAudioRealtimeServerMessage)messages[0]; + Assert.AreEqual("The weather is sunny.", outputMsg.Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCall_EmitsResponseCreatedAndFunctionCall() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall + { + Id = "call-1", + Name = "get_weather", + Args = new Dictionary { ["city"] = "Seattle" } + } + } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + ResponseOutputItemAdded + ResponseOutputItemDone + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[2].Type); + + var itemMsg = (ResponseOutputItemRealtimeServerMessage)messages[1]; + var functionCall = itemMsg.Item?.Contents?.OfType().First(); + Assert.IsNotNull(functionCall); + Assert.AreEqual("call-1", functionCall.CallId); + Assert.AreEqual("get_weather", functionCall.Name); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_GoAway_EmitsError() + { + SetupReceiveOnce(new LiveServerMessage + { + GoAway = new LiveServerGoAway() + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + var errorMsg = messages[0] as ErrorRealtimeServerMessage; + Assert.IsNotNull(errorMsg); + Assert.IsNotNull(errorMsg.Error); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_UsageMetadata_EmitsResponseDone_WhenResponseInProgress() + { + SetupReceiveSequence(new[] + { + // Start a response so _responseInProgress is true + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "response" } } + } + } + }, + // Usage metadata should emit ResponseDone + new LiveServerMessage + { + UsageMetadata = new UsageMetadata + { + PromptTokenCount = 10, + ResponseTokenCount = 20, + TotalTokenCount = 30, + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + OutputTextDelta + ResponseDone(usage) + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); + var responseDone = (ResponseCreatedRealtimeServerMessage)messages[2]; + Assert.IsNotNull(responseDone.Usage); + Assert.AreEqual(10, responseDone.Usage.InputTokenCount); + Assert.AreEqual(20, responseDone.Usage.OutputTokenCount); + Assert.AreEqual(30, responseDone.Usage.TotalTokenCount); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_UsageMetadata_NoResponseInProgress_IsSkipped() + { + // No prior model response — _responseInProgress is false + SetupReceiveOnce(new LiveServerMessage + { + UsageMetadata = new UsageMetadata + { + PromptTokenCount = 5, + ResponseTokenCount = 10, + TotalTokenCount = 15, + } + }); + + var messages = await CollectMessages(); + + // Usage metadata without a response in progress should be skipped + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_TurnComplete_PreventsDoubleResponseDone_FromUsage() + { + SetupReceiveSequence(new[] + { + // Model response + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Hi" } } + } + } + }, + // TurnComplete — emits ResponseDone and resets _responseInProgress + new LiveServerMessage + { + ServerContent = new LiveServerContent { TurnComplete = true } + }, + // Usage after TurnComplete — should NOT emit another ResponseDone + new LiveServerMessage + { + UsageMetadata = new UsageMetadata + { + PromptTokenCount = 10, + ResponseTokenCount = 20, + TotalTokenCount = 30, + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + OutputTextDelta + ResponseDone(TurnComplete) — no second ResponseDone + Assert.AreEqual(3, messages.Count); + var responseDoneMessages = messages.Where( + m => m.Type == RealtimeServerMessageType.ResponseDone).ToList(); + Assert.AreEqual(1, responseDoneMessages.Count, "Should only have one ResponseDone"); + } + + #endregion + + #region ConversationItemCreate Tests + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_TextContent_SendsClientContent() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello from user") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + // Verify the sent JSON contains our text + Assert.IsTrue(sentMessages[0].Contains("Hello from user"), + "Sent message should contain the text content"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_FunctionResult_SendsToolResponse() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-123", "sunny") }, + role: ChatRole.Tool)); + + await _session.SendAsync(msg); + + // Function results are buffered until CreateResponse + Assert.AreEqual(0, sentMessages.Count, + "Function results should be buffered, not sent immediately"); + + // Flush via CreateResponse + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(1, sentMessages.Count, + "Flushed tool response should be sent as a single WebSocket message"); + Assert.IsTrue(sentMessages[0].Contains("call-123"), + "Tool response should contain the call ID"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_FunctionResult_IncludesFunctionName() + { + // First, simulate receiving a tool call so the session caches the CallId → Name mapping. + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "call-42", Name = "get_weather" } + } + } + }); + + await CollectMessages(); + + // Now send the function result back + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-42", "sunny") }, + role: ChatRole.Tool)); + + await _session.SendAsync(msg); + + // Function results are buffered until CreateResponse + Assert.AreEqual(0, sentMessages.Count); + + // Flush via CreateResponse + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("get_weather"), + "Tool response should include the function name from the original tool call"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_EmptyContents_DoesNotSend() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List(), + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(0, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_AssistantRole_MappedToModel() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("I am the model") }, + role: ChatRole.Assistant)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + // Gemini uses "model" role, not "assistant" + Assert.IsTrue(sentMessages[0].Contains("model"), + "Assistant role should be mapped to 'model' for Gemini"); + } + + #endregion + + #region BuildLiveConnectConfig Tests + + [TestMethod] + public void BuildLiveConnectConfig_NullOptions_DefaultsToAudioModality() + { + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(null); + + Assert.IsNotNull(config.ResponseModalities); + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_SystemInstructions_MappedCorrectly() + { + var options = new RealtimeSessionOptions + { + Instructions = "You are a helpful assistant.", + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.SystemInstruction); + Assert.AreEqual(1, config.SystemInstruction.Parts.Count); + Assert.AreEqual("You are a helpful assistant.", config.SystemInstruction.Parts[0].Text); + Assert.AreEqual("user", config.SystemInstruction.Role); + } + + [TestMethod] + public void BuildLiveConnectConfig_EmptyInstructions_NoSystemInstruction() + { + var options = new RealtimeSessionOptions + { + Instructions = "", + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.SystemInstruction); + } + + [TestMethod] + public void BuildLiveConnectConfig_OutputModalities_AudioAndText() + { + var options = new RealtimeSessionOptions + { + OutputModalities = new List { "audio", "text" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(2, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); + Assert.AreEqual(Modality.Text, config.ResponseModalities[1]); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoOutputModalities_DefaultsToAudio() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_UnknownModality_DefaultsToText() + { + var options = new RealtimeSessionOptions + { + OutputModalities = new List { "video" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Text, config.ResponseModalities[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_Voice_MappedToSpeechConfig() + { + var options = new RealtimeSessionOptions + { + Voice = "Puck", + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.SpeechConfig); + Assert.IsNotNull(config.SpeechConfig.VoiceConfig); + Assert.AreEqual("Puck", config.SpeechConfig.VoiceConfig.PrebuiltVoiceConfig.VoiceName); + } + + [TestMethod] + public void BuildLiveConnectConfig_NullVoice_NoSpeechConfig() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.SpeechConfig); + } + + [TestMethod] + public void BuildLiveConnectConfig_MaxOutputTokens_MappedToGenerationConfig() + { + var options = new RealtimeSessionOptions + { + MaxOutputTokens = 500, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.GenerationConfig); + Assert.AreEqual(500, config.GenerationConfig.MaxOutputTokens); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoMaxOutputTokens_NoGenerationConfig() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.GenerationConfig); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionOptions_EnablesBothDirections() + { + var options = new RealtimeSessionOptions + { + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNotNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoTranscriptionOptions_NoTranscription() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.InputAudioTranscription); + Assert.IsNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoVadOptions_DisablesVadByDefault() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsNotNull(config.RealtimeInputConfig.AutomaticActivityDetection); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_VadEnabled_EnablesAutomaticActivityDetection() + { + var options = new RealtimeSessionOptions + { + VoiceActivityDetection = new VoiceActivityDetectionOptions + { + Enabled = true, + AllowInterruption = true, + }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsNotNull(config.RealtimeInputConfig.AutomaticActivityDetection); + Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + Assert.AreEqual( + ActivityHandling.StartOfActivityInterrupts, + config.RealtimeInputConfig.ActivityHandling); + } + + [TestMethod] + public void BuildLiveConnectConfig_VadEnabled_NoInterruption() + { + var options = new RealtimeSessionOptions + { + VoiceActivityDetection = new VoiceActivityDetectionOptions + { + Enabled = true, + AllowInterruption = false, + }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + Assert.AreEqual( + ActivityHandling.NoInterruption, + config.RealtimeInputConfig.ActivityHandling); + } + + [TestMethod] + public void BuildLiveConnectConfig_VadDisabled_DisablesAutomaticActivityDetection() + { + var options = new RealtimeSessionOptions + { + VoiceActivityDetection = new VoiceActivityDetectionOptions + { + Enabled = false, + }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_Tools_MappedToFunctionDeclarations() + { + var fn = AIFunctionFactory.Create( + (string city) => $"Weather in {city}: sunny", + "get_weather", + "Gets the weather"); + + var options = new RealtimeSessionOptions + { + Tools = new List { fn }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.Tools); + Assert.AreEqual(1, config.Tools.Count); + Assert.AreEqual(1, config.Tools[0].FunctionDeclarations.Count); + Assert.AreEqual("get_weather", config.Tools[0].FunctionDeclarations[0].Name); + Assert.AreEqual("Gets the weather", config.Tools[0].FunctionDeclarations[0].Description); + } + + [TestMethod] + public void BuildLiveConnectConfig_EmptyTools_NoToolsConfig() + { + var options = new RealtimeSessionOptions + { + Tools = new List(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.Tools); + } + + [TestMethod] + public void BuildLiveConnectConfig_AllOptionsCombined_MapsEverything() + { + var fn = AIFunctionFactory.Create( + (string q) => "result", + "search", + "Searches things"); + + var options = new RealtimeSessionOptions + { + Instructions = "Be concise.", + OutputModalities = new List { "audio", "text" }, + Voice = "Aoede", + MaxOutputTokens = 1000, + Tools = new List { fn }, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual("Be concise.", config.SystemInstruction.Parts[0].Text); + Assert.AreEqual(2, config.ResponseModalities.Count); + Assert.AreEqual("Aoede", config.SpeechConfig.VoiceConfig.PrebuiltVoiceConfig.VoiceName); + Assert.AreEqual(1000, config.GenerationConfig.MaxOutputTokens); + Assert.AreEqual(1, config.Tools[0].FunctionDeclarations.Count); + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNotNull(config.OutputAudioTranscription); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + #endregion + + #region ExtractDataBytes Tests + + [TestMethod] + public void ExtractDataBytes_ValidBase64DataUri_ExtractsBytes() + { + byte[] expected = new byte[] { 1, 2, 3, 4, 5 }; + string base64 = Convert.ToBase64String(expected); + var content = new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void ExtractDataBytes_InvalidBase64_FallsBackToData() + { + byte[] expected = new byte[] { 10, 20, 30 }; + var content = new DataContent(expected, "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void ExtractDataBytes_NullUri_UsesDirectData() + { + byte[] expected = new byte[] { 7, 8, 9 }; + var content = new DataContent(expected, "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void ExtractDataBytes_DataUriNoComma_FallsBackToData() + { + byte[] expected = new byte[] { 42, 43, 44 }; + // DataContent with raw byte data but no URI + var content = new DataContent(expected, "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + #endregion + + #region CreateResponseRealtimeClientMessage Tests + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterConversationItem_SendsTurnComplete() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Send a conversation item first (sets _lastInputWasRealtime = false) + var itemMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + await _session.SendAsync(itemMsg); + + sentMessages.Clear(); + + // CreateResponse should send TurnComplete since last input was NOT realtime + var createResp = new CreateResponseRealtimeClientMessage(); + await _session.SendAsync(createResp); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("turnComplete"), + "Should send turnComplete when last input was conversation item (not realtime audio)"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterAudioCommit_DoesNotSendTurnComplete() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Send audio append + commit (sets _lastInputWasRealtime = true) + byte[] audioData = new byte[100]; + new Random(42).NextBytes(audioData); + string base64 = Convert.ToBase64String(audioData); + + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + int countBeforeCreateResponse = sentMessages.Count; + + // CreateResponse should NOT send anything (audio commit already sent ActivityEnd) + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(countBeforeCreateResponse, sentMessages.Count, + "CreateResponse should not send TurnComplete after realtime audio input"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterToolResponse_DoesNotSendTurnComplete() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Send a function result (simulates middleware returning tool response) + var toolResultMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-1", "sunny") }, + role: ChatRole.Tool)); + await _session.SendAsync(toolResultMsg); + + // CreateResponse should flush the tool response but NOT send TurnComplete + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + // Should have exactly 1 message: the batched tool response. No TurnComplete. + Assert.AreEqual(1, sentMessages.Count, + "Should send tool response but NOT TurnComplete after function result"); + Assert.IsTrue(sentMessages[0].Contains("call-1"), + "Tool response should contain the call ID"); + Assert.IsFalse(sentMessages[0].Contains("turnComplete"), + "Should not contain turnComplete after tool response"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_BatchesMultipleToolResponses() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Simulate middleware sending separate CreateConversationItem per function result + var toolResult1 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-1", "sunny") }, + role: ChatRole.Tool)); + var toolResult2 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-2", "72F") }, + role: ChatRole.Tool)); + + await _session.SendAsync(toolResult1); + await _session.SendAsync(toolResult2); + + // No WebSocket sends yet — results are buffered + Assert.AreEqual(0, sentMessages.Count, + "Function results should be buffered until CreateResponse"); + + // CreateResponse flushes all results in a single batched SendToolResponseAsync + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(1, sentMessages.Count, + "All function results should be sent in a single WebSocket message"); + Assert.IsTrue(sentMessages[0].Contains("call-1"), + "Batched tool response should contain first call ID"); + Assert.IsTrue(sentMessages[0].Contains("call-2"), + "Batched tool response should contain second call ID"); + Assert.IsFalse(sentMessages[0].Contains("turnComplete"), + "Should not contain turnComplete after batched tool responses"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterToolResponse_NextTextSendResetsFlagCorrectly() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Complete a full tool call cycle: tool response → CreateResponse + var toolResultMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-1", "sunny") }, + role: ChatRole.Tool)); + await _session.SendAsync(toolResultMsg); + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + sentMessages.Clear(); + + // Now send normal text → CreateResponse. This SHOULD send TurnComplete + var textMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello again") }, + role: ChatRole.User)); + await _session.SendAsync(textMsg); + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + // Should have 2 messages: the text content and TurnComplete + Assert.AreEqual(2, sentMessages.Count, + "Normal text followed by CreateResponse should send content + TurnComplete"); + Assert.IsTrue(sentMessages[1].Contains("turnComplete"), + "Should send turnComplete for normal text input after tool cycle completes"); + } + + #endregion + + #region Additional MapServerMessage Tests + + [TestMethod] + public async Task GetStreamingResponseAsync_GenerationComplete_EmitsResponseDone() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Response" } } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent { GenerationComplete = true } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCallCancellation_EmitsRawContentOnly() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCallCancellation = new LiveServerToolCallCancellation + { + Ids = new List { "call-1", "call-2" }, + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.RawContentOnly, messages[0].Type); + Assert.IsNotNull(messages[0].RawRepresentation); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MultipleTextParts_EmitsMultipleDeltas() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List + { + new Part { Text = "Part one" }, + new Part { Text = "Part two" }, + } + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + 2 OutputTextDelta + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[2].Type); + Assert.AreEqual("Part one", ((OutputTextAudioRealtimeServerMessage)messages[1]).Text); + Assert.AreEqual("Part two", ((OutputTextAudioRealtimeServerMessage)messages[2]).Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MixedAudioAndText_EmitsBothDeltas() + { + byte[] audioBytes = new byte[] { 10, 20, 30 }; + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List + { + new Part + { + InlineData = new Blob + { + Data = audioBytes, + MimeType = "audio/pcm", + } + }, + new Part { Text = "Hello there" }, + } + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + OutputAudioDelta + OutputTextDelta + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputAudioDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[2].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MultipleModelTurns_OnlyOneResponseCreated() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "First chunk" } } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Second chunk" } } + } + } + }, + }); + + var messages = await CollectMessages(); + + // Only one ResponseCreated for the entire response cycle + var responseCreatedCount = messages.Count(m => m.Type == RealtimeServerMessageType.ResponseCreated); + Assert.AreEqual(1, responseCreatedCount, + "Should only emit one ResponseCreated across multiple ModelTurn messages in the same response"); + + // But both text deltas should appear + var textDeltas = messages.Where(m => m.Type == RealtimeServerMessageType.OutputTextDelta).ToList(); + Assert.AreEqual(2, textDeltas.Count); + Assert.AreEqual("First chunk", ((OutputTextAudioRealtimeServerMessage)textDeltas[0]).Text); + Assert.AreEqual("Second chunk", ((OutputTextAudioRealtimeServerMessage)textDeltas[1]).Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MultipleToolCalls_EmitsAllItems() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "c1", Name = "fn1", Args = new Dictionary { ["a"] = "1" } }, + new FunctionCall { Id = "c2", Name = "fn2", Args = new Dictionary { ["b"] = "2" } }, + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + (ResponseOutputItemAdded + ResponseOutputItemDone) × 2 + Assert.AreEqual(5, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[2].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[3].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[4].Type); + + var fn1 = ((ResponseOutputItemRealtimeServerMessage)messages[1]).Item?.Contents?.OfType().First(); + var fn2 = ((ResponseOutputItemRealtimeServerMessage)messages[3]).Item?.Contents?.OfType().First(); + Assert.AreEqual("fn1", fn1?.Name); + Assert.AreEqual("fn2", fn2?.Name); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCallThenTurnComplete_EmitsResponseDone() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "c1", Name = "fn1" } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent { TurnComplete = true } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + ItemAdded + ItemDone + ResponseDone + Assert.AreEqual(4, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[3].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCallWithNullArgs_EmitsEmptyArguments() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "c1", Name = "no_args_fn", Args = null } + } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(3, messages.Count); + var fc = ((ResponseOutputItemRealtimeServerMessage)messages[1]).Item?.Contents?.OfType().First(); + Assert.IsNotNull(fc); + Assert.AreEqual("no_args_fn", fc.Name); + Assert.IsNull(fc.Arguments); + } + + #endregion + + #region ConversationItemCreate Additional Tests + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_UserRole_MapsToUser() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("User text") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("user"), + "User role should map to 'user'"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_NullRole_DefaultsToUser() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("No role text") })); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("user"), + "Null role should default to 'user'"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_MultipleFunctionResults_SendsAll() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Multiple function results should all be sent in a single SendToolResponseAsync call + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List + { + new FunctionResultContent("call-1", "result-one"), + new FunctionResultContent("call-2", "result-two"), + }, + role: ChatRole.Tool)); + + await _session.SendAsync(msg); + + // Function results are buffered until CreateResponse + Assert.AreEqual(0, sentMessages.Count, + "Function results should be buffered, not sent immediately"); + + // Flush via CreateResponse + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + // All function results are batched into one WebSocket message + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("call-1")); + Assert.IsTrue(sentMessages[0].Contains("result-one")); + Assert.IsTrue(sentMessages[0].Contains("call-2")); + Assert.IsTrue(sentMessages[0].Contains("result-two")); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_NullContents_DoesNotSend() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List())); + + await _session.SendAsync(msg); + + Assert.AreEqual(0, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_AudioContent_SendsInlineData() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("audio/pcm")); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_ImageContent_SendsInlineData() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + byte[] imageData = new byte[] { 0x89, 0x50, 0x4E, 0x47 }; + string base64 = Convert.ToBase64String(imageData); + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new DataContent($"data:image/png;base64,{base64}", "image/png") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("image/png")); + } + + #endregion + + #region Audio Frame Content Verification Tests + + [TestMethod] + public async Task SendAsync_AudioCommit_FrameContentVerification_PreservesData() + { + // Use a known pattern so we can verify exact bytes + byte[] audioData = new byte[64_000]; // Exactly 2 frames + for (int i = 0; i < audioData.Length; i++) + { + audioData[i] = (byte)(i % 256); + } + + string base64 = Convert.ToBase64String(audioData); + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 2 audio frames + ActivityEnd = 4 messages + Assert.AreEqual(4, sentMessages.Count); + + // Verify the first message is ActivityStart + Assert.IsTrue(sentMessages[0].Contains("activityStart"), + "First message should be ActivityStart"); + + // Verify audio frames contain base64-encoded data + Assert.IsTrue(sentMessages[1].Contains("audio/pcm"), + "Audio frame should contain mime type"); + Assert.IsTrue(sentMessages[2].Contains("audio/pcm"), + "Audio frame should contain mime type"); + + // Verify the last message is ActivityEnd + Assert.IsTrue(sentMessages[3].Contains("activityEnd"), + "Last message should be ActivityEnd"); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_ExactFrameSize_SingleFrame() + { + // Exactly 32000 bytes = exactly 1 frame (no splitting needed) + byte[] audioData = new byte[32_000]; + new Random(42).NextBytes(audioData); + + string base64 = Convert.ToBase64String(audioData); + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 1 frame + ActivityEnd = 3 + Assert.AreEqual(3, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_FrameBoundary_SplitsCorrectly() + { + // 32001 bytes = 32000 + 1 → 2 frames + byte[] audioData = new byte[32_001]; + new Random(42).NextBytes(audioData); + + string base64 = Convert.ToBase64String(audioData); + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 2 frames (32000 + 1) + ActivityEnd = 4 + Assert.AreEqual(4, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_MultipleAppends_PreservesOrder() + { + byte[] chunk1 = new byte[100]; + byte[] chunk2 = new byte[200]; + new Random(42).NextBytes(chunk1); + new Random(43).NextBytes(chunk2); + + string base64_1 = Convert.ToBase64String(chunk1); + string base64_2 = Convert.ToBase64String(chunk2); + + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64_1}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64_2}", "audio/pcm"))); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 2 audio frames (both under 32KB) + ActivityEnd = 4 + Assert.AreEqual(4, sentMessages.Count); + } + + #endregion + + #region Audio MIME Type Tests + + [TestMethod] + public async Task SendAsync_AudioCommit_UsesDefaultMimeType_WhenNoInputAudioFormat() + { + // Default session (null options) should use audio/pcm + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Verify audio frame uses default audio/pcm mime type + Assert.IsTrue(sentMessages[1].Contains("audio/pcm"), "Audio frame should use default audio/pcm"); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_UsesCustomMimeType_WhenInputAudioFormatSet() + { + // Create session with custom audio format + var customOptions = new RealtimeSessionOptions + { + InputAudioFormat = new RealtimeAudioFormat("audio/opus", 48000), + }; + var customSession = new GoogleGenAIRealtimeSession(_asyncSession, "test-model", customOptions); + + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await customSession.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/opus;base64,{base64}", "audio/opus"))); + await customSession.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Verify audio frame uses the custom mime type + Assert.IsTrue(sentMessages[1].Contains("audio/opus"), + "Audio frame should use the configured audio/opus mime type"); + Assert.IsFalse(sentMessages[1].Contains("audio/pcm"), + "Audio frame should NOT contain the default audio/pcm"); + } + + #endregion + + #region Dispose Race Safety Tests + + [TestMethod] + public async Task SendAsync_ConcurrentDispose_DoesNotThrow() + { + // Simulate a race: SendAsync acquires the lock, then DisposeAsync runs. + // The Release() in finally should not throw even if the semaphore is disposed. + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Returns(Task.CompletedTask); + + _mockWebSocket + .Setup(ws => ws.CloseAsync( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Task.CompletedTask); + + // Append audio, then commit and dispose concurrently + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Dispose should not throw even after sends completed + await _session.DisposeAsync(); + + // Double dispose should also be safe + await _session.DisposeAsync(); + } + + #endregion + + #region DisposeAsync Additional Tests + + [TestMethod] + public async Task DisposeAsync_ClosesUnderlyingWebSocket() + { + _mockWebSocket + .Setup(ws => ws.CloseAsync( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Task.CompletedTask); + + await _session.DisposeAsync(); + + _mockWebSocket.Verify(ws => ws.CloseAsync( + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Once); + } + + #endregion + + #region Exception Handling Tests + + [TestMethod] + public async Task SendAsync_ObjectDisposedException_FromAsyncSession_Swallowed() + { + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .ThrowsAsync(new ObjectDisposedException("WebSocket")); + + // Send a conversation item that triggers a real send to the WebSocket + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // Should NOT throw — teardown exception is swallowed + await _session.SendAsync(msg); + } + + [TestMethod] + public async Task SendAsync_WebSocketException_FromAsyncSession_Swallowed() + { + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .ThrowsAsync(new WebSocketException("connection lost")); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // Should NOT throw — teardown exception is swallowed + await _session.SendAsync(msg); + } + + [TestMethod] + public async Task SendAsync_InternalOperationCancelled_Swallowed() + { + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .ThrowsAsync(new OperationCanceledException("internal disposal")); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // Should NOT throw — internal cancellation (not the caller's token) is swallowed + await _session.SendAsync(msg); + } + + [TestMethod] + public async Task SendAsync_CallerCancellation_Propagated() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // Caller cancellation should propagate (ThrowIfCancellationRequested at top) + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(msg, cts.Token)); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ObjectDisposedException_Swallowed() + { + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ThrowsAsync(new ObjectDisposedException("WebSocket")); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_InternalOperationCancelled_Swallowed() + { + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ThrowsAsync(new OperationCanceledException("internal cancellation")); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + #endregion + + #region Helper Methods + + private void SetupReceiveOnce(LiveServerMessage message) + { + string json = JsonSerializer.Serialize(message, JsonConfig.JsonSerializerOptions); + byte[] bytes = Encoding.UTF8.GetBytes(json); + var callCount = 0; + + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .Callback, CancellationToken>((buffer, _) => + { + if (callCount == 0) + { + Buffer.BlockCopy(bytes, 0, buffer.Array!, buffer.Offset, bytes.Length); + } + }) + .Returns, CancellationToken>((_, _) => + { + callCount++; + if (callCount == 1) + { + return Task.FromResult( + new WebSocketReceiveResult(bytes.Length, WebSocketMessageType.Text, true)); + } + return Task.FromResult( + new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, + WebSocketCloseStatus.NormalClosure, "done")); + }); + } + + private void SetupReceiveSequence(LiveServerMessage[] messages) + { + var serialized = messages.Select(m => + { + string json = JsonSerializer.Serialize(m, JsonConfig.JsonSerializerOptions); + return Encoding.UTF8.GetBytes(json); + }).ToList(); + + var callCount = 0; + + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .Callback, CancellationToken>((buffer, _) => + { + if (callCount < serialized.Count) + { + Buffer.BlockCopy(serialized[callCount], 0, buffer.Array!, buffer.Offset, + serialized[callCount].Length); + } + }) + .Returns, CancellationToken>((_, _) => + { + var idx = callCount; + callCount++; + if (idx < serialized.Count) + { + return Task.FromResult( + new WebSocketReceiveResult(serialized[idx].Length, WebSocketMessageType.Text, true)); + } + return Task.FromResult( + new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, + WebSocketCloseStatus.NormalClosure, "done")); + }); + } + + private async Task> CollectMessages() + { + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + return messages; + } + + #endregion + + #region Concurrency Tests + + [TestMethod] + public async Task SendAsync_ConcurrentCalls_AreSerialized() + { + // Verify that concurrent SendAsync calls don't interleave WebSocket sends. + // We track the order of send completions to ensure no overlap. + var sendOrder = new List(); + var gate = new SemaphoreSlim(0); + + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Returns, WebSocketMessageType, bool, CancellationToken>( + async (buffer, _, _, ct) => + { + string json = Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count); + // The first call blocks briefly; if sends aren't serialized, the second + // would start before the first completes. + if (json.Contains("turn-1")) + { + lock (sendOrder) { sendOrder.Add(1); } + gate.Release(); + await Task.Delay(50, ct); + lock (sendOrder) { sendOrder.Add(-1); } + } + else if (json.Contains("turn-2")) + { + await gate.WaitAsync(ct); + lock (sendOrder) { sendOrder.Add(2); } + } + }); + + var msg1 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("turn-1") }, role: ChatRole.User)); + var msg2 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("turn-2") }, role: ChatRole.User)); + + // Launch both sends concurrently + var t1 = _session.SendAsync(msg1); + var t2 = _session.SendAsync(msg2); + await Task.WhenAll(t1, t2); + + // Because sends are serialized, turn-2 can only start after turn-1 completes. + // Expected order: [1, -1, 2] (start-1, end-1, start-2) + Assert.AreEqual(3, sendOrder.Count); + Assert.AreEqual(1, sendOrder[0], "turn-1 should start first"); + Assert.AreEqual(-1, sendOrder[1], "turn-1 should complete before turn-2 starts"); + Assert.AreEqual(2, sendOrder[2], "turn-2 should start after turn-1 completes"); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_DoesNotBlockConcurrentSends() + { + // AudioAppend only buffers — it should NOT acquire the send lock, + // so it completes even while another send holds the lock. + var sendStarted = new TaskCompletionSource(); + var sendCanProceed = new TaskCompletionSource(); + + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Returns, WebSocketMessageType, bool, CancellationToken>( + async (_, _, _, ct) => + { + sendStarted.SetResult(true); + await sendCanProceed.Task; + }); + + // Start a text send that will hold the lock + var textMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("hello") }, role: ChatRole.User)); + var textSend = _session.SendAsync(textMsg); + + // Wait until the text send has acquired the lock and is in-flight + await sendStarted.Task; + + // AudioAppend should complete immediately (no lock contention) + var audioContent = new DataContent("data:audio/pcm;base64,AQID", "audio/pcm"); + var audioAppend = new InputAudioBufferAppendRealtimeClientMessage(audioContent); + var appendTask = _session.SendAsync(audioAppend); + + // Append should complete quickly even though the text send holds the lock + var completed = await Task.WhenAny(appendTask, Task.Delay(1000)); + Assert.AreSame(appendTask, completed, "AudioAppend should not block on the send lock"); + + // Release the text send + sendCanProceed.SetResult(true); + await textSend; + } + + #endregion +} + +#pragma warning restore MEAI001 diff --git a/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json b/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json index a23b4a43..7d38165e 100644 --- a/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json +++ b/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json @@ -263,7 +263,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -280,9 +280,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/Google.GenAI.Tests/packages.lock.json b/Google.GenAI.Tests/packages.lock.json index 36019bfe..adf25881 100644 --- a/Google.GenAI.Tests/packages.lock.json +++ b/Google.GenAI.Tests/packages.lock.json @@ -247,7 +247,7 @@ "type": "Project", "dependencies": { "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", "MimeTypes": "[2.5.2, )" } }, @@ -264,9 +264,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "CentralTransitive", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/Google.GenAI/GoogleGenAIExtensions.cs b/Google.GenAI/GoogleGenAIExtensions.cs index 3396872b..f3235e14 100644 --- a/Google.GenAI/GoogleGenAIExtensions.cs +++ b/Google.GenAI/GoogleGenAIExtensions.cs @@ -129,4 +129,21 @@ public static IHostedFileClient AsIHostedFileClient(this Files files) Utilities.ThrowIfNull(files, nameof(files)); return new GoogleGenAIHostedFileClient(files); } + + /// + /// Creates an wrapper around the specified + /// for use with the Google GenAI Live API. + /// + /// The to wrap. + /// The default model ID to use for realtime sessions. + /// An that wraps the specified client. + /// is . +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.Experimental("MEAI001")] +#endif + public static IRealtimeClient AsIRealtimeClient(this Client client, string? defaultModelId = null) + { + Utilities.ThrowIfNull(client, nameof(client)); + return new GoogleGenAIRealtimeClient(client, defaultModelId); + } } diff --git a/Google.GenAI/GoogleGenAIRealtimeClient.cs b/Google.GenAI/GoogleGenAIRealtimeClient.cs new file mode 100644 index 00000000..dafda70a --- /dev/null +++ b/Google.GenAI/GoogleGenAIRealtimeClient.cs @@ -0,0 +1,219 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Google.GenAI; +using Google.GenAI.Types; + +namespace Microsoft.Extensions.AI; + +/// +/// Provides an implementation for Google GenAI's Live API. +/// +#if NET8_0_OR_GREATER +[System.Diagnostics.CodeAnalysis.Experimental("MEAI001")] +#endif +public sealed class GoogleGenAIRealtimeClient : IRealtimeClient +{ + private readonly Client _client; + private readonly string? _defaultModelId; + private ChatClientMetadata? _metadata; + + /// Initializes a new instance wrapping an existing . + /// The Google GenAI client. + /// The default model to use for realtime sessions (e.g. "gemini-2.5-flash-native-audio-preview-12-2025"). + /// is . + public GoogleGenAIRealtimeClient(Client client, string? defaultModelId = null) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _defaultModelId = defaultModelId; + } + + /// + public async Task CreateSessionAsync( + RealtimeSessionOptions? options = null, + CancellationToken cancellationToken = default) + { + string model = options?.Model ?? _defaultModelId + ?? throw new InvalidOperationException( + "No model specified. Provide a model via RealtimeSessionOptions.Model or the defaultModelId constructor parameter."); + + var config = BuildLiveConnectConfig(options); + + var asyncSession = await _client.Live.ConnectAsync(model, config, cancellationToken).ConfigureAwait(false); + + return new GoogleGenAIRealtimeSession(asyncSession, model, options); + } + + /// + public object? GetService(System.Type serviceType, object? serviceKey = null) + { + if (serviceType is null) + { + throw new ArgumentNullException(nameof(serviceType)); + } + + if (serviceKey is not null) + { + return null; + } + + if (serviceType == typeof(ChatClientMetadata)) + { + return _metadata ??= new ChatClientMetadata("google-genai", defaultModelId: _defaultModelId); + } + + if (serviceType.IsInstanceOfType(this)) + { + return this; + } + + if (serviceType.IsInstanceOfType(_client)) + { + return _client; + } + + return null; + } + + /// + public void Dispose() + { + // Client lifecycle is not owned by this wrapper. + } + + /// Converts MEAI session options to a Google GenAI . + internal static LiveConnectConfig BuildLiveConnectConfig(RealtimeSessionOptions? options) + { + var config = new LiveConnectConfig(); + + if (options is null) + { + config.ResponseModalities = new List { Modality.Audio }; + config.RealtimeInputConfig = new RealtimeInputConfig + { + AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true } + }; + return config; + } + + // System instructions + if (!string.IsNullOrEmpty(options.Instructions)) + { + config.SystemInstruction = new Content + { + Parts = new List { new Part { Text = options.Instructions } }, + Role = "user" + }; + } + + // Output modalities + if (options.OutputModalities is { Count: > 0 }) + { + config.ResponseModalities = new List(); + foreach (var modality in options.OutputModalities) + { + config.ResponseModalities.Add(modality.ToLowerInvariant() switch + { + "audio" => Modality.Audio, + "text" => Modality.Text, + _ => Modality.Text, + }); + } + } + else + { + config.ResponseModalities = new List { Modality.Audio }; + } + + // Voice / speech config + if (!string.IsNullOrEmpty(options.Voice)) + { + config.SpeechConfig = new SpeechConfig + { + VoiceConfig = new VoiceConfig + { + PrebuiltVoiceConfig = new PrebuiltVoiceConfig + { + VoiceName = options.Voice, + } + } + }; + } + + // Generation config + if (options.MaxOutputTokens.HasValue) + { + config.GenerationConfig ??= new GenerationConfig(); + config.GenerationConfig.MaxOutputTokens = options.MaxOutputTokens.Value; + } + + // Tools (AIFunction → Google FunctionDeclaration) + if (options.Tools is { Count: > 0 }) + { + var functionDeclarations = new List(); + foreach (var tool in options.Tools) + { + if (tool is AIFunction aiFunction) + { + functionDeclarations.Add(GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(aiFunction)); + } + } + + if (functionDeclarations.Count > 0) + { + config.Tools = new List + { + new Tool { FunctionDeclarations = functionDeclarations } + }; + } + } + + // Transcription + if (options.TranscriptionOptions is not null) + { + config.InputAudioTranscription = new AudioTranscriptionConfig(); + config.OutputAudioTranscription = new AudioTranscriptionConfig(); + } + + // Configure VAD / activity detection based on MEAI options. + config.RealtimeInputConfig = new RealtimeInputConfig(); + + if (options.VoiceActivityDetection is { Enabled: true } vad) + { + // Automatic VAD enabled — the server detects speech boundaries. + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = false }; + config.RealtimeInputConfig.ActivityHandling = vad.AllowInterruption + ? ActivityHandling.StartOfActivityInterrupts + : ActivityHandling.NoInterruption; + } + else if (options.VoiceActivityDetection is { Enabled: false }) + { + // VAD explicitly disabled — the client controls activity boundaries + // via explicit ActivityStart/ActivityEnd signals. + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; + } + else + { + // No VAD options specified — disable automatic VAD by default when using + // the MEAI audio buffering pattern (AudioBufferAppend → AudioBufferCommit). + // The client controls activity boundaries via explicit ActivityEnd signals. + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; + } + + return config; + } +} + diff --git a/Google.GenAI/GoogleGenAIRealtimeSession.cs b/Google.GenAI/GoogleGenAIRealtimeSession.cs new file mode 100644 index 00000000..4bafbf56 --- /dev/null +++ b/Google.GenAI/GoogleGenAIRealtimeSession.cs @@ -0,0 +1,748 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections.Concurrent; +using System.Linq; +using System.Net.WebSockets; +using System.Runtime.CompilerServices; +using System.Text.Json; + +using Google.GenAI; +using Google.GenAI.Types; + +namespace Microsoft.Extensions.AI; + +/// +/// Provides an implementation for Google GenAI's Live API, +/// wrapping an WebSocket connection. +/// +#if NET8_0_OR_GREATER +[System.Diagnostics.CodeAnalysis.Experimental("MEAI001")] +#endif +public sealed class GoogleGenAIRealtimeSession : IRealtimeClientSession +{ + private readonly AsyncSession _asyncSession; + private readonly ChatClientMetadata _metadata; + private int _disposed; + + // Buffer for audio chunks between Append and Commit. + // Protected by _audioBufferLock. Capped at MaxAudioBufferBytes to prevent unbounded growth. + private readonly List _audioBuffer = new(); + private readonly object _audioBufferLock = new(); + private int _audioBufferSize; + + /// Maximum buffered audio size (10 MB). Exceeding this throws . + private const int MaxAudioBufferBytes = 10 * 1024 * 1024; + + // Track whether a response is in progress to emit ResponseCreated only once per response. + // Accessed only from GetStreamingResponseAsync's single enumeration; callers must not + // enumerate concurrently. + private bool _responseInProgress; + + // Serializes all WebSocket send operations. Required because: + // 1. WebSocket.SendAsync is NOT thread-safe for concurrent calls. + // 2. FunctionInvokingRealtimeSession middleware can call SendAsync (to return + // function results) concurrently with the caller's own SendAsync (e.g., audio). + // 3. HandleAudioCommitAsync sends a multi-message sequence (ActivityStart → + // audio frames → ActivityEnd) that must be atomic. + private readonly SemaphoreSlim _sendLock = new(1, 1); + + // Track whether audio was sent via SendRealtimeInputAsync to avoid mixing with SendClientContentAsync. + private bool _lastInputWasRealtime; + + // Track whether a tool response was just sent. After SendToolResponseAsync, the server + // automatically continues generating — sending TurnComplete would be unexpected. + private bool _lastSendWasToolResponse; + + // Maps function call IDs to function names. Populated when ToolCall messages arrive, + // consumed when sending FunctionResponse back to the server. + private readonly ConcurrentDictionary _callIdToFunctionName = new(); + + // Accumulates function results across multiple CreateConversationItem sends so they can + // be batched into a single SendToolResponseAsync call. The MEAI middleware sends one + // CreateConversationItem per function result followed by a single CreateResponse. + // Gemini expects all function results in one SendToolResponseAsync call, so we buffer + // them here and flush on CreateResponse. + private readonly List _pendingToolResponses = new(); + + // When true, automatic VAD is enabled and the server handles speech boundary detection. + // ActivityStart/ActivityEnd framing is skipped during audio commit. + private readonly bool _vadEnabled; + + // The MIME type for audio frames sent to the server, derived from InputAudioFormat. + private readonly string _inputAudioMimeType; + + /// + public RealtimeSessionOptions? Options { get; private set; } + + /// Initializes a new instance wrapping a connected . + /// The connected for WebSocket communication. + /// The model name for metadata. + /// Optional initial session options. + public GoogleGenAIRealtimeSession( + AsyncSession asyncSession, + string model, + RealtimeSessionOptions? initialOptions) + { + _asyncSession = asyncSession ?? throw new ArgumentNullException(nameof(asyncSession)); + _metadata = new ChatClientMetadata("google-genai", defaultModelId: model); + Options = initialOptions; + _vadEnabled = initialOptions?.VoiceActivityDetection is { Enabled: true }; + _inputAudioMimeType = initialOptions?.InputAudioFormat?.MediaType ?? "audio/pcm"; + } + + /// + public async Task SendAsync( + RealtimeClientMessage message, + CancellationToken cancellationToken = default) + { + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } + + if (Volatile.Read(ref _disposed) != 0) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + + cancellationToken.ThrowIfCancellationRequested(); + + // AudioAppend only buffers data in memory — no WebSocket I/O, no lock needed. + if (message is InputAudioBufferAppendRealtimeClientMessage audioAppend) + { + HandleAudioAppend(audioAppend); + return; + } + + // All other message types perform WebSocket I/O and must be serialized. + // WaitAsync may throw ObjectDisposedException if DisposeAsync races between the + // _disposed check above and this call — treat it the same as a post-dispose send. + try + { + await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); + } + catch (ObjectDisposedException) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + + try + { + switch (message) + { + case InputAudioBufferCommitRealtimeClientMessage: + await HandleAudioCommitAsync(cancellationToken).ConfigureAwait(false); + break; + + case CreateConversationItemRealtimeClientMessage itemCreate: + await HandleConversationItemCreateAsync(itemCreate, cancellationToken).ConfigureAwait(false); + break; + + case SessionUpdateRealtimeClientMessage: + // Gemini's Live API does not support mid-session reconfiguration. + break; + + case CreateResponseRealtimeClientMessage: + if (_pendingToolResponses.Count > 0) + { + // Flush all buffered function results in a single SendToolResponseAsync call. + // The MEAI middleware sends one CreateConversationItem per function result, + // but Gemini expects all results in one call. + await _asyncSession.SendToolResponseAsync( + new LiveSendToolResponseParameters + { + FunctionResponses = new List(_pendingToolResponses) + }, + cancellationToken).ConfigureAwait(false); + _pendingToolResponses.Clear(); + _lastSendWasToolResponse = true; + } + + if (_lastSendWasToolResponse) + { + // After a tool response, Gemini automatically continues generating. + // Do not send TurnComplete — it would cause the server to close the connection. + _lastSendWasToolResponse = false; + } + else if (!_lastInputWasRealtime) + { + await _asyncSession.SendClientContentAsync( + new LiveSendClientContentParameters { TurnComplete = true }, + cancellationToken).ConfigureAwait(false); + } + break; + + default: + break; + } + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + // The caller explicitly cancelled via their token — propagate so they + // can observe the cancellation they requested. + throw; + } + catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException or WebSocketException) + { + // These exceptions are expected during session teardown and are swallowed: + // - OperationCanceledException: internal cancellation from disposal (not the caller's token). + // - ObjectDisposedException: DisposeAsync was called on another thread while an + // operation was in-flight on the underlying WebSocket. + // - WebSocketException: the connection was closed (server disconnect or local close). + } + finally + { + try + { + _sendLock.Release(); + } + catch (ObjectDisposedException) + { + // DisposeAsync was called concurrently and disposed the semaphore. + } + } + } + + /// + public async IAsyncEnumerable GetStreamingResponseAsync( + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + if (Volatile.Read(ref _disposed) != 0) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + + while (!cancellationToken.IsCancellationRequested) + { + LiveServerMessage? serverMessage; + try + { + serverMessage = await _asyncSession.ReceiveAsync(cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + // The caller explicitly cancelled via their token — propagate so they + // can observe the cancellation they requested. + throw; + } + catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException or WebSocketException) + { + // These exceptions are expected during session teardown and are swallowed: + // - OperationCanceledException: internal cancellation from disposal (not the caller's token). + // - ObjectDisposedException: DisposeAsync was called on another thread while an + // operation was in-flight on the underlying WebSocket. + // - WebSocketException: the connection was closed (server disconnect or local close). + yield break; + } + + if (serverMessage is null) + { + yield break; + } + + // Map Google Live server messages to MEAI server message types + foreach (var mapped in MapServerMessage(serverMessage)) + { + yield return mapped; + } + } + } + + /// + public object? GetService(System.Type serviceType, object? serviceKey = null) + { + if (serviceType is null) + { + throw new ArgumentNullException(nameof(serviceType)); + } + + if (serviceKey is not null) + { + return null; + } + + if (serviceType == typeof(ChatClientMetadata)) + { + return _metadata; + } + + if (serviceType.IsInstanceOfType(this)) + { + return this; + } + + if (serviceType.IsInstanceOfType(_asyncSession)) + { + return _asyncSession; + } + + return null; + } + + /// + public async ValueTask DisposeAsync() + { + if (Interlocked.Exchange(ref _disposed, 1) != 0) + { + return; + } + + _responseInProgress = false; + await _asyncSession.DisposeAsync().ConfigureAwait(false); + _sendLock.Dispose(); + } + + #region Send Helpers (MEAI → Google GenAI) + + private void HandleAudioAppend( + InputAudioBufferAppendRealtimeClientMessage audioAppend) + { + if (audioAppend.Content is null || !audioAppend.Content.HasTopLevelMediaType("audio")) + { + return; + } + + byte[] audioBytes = ExtractDataBytes(audioAppend.Content); + + // Buffer audio data; it will be sent on commit with proper activity framing. + lock (_audioBufferLock) + { + if (_audioBufferSize + audioBytes.Length > MaxAudioBufferBytes) + { + throw new InvalidOperationException( + $"Audio buffer would exceed {MaxAudioBufferBytes} bytes. " + + "Call AudioBufferCommit before appending more audio."); + } + + _audioBuffer.Add(audioBytes); + _audioBufferSize += audioBytes.Length; + } + } + + private async Task HandleAudioCommitAsync(CancellationToken cancellationToken) + { + List bufferedChunks; + lock (_audioBufferLock) + { + if (_audioBuffer.Count == 0) + { + return; + } + + // Snapshot and clear the buffer. Avoids consolidating all chunks into a + // single array only to re-split — instead we send each buffered chunk directly. + bufferedChunks = new List(_audioBuffer); + _audioBuffer.Clear(); + _audioBufferSize = 0; + } + + _lastInputWasRealtime = true; + + // When VAD is disabled, explicit ActivityStart/ActivityEnd framing is required. + // ActivityStart marks the beginning of user speech; ActivityEnd triggers model response. + // When VAD is enabled, the server auto-detects speech boundaries — skip framing. + if (!_vadEnabled) + { + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + ActivityStart = new ActivityStart() + }, + cancellationToken).ConfigureAwait(false); + } + + // Send buffered chunks directly, splitting only those that exceed the frame size limit. + const int maxFrameBytes = 32_000; + foreach (var buffered in bufferedChunks) + { + if (buffered.Length <= maxFrameBytes) + { + // Common case: chunk fits in a single frame — send without copying + await SendAudioFrameAsync(buffered, cancellationToken).ConfigureAwait(false); + } + else + { + // Large chunk: split into frames + for (int i = 0; i < buffered.Length; i += maxFrameBytes) + { + int len = Math.Min(maxFrameBytes, buffered.Length - i); + byte[] frame = new byte[len]; + Buffer.BlockCopy(buffered, i, frame, 0, len); + await SendAudioFrameAsync(frame, cancellationToken).ConfigureAwait(false); + } + } + } + + // When VAD is disabled, signal end of user activity — this triggers the model to respond. + // When VAD is enabled, the server detects end of speech automatically. + if (!_vadEnabled) + { + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + ActivityEnd = new ActivityEnd() + }, + cancellationToken).ConfigureAwait(false); + } + } + + private Task SendAudioFrameAsync(byte[] data, CancellationToken cancellationToken) + { + return _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Audio = new Blob + { + Data = data, + MimeType = _inputAudioMimeType, + } + }, + cancellationToken); + } + + private async Task HandleConversationItemCreateAsync( + CreateConversationItemRealtimeClientMessage itemCreate, + CancellationToken cancellationToken) + { + if (itemCreate.Item?.Contents is null or { Count: 0 }) + { + return; + } + + // Collect all function results (tool responses use a separate API call). + var functionResults = new List(); + foreach (var content in itemCreate.Item.Contents) + { + if (content is FunctionResultContent functionResult) + { + _callIdToFunctionName.TryRemove(functionResult.CallId, out var functionName); + functionResults.Add(new FunctionResponse + { + Id = functionResult.CallId, + Name = functionName ?? string.Empty, + Response = new Dictionary + { + ["result"] = functionResult.Result?.ToString() ?? string.Empty + } + }); + } + } + + if (functionResults.Count > 0) + { + // Buffer function results — they will be flushed as a single batched + // SendToolResponseAsync call when CreateResponse arrives. + _pendingToolResponses.AddRange(functionResults); + _lastSendWasToolResponse = true; + return; + } + + // Otherwise, treat as text/content conversation input + var parts = new List(); + foreach (var content in itemCreate.Item.Contents) + { + if (content is TextContent textContent && !string.IsNullOrEmpty(textContent.Text)) + { + parts.Add(new Part { Text = textContent.Text }); + } + else if (content is DataContent dataContent) + { + if (dataContent.HasTopLevelMediaType("audio")) + { + parts.Add(new Part + { + InlineData = new Blob + { + Data = ExtractDataBytes(dataContent), + MimeType = dataContent.MediaType ?? "audio/pcm", + } + }); + } + else if (dataContent.HasTopLevelMediaType("image")) + { + byte[] imageBytes = ExtractDataBytes(dataContent); + parts.Add(new Part + { + InlineData = new Blob + { + Data = imageBytes, + MimeType = dataContent.MediaType ?? "image/png", + } + }); + } + } + } + + if (parts.Count == 0) + { + return; + } + + string role = itemCreate.Item.Role?.Value switch + { + "assistant" => "model", + _ => "user", + }; + + _lastInputWasRealtime = false; + _lastSendWasToolResponse = false; + await _asyncSession.SendClientContentAsync( + new LiveSendClientContentParameters + { + Turns = new List + { + new Content + { + Parts = parts, + Role = role, + } + }, + }, + cancellationToken).ConfigureAwait(false); + } + + internal static byte[] ExtractDataBytes(DataContent content) + { + string? dataUri = content.Uri?.ToString(); + + if (dataUri is not null) + { + int commaIndex = dataUri.LastIndexOf(','); + if (commaIndex >= 0 && commaIndex < dataUri.Length - 1) + { + string base64 = dataUri.Substring(commaIndex + 1); + try + { + return Convert.FromBase64String(base64); + } + catch (FormatException) + { + // Fall through to content.Data.ToArray() below + } + } + } + + return content.Data.ToArray(); + } + + #endregion + + #region Receive Helpers (Google GenAI → MEAI) + + private IEnumerable MapServerMessage(LiveServerMessage serverMessage) + { + // SetupComplete — skip (internal protocol message, not relevant to MEAI consumers) + if (serverMessage.SetupComplete is not null) + { + yield break; + } + + // Server content (model responses — audio, text, transcription) + if (serverMessage.ServerContent is { } serverContent) + { + foreach (var msg in MapServerContent(serverContent, serverMessage)) + { + yield return msg; + } + } + + // Tool calls — emit ResponseCreated (if not already), then ResponseOutputItemAdded + ResponseOutputItemDone for each + if (serverMessage.ToolCall is { FunctionCalls: { Count: > 0 } functionCalls }) + { + if (!_responseInProgress) + { + _responseInProgress = true; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseCreated) + { + RawRepresentation = serverMessage, + }; + } + + foreach (var fc in functionCalls) + { + if (fc.Id is not null && fc.Name is not null) + { + _callIdToFunctionName[fc.Id] = fc.Name; + } + + var contents = new List + { + new FunctionCallContent( + fc.Id ?? string.Empty, + fc.Name ?? string.Empty, + fc.Args?.ToDictionary(kvp => kvp.Key, kvp => (object?)kvp.Value)) + }; + + var item = new RealtimeConversationItem(contents, id: fc.Id, role: ChatRole.Assistant); + + // Emit ResponseOutputItemAdded (signals start of output item) + yield return new ResponseOutputItemRealtimeServerMessage(RealtimeServerMessageType.ResponseOutputItemAdded) + { + Item = item, + RawRepresentation = serverMessage, + }; + + // Emit ResponseOutputItemDone (required by FunctionInvokingRealtimeSession middleware) + yield return new ResponseOutputItemRealtimeServerMessage(RealtimeServerMessageType.ResponseOutputItemDone) + { + Item = item, + RawRepresentation = serverMessage, + }; + } + } + + // Tool call cancellation + if (serverMessage.ToolCallCancellation is { Ids: { Count: > 0 } }) + { + yield return new RealtimeServerMessage + { + Type = RealtimeServerMessageType.RawContentOnly, + RawRepresentation = serverMessage, + }; + } + + // Usage metadata — emit as ResponseDone only if one wasn't already emitted + // by TurnComplete/GenerationComplete above (which resets _responseInProgress). + if (serverMessage.UsageMetadata is { } usage && _responseInProgress) + { + _responseInProgress = false; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseDone) + { + Usage = new UsageDetails + { + InputTokenCount = usage.PromptTokenCount ?? 0, + OutputTokenCount = usage.ResponseTokenCount ?? 0, + TotalTokenCount = usage.TotalTokenCount ?? 0, + }, + RawRepresentation = serverMessage, + }; + } + + // GoAway (server disconnect) + if (serverMessage.GoAway is not null) + { + yield return new ErrorRealtimeServerMessage + { + Error = new ErrorContent("Server is disconnecting (GoAway)"), + RawRepresentation = serverMessage, + }; + } + } + + private IEnumerable MapServerContent( + LiveServerContent serverContent, + LiveServerMessage rawMessage) + { + if (serverContent.ModelTurn?.Parts is { Count: > 0 } parts) + { + // Emit ResponseCreated once when a new response cycle begins + if (!_responseInProgress) + { + _responseInProgress = true; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseCreated) + { + RawRepresentation = rawMessage, + }; + } + + foreach (var part in parts) + { + // Audio data + if (part.InlineData is { Data: not null } blob && + blob.MimeType?.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) == true) + { + yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputAudioDelta) + { + Audio = Convert.ToBase64String(blob.Data), + RawRepresentation = rawMessage, + }; + } + + // Text response + if (!string.IsNullOrEmpty(part.Text)) + { + yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputTextDelta) + { + Text = part.Text, + RawRepresentation = rawMessage, + }; + } + } + } + + // Input transcription + if (serverContent.InputTranscription is { Text: not null } inputTranscription) + { + yield return new InputAudioTranscriptionRealtimeServerMessage(RealtimeServerMessageType.InputAudioTranscriptionCompleted) + { + Transcription = inputTranscription.Text, + RawRepresentation = rawMessage, + }; + } + + // Output transcription + if (serverContent.OutputTranscription is { Text: not null } outputTranscription) + { + yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputAudioTranscriptionDelta) + { + Text = outputTranscription.Text, + RawRepresentation = rawMessage, + }; + } + + // Turn complete or generation complete — reset response tracking and emit ResponseDone + if (serverContent.TurnComplete == true || serverContent.GenerationComplete == true) + { + _responseInProgress = false; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseDone) + { + RawRepresentation = rawMessage, + }; + } + } + + #endregion + + #region Tool Mapping Helpers + + /// + /// Converts an to a Google GenAI , + /// mapping the function name, description, and JSON schema for parameters. + /// + /// The AI function to convert. + /// A Google GenAI function declaration. + internal static FunctionDeclaration ToGoogleFunctionDeclaration(AIFunction aiFunction) + { + var declaration = new FunctionDeclaration + { + Name = aiFunction.Name, + Description = aiFunction.Description, + }; + + // Map the JSON schema for parameters + if (aiFunction.JsonSchema is JsonElement schemaElement && + schemaElement.ValueKind != JsonValueKind.Undefined) + { + declaration.ParametersJsonSchema = schemaElement; + } + + return declaration; + } + + #endregion +} + diff --git a/Google.GenAI/Live.cs b/Google.GenAI/Live.cs index fa8c58fe..476165d8 100644 --- a/Google.GenAI/Live.cs +++ b/Google.GenAI/Live.cs @@ -309,7 +309,7 @@ public async Task SendToolResponseAsync(LiveSendToolResponseParameters toolRespo } var buffer = new byte[4096]; - var messageBuilder = new StringBuilder(); + using var messageStream = new MemoryStream(); WebSocketReceiveResult result; try @@ -321,7 +321,7 @@ public async Task SendToolResponseAsync(LiveSendToolResponseParameters toolRespo { return null; } - messageBuilder.Append(Encoding.UTF8.GetString(buffer, 0, result.Count)); + messageStream.Write(buffer, 0, result.Count); } while (!result.EndOfMessage); } @@ -330,7 +330,7 @@ public async Task SendToolResponseAsync(LiveSendToolResponseParameters toolRespo return null; } - var messageString = messageBuilder.ToString(); + var messageString = Encoding.UTF8.GetString(messageStream.GetBuffer(), 0, (int)messageStream.Length); if (string.IsNullOrEmpty(messageString)) { throw new InvalidOperationException("Received an empty message from the server."); diff --git a/Google.GenAI/packages.lock.json b/Google.GenAI/packages.lock.json index 59d68c6c..1151b2b2 100644 --- a/Google.GenAI/packages.lock.json +++ b/Google.GenAI/packages.lock.json @@ -15,9 +15,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "Direct", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } @@ -189,9 +189,9 @@ }, "Microsoft.Extensions.AI.Abstractions": { "type": "Direct", - "requested": "[10.4.0, )", - "resolved": "10.4.0", - "contentHash": "t3S2H4do4YeNheIfE3GEl3MnKIrnxpbLu7a88spfApYR3in9ddhIq/GMtxgMaFjn/PUMTCFv5YH7Y6Q91dsDXQ==", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", "dependencies": { "System.Text.Json": "10.0.4" } diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..95e879ef --- /dev/null +++ b/nuget.config @@ -0,0 +1,6 @@ + + + + + + From 6121fcf4f731c2f51f5f62b35d1d8856806989cd Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 26 Mar 2026 16:06:52 -0700 Subject: [PATCH 2/7] Fix Gemini realtime provider for gemini-3.1-flash-live-preview - Use SendRealtimeInputAsync for all input types (text, image, audio) to avoid interleaving with SendClientContentAsync which causes WebSocket close - Fix VAD handling: use ActivityStart/ActivityEnd framing when VAD is disabled, AudioStreamEnd when VAD is enabled for push-to-talk - Fix image input: send as Video blob without activity framing, use minimal text trigger in CreateResponse since Gemini treats images as streaming context - Fix function calling: convert MEAI JsonSchema to Google Schema type with proper uppercase type names (STRING, OBJECT, etc.) - Text input auto-triggers model response without framing --- Google.GenAI/GoogleGenAIRealtimeSession.cs | 210 +++++++++++++++------ Google.GenAI/packages.lock.json | 33 ---- 2 files changed, 150 insertions(+), 93 deletions(-) diff --git a/Google.GenAI/GoogleGenAIRealtimeSession.cs b/Google.GenAI/GoogleGenAIRealtimeSession.cs index 4bafbf56..bb631096 100644 --- a/Google.GenAI/GoogleGenAIRealtimeSession.cs +++ b/Google.GenAI/GoogleGenAIRealtimeSession.cs @@ -60,13 +60,15 @@ public sealed class GoogleGenAIRealtimeSession : IRealtimeClientSession // audio frames → ActivityEnd) that must be atomic. private readonly SemaphoreSlim _sendLock = new(1, 1); - // Track whether audio was sent via SendRealtimeInputAsync to avoid mixing with SendClientContentAsync. - private bool _lastInputWasRealtime; - // Track whether a tool response was just sent. After SendToolResponseAsync, the server // automatically continues generating — sending TurnComplete would be unexpected. private bool _lastSendWasToolResponse; + // Track whether the last content sent was media (image/video/audio via CreateConversationItem) + // that does not auto-trigger a model response. Unlike text, media input requires an explicit + // ActivityEnd signal in CreateResponse to prompt the model to respond. + private bool _pendingMediaNeedsTrigger; + // Maps function call IDs to function names. Populated when ToolCall messages arrive, // consumed when sending FunctionResponse back to the server. private readonly ConcurrentDictionary _callIdToFunctionName = new(); @@ -175,15 +177,27 @@ await _asyncSession.SendToolResponseAsync( if (_lastSendWasToolResponse) { // After a tool response, Gemini automatically continues generating. - // Do not send TurnComplete — it would cause the server to close the connection. + // Do not send ActivityEnd — it would be unexpected. _lastSendWasToolResponse = false; } - else if (!_lastInputWasRealtime) + else if (_pendingMediaNeedsTrigger) { - await _asyncSession.SendClientContentAsync( - new LiveSendClientContentParameters { TurnComplete = true }, + // Media inputs (image, video) via SendRealtimeInputAsync are added to + // the model's context but don't auto-trigger a response. Gemini's Live API + // has no equivalent to OpenAI's CreateResponse command — the only way to + // trigger a response is via text input. Send a minimal whitespace text to + // prompt the model to respond about the media in context without biasing + // the response content. + _pendingMediaNeedsTrigger = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Text = " ", + }, cancellationToken).ConfigureAwait(false); } + // For text input: auto-triggers, no signal needed. + // For audio commit: ActivityEnd/AudioStreamEnd already sent in HandleAudioCommitAsync. break; default: @@ -350,11 +364,13 @@ private async Task HandleAudioCommitAsync(CancellationToken cancellationToken) _audioBufferSize = 0; } - _lastInputWasRealtime = true; + _lastSendWasToolResponse = false; + _pendingMediaNeedsTrigger = false; - // When VAD is disabled, explicit ActivityStart/ActivityEnd framing is required. - // ActivityStart marks the beginning of user speech; ActivityEnd triggers model response. - // When VAD is enabled, the server auto-detects speech boundaries — skip framing. + // When VAD is disabled, explicit ActivityStart/ActivityEnd framing is required + // to mark speech boundaries and trigger the model to respond. + // When VAD is enabled, the server auto-detects speech boundaries — + // sending explicit framing conflicts with automatic detection. if (!_vadEnabled) { await _asyncSession.SendRealtimeInputAsync( @@ -387,8 +403,10 @@ await _asyncSession.SendRealtimeInputAsync( } } - // When VAD is disabled, signal end of user activity — this triggers the model to respond. - // When VAD is enabled, the server detects end of speech automatically. + // When VAD is disabled, signal end of user activity to trigger the model's response. + // When VAD is enabled, send AudioStreamEnd to indicate the mic was turned off and the + // server should process the buffered audio. AudioStreamEnd is specifically designed for + // the push-to-talk pattern with automatic activity detection. if (!_vadEnabled) { await _asyncSession.SendRealtimeInputAsync( @@ -398,6 +416,15 @@ await _asyncSession.SendRealtimeInputAsync( }, cancellationToken).ConfigureAwait(false); } + else + { + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + AudioStreamEnd = true + }, + cancellationToken).ConfigureAwait(false); + } } private Task SendAudioFrameAsync(byte[] data, CancellationToken cancellationToken) @@ -451,68 +478,68 @@ private async Task HandleConversationItemCreateAsync( return; } - // Otherwise, treat as text/content conversation input - var parts = new List(); + // Send text and media via SendRealtimeInputAsync without activity framing. + // Text auto-triggers a model response. Images/audio are treated as streaming + // context by Gemini's Live API — they do NOT auto-trigger a response. + // When only media is sent (no accompanying text), we append a brief text prompt + // so the model knows to respond about the media content. + bool hasText = false; + bool hasMedia = false; foreach (var content in itemCreate.Item.Contents) { if (content is TextContent textContent && !string.IsNullOrEmpty(textContent.Text)) { - parts.Add(new Part { Text = textContent.Text }); + hasText = true; + _lastSendWasToolResponse = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Text = textContent.Text, + }, + cancellationToken).ConfigureAwait(false); } else if (content is DataContent dataContent) { - if (dataContent.HasTopLevelMediaType("audio")) + if (dataContent.HasTopLevelMediaType("image")) { - parts.Add(new Part - { - InlineData = new Blob + hasMedia = true; + _lastSendWasToolResponse = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters { - Data = ExtractDataBytes(dataContent), - MimeType = dataContent.MediaType ?? "audio/pcm", - } - }); + Video = new Blob + { + Data = ExtractDataBytes(dataContent), + MimeType = dataContent.MediaType ?? "image/jpeg", + } + }, + cancellationToken).ConfigureAwait(false); } - else if (dataContent.HasTopLevelMediaType("image")) + else if (dataContent.HasTopLevelMediaType("audio")) { - byte[] imageBytes = ExtractDataBytes(dataContent); - parts.Add(new Part - { - InlineData = new Blob + hasMedia = true; + _lastSendWasToolResponse = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters { - Data = imageBytes, - MimeType = dataContent.MediaType ?? "image/png", - } - }); + Audio = new Blob + { + Data = ExtractDataBytes(dataContent), + MimeType = dataContent.MediaType ?? _inputAudioMimeType, + } + }, + cancellationToken).ConfigureAwait(false); } } } - if (parts.Count == 0) + if (hasMedia && !hasText) { - return; + // Gemini treats media as streaming context (like a video frame) and won't + // respond until it receives a text/voice prompt. Send a brief text to + // trigger a response about the media content. + _pendingMediaNeedsTrigger = true; } - - string role = itemCreate.Item.Role?.Value switch - { - "assistant" => "model", - _ => "user", - }; - - _lastInputWasRealtime = false; - _lastSendWasToolResponse = false; - await _asyncSession.SendClientContentAsync( - new LiveSendClientContentParameters - { - Turns = new List - { - new Content - { - Parts = parts, - Role = role, - } - }, - }, - cancellationToken).ConfigureAwait(false); } internal static byte[] ExtractDataBytes(DataContent content) @@ -733,16 +760,79 @@ internal static FunctionDeclaration ToGoogleFunctionDeclaration(AIFunction aiFun Description = aiFunction.Description, }; - // Map the JSON schema for parameters + // Convert the MEAI JSON schema to a Google Schema object. + // Google's API expects the Schema type with uppercase type names (STRING, OBJECT, etc.), + // not raw JSON schema with lowercase types. Using Parameters instead of ParametersJsonSchema + // ensures compatibility with the Live API's function calling. if (aiFunction.JsonSchema is JsonElement schemaElement && - schemaElement.ValueKind != JsonValueKind.Undefined) + schemaElement.ValueKind == JsonValueKind.Object) { - declaration.ParametersJsonSchema = schemaElement; + declaration.Parameters = ConvertJsonSchemaToGoogleSchema(schemaElement); } return declaration; } + /// + /// Recursively converts a standard JSON Schema to a Google GenAI + /// object, mapping lowercase type names to Google's uppercase enum values. + /// + internal static Schema ConvertJsonSchemaToGoogleSchema(JsonElement element) + { + var schema = new Schema(); + + if (element.TryGetProperty("type", out var typeValue)) + { + schema.Type = typeValue.GetString()?.ToLowerInvariant() switch + { + "object" => Google.GenAI.Types.Type.Object, + "string" => Google.GenAI.Types.Type.String, + "integer" => Google.GenAI.Types.Type.Integer, + "number" => Google.GenAI.Types.Type.Number, + "boolean" => Google.GenAI.Types.Type.Boolean, + "array" => Google.GenAI.Types.Type.Array, + _ => null + }; + } + + if (element.TryGetProperty("description", out var desc) && + desc.ValueKind == JsonValueKind.String) + { + schema.Description = desc.GetString(); + } + + if (element.TryGetProperty("properties", out var props) && + props.ValueKind == JsonValueKind.Object) + { + schema.Properties = new Dictionary(); + foreach (var prop in props.EnumerateObject()) + { + schema.Properties[prop.Name] = ConvertJsonSchemaToGoogleSchema(prop.Value); + } + } + + if (element.TryGetProperty("required", out var req) && + req.ValueKind == JsonValueKind.Array) + { + schema.Required = new List(); + foreach (var item in req.EnumerateArray()) + { + if (item.ValueKind == JsonValueKind.String) + { + schema.Required.Add(item.GetString()!); + } + } + } + + if (element.TryGetProperty("items", out var items) && + items.ValueKind == JsonValueKind.Object) + { + schema.Items = ConvertJsonSchemaToGoogleSchema(items); + } + + return schema; + } + #endregion } diff --git a/Google.GenAI/packages.lock.json b/Google.GenAI/packages.lock.json index 1151b2b2..da4d7d0e 100644 --- a/Google.GenAI/packages.lock.json +++ b/Google.GenAI/packages.lock.json @@ -37,27 +37,6 @@ "Microsoft.NETCore.Platforms": "1.1.0" } }, - "System.Collections.Immutable": { - "type": "Direct", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Net.ServerSentEvents": { - "type": "Direct", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "VTWjeyx9nPb4+hkjGcAaDw1nOckypMtvABmxSWm6PPYwrXoIiVG3jwtNlAGhaGVjDkBrERABox67wYTAcHxg7Q==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "9.0.0", - "System.Memory": "4.5.5", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, "Google.Apis": { "type": "Transitive", "resolved": "1.69.0", @@ -202,18 +181,6 @@ "resolved": "2.5.2", "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" }, - "System.Collections.Immutable": { - "type": "Direct", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==" - }, - "System.Net.ServerSentEvents": { - "type": "Direct", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "VTWjeyx9nPb4+hkjGcAaDw1nOckypMtvABmxSWm6PPYwrXoIiVG3jwtNlAGhaGVjDkBrERABox67wYTAcHxg7Q==" - }, "Google.Apis": { "type": "Transitive", "resolved": "1.69.0", From b661554d541182c871717b4d4da924ff36d79209 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Fri, 27 Mar 2026 18:14:20 -0700 Subject: [PATCH 3/7] Fix Gemini realtime: SetupComplete wait, convenience constructor, function calling reliability, and test corrections - Wait for SetupComplete after ConnectAsync so tools are configured before caller sends audio/text - Add convenience constructor GoogleGenAIRealtimeClient(apiKey, defaultModelId) - Guard against null function call IDs with synthetic GUID fallback - Always store callId-to-functionName mapping regardless of null checks - Fix 5 test expectations to match actual Gemini Live API behavior: - ParametersJsonSchema -> Parameters (Google Schema) - Text auto-triggers response (no turnComplete needed) - SendRealtimeInputAsync has no role field --- Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs | 46 +++++++++++-------- Google.GenAI/GoogleGenAIRealtimeClient.cs | 26 +++++++++++ Google.GenAI/GoogleGenAIRealtimeSession.cs | 15 +++--- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs index 4edf8346..39eef0e4 100644 --- a/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs +++ b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs @@ -69,7 +69,7 @@ public void AsIRealtimeClient_NullModelId_ReturnsNonNull() public void RealtimeClient_NullClient_ThrowsArgumentNullException() { Assert.ThrowsException( - () => new GoogleGenAIRealtimeClient(null!, "model")); + () => new GoogleGenAIRealtimeClient((Client)null!, "model")); } [TestMethod] @@ -213,8 +213,8 @@ public void ToGoogleFunctionDeclaration_WithJsonSchema_MapsParameters() Assert.AreEqual("greet", declaration.Name); Assert.AreEqual("Greets a person", declaration.Description); - // The ParametersJsonSchema should be set since the function has parameters - Assert.IsNotNull(declaration.ParametersJsonSchema); + // The Parameters (Google Schema) should be set since the function has parameters + Assert.IsNotNull(declaration.Parameters); } [TestMethod] @@ -1439,21 +1439,26 @@ public async Task SendAsync_CreateResponse_AfterConversationItem_SendsTurnComple }) .Returns(Task.CompletedTask); - // Send a conversation item first (sets _lastInputWasRealtime = false) + // Send a conversation item with text var itemMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( new List { new TextContent("Hello") }, role: ChatRole.User)); await _session.SendAsync(itemMsg); + // Text input auto-triggers a response in Gemini's Live API, so the text + // send happened during CreateConversationItem, not CreateResponse. + Assert.AreEqual(1, sentMessages.Count, "Text should be sent via SendRealtimeInputAsync"); + Assert.IsTrue(sentMessages[0].Contains("Hello"), + "The sent message should contain the text content"); + sentMessages.Clear(); - // CreateResponse should send TurnComplete since last input was NOT realtime + // CreateResponse after text input does nothing — text auto-triggers. var createResp = new CreateResponseRealtimeClientMessage(); await _session.SendAsync(createResp); - Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("turnComplete"), - "Should send turnComplete when last input was conversation item (not realtime audio)"); + Assert.AreEqual(0, sentMessages.Count, + "CreateResponse should not send additional messages after text input (auto-triggers)"); } [TestMethod] @@ -1597,18 +1602,19 @@ public async Task SendAsync_CreateResponse_AfterToolResponse_NextTextSendResetsF sentMessages.Clear(); - // Now send normal text → CreateResponse. This SHOULD send TurnComplete + // Now send normal text → CreateResponse. Text auto-triggers; CreateResponse is a no-op. var textMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( new List { new TextContent("Hello again") }, role: ChatRole.User)); await _session.SendAsync(textMsg); await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - // Should have 2 messages: the text content and TurnComplete - Assert.AreEqual(2, sentMessages.Count, - "Normal text followed by CreateResponse should send content + TurnComplete"); - Assert.IsTrue(sentMessages[1].Contains("turnComplete"), - "Should send turnComplete for normal text input after tool cycle completes"); + // Should have 1 message: the text content sent via SendRealtimeInputAsync. + // CreateResponse does not send a turnComplete — text auto-triggers in Gemini. + Assert.AreEqual(1, sentMessages.Count, + "Normal text followed by CreateResponse should send only the text content"); + Assert.IsTrue(sentMessages[0].Contains("Hello again"), + "The sent message should contain the text content"); } #endregion @@ -1876,9 +1882,11 @@ public async Task SendAsync_ConversationItemCreate_UserRole_MapsToUser() await _session.SendAsync(msg); + // Gemini's SendRealtimeInputAsync sends text only (no role field). + // Verify the text content was sent. Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("user"), - "User role should map to 'user'"); + Assert.IsTrue(sentMessages[0].Contains("User text"), + "User text should be sent via SendRealtimeInputAsync"); } [TestMethod] @@ -1903,9 +1911,11 @@ public async Task SendAsync_ConversationItemCreate_NullRole_DefaultsToUser() await _session.SendAsync(msg); + // Gemini's SendRealtimeInputAsync sends text only (no role field). + // Verify the text content was sent regardless of the absence of a role. Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("user"), - "Null role should default to 'user'"); + Assert.IsTrue(sentMessages[0].Contains("No role text"), + "Text should be sent via SendRealtimeInputAsync even without a role"); } [TestMethod] diff --git a/Google.GenAI/GoogleGenAIRealtimeClient.cs b/Google.GenAI/GoogleGenAIRealtimeClient.cs index dafda70a..1ceeea95 100644 --- a/Google.GenAI/GoogleGenAIRealtimeClient.cs +++ b/Google.GenAI/GoogleGenAIRealtimeClient.cs @@ -41,6 +41,21 @@ public GoogleGenAIRealtimeClient(Client client, string? defaultModelId = null) _defaultModelId = defaultModelId; } + /// Initializes a new instance using an API key. + /// The Google GenAI API key. + /// The default model to use for realtime sessions. + /// is or empty. + public GoogleGenAIRealtimeClient(string apiKey, string? defaultModelId = null) + { + if (string.IsNullOrEmpty(apiKey)) + { + throw new ArgumentNullException(nameof(apiKey)); + } + + _client = new Client(apiKey: apiKey); + _defaultModelId = defaultModelId; + } + /// public async Task CreateSessionAsync( RealtimeSessionOptions? options = null, @@ -54,6 +69,17 @@ public async Task CreateSessionAsync( var asyncSession = await _client.Live.ConnectAsync(model, config, cancellationToken).ConfigureAwait(false); + // The Google SDK's ConnectAsync sends the setup message but does NOT wait + // for the server's SetupComplete acknowledgment. We must drain it here so + // the session is fully ready (tools configured, modalities set) before the + // caller starts sending audio or text. + var setupResponse = await asyncSession.ReceiveAsync(cancellationToken).ConfigureAwait(false); + if (setupResponse?.SetupComplete is null) + { + throw new InvalidOperationException( + "Expected SetupComplete from Gemini server after connection, but received an unexpected message."); + } + return new GoogleGenAIRealtimeSession(asyncSession, model, options); } diff --git a/Google.GenAI/GoogleGenAIRealtimeSession.cs b/Google.GenAI/GoogleGenAIRealtimeSession.cs index bb631096..96b32d22 100644 --- a/Google.GenAI/GoogleGenAIRealtimeSession.cs +++ b/Google.GenAI/GoogleGenAIRealtimeSession.cs @@ -601,20 +601,21 @@ private IEnumerable MapServerMessage(LiveServerMessage se foreach (var fc in functionCalls) { - if (fc.Id is not null && fc.Name is not null) - { - _callIdToFunctionName[fc.Id] = fc.Name; - } + // Ensure every function call has a usable ID for the round-trip mapping. + var callId = fc.Id ?? Guid.NewGuid().ToString(); + var functionName = fc.Name ?? string.Empty; + + _callIdToFunctionName[callId] = functionName; var contents = new List { new FunctionCallContent( - fc.Id ?? string.Empty, - fc.Name ?? string.Empty, + callId, + functionName, fc.Args?.ToDictionary(kvp => kvp.Key, kvp => (object?)kvp.Value)) }; - var item = new RealtimeConversationItem(contents, id: fc.Id, role: ChatRole.Assistant); + var item = new RealtimeConversationItem(contents, id: callId, role: ChatRole.Assistant); // Emit ResponseOutputItemAdded (signals start of output item) yield return new ResponseOutputItemRealtimeServerMessage(RealtimeServerMessageType.ResponseOutputItemAdded) From 7d22cc8923abe1a997a05fac28b33fb0cd2f4e0c Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Sun, 29 Mar 2026 12:35:06 -0700 Subject: [PATCH 4/7] Add transcript-only session support (SessionKind.Transcription) - Branch on SessionKind.Transcription in BuildLiveConnectConfig to create minimal config: input transcription only, text modality, no voice/tools/instructions - Map TranscriptionOptions.SpeechLanguage to AudioTranscriptionConfig.LanguageCodes for both transcription and conversation modes - Add 8 tests covering transcription mode config, language mapping, VAD, and verifying conversation-oriented options are excluded --- Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs | 134 ++++++++++++++++++ Google.GenAI/GoogleGenAIRealtimeClient.cs | 56 +++++++- local-packages/Google.GenAI.1.0.0-local.nupkg | Bin 0 -> 996754 bytes 3 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 local-packages/Google.GenAI.1.0.0-local.nupkg diff --git a/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs index 39eef0e4..06116d9a 100644 --- a/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs +++ b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs @@ -1368,6 +1368,140 @@ public void BuildLiveConnectConfig_AllOptionsCombined_MapsEverything() Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); } + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_OnlyInputTranscription() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_TextModalityOnly() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Text, config.ResponseModalities[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_NoVoiceOrToolsOrInstructions() + { + var fn = AIFunctionFactory.Create( + (string q) => "result", + "search", + "Searches things"); + + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + Instructions = "Be concise.", + Voice = "Aoede", + MaxOutputTokens = 500, + Tools = new List { fn }, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + // Transcription-only: conversation-oriented options are ignored + Assert.IsNull(config.SystemInstruction); + Assert.IsNull(config.SpeechConfig); + Assert.IsNull(config.GenerationConfig); + Assert.IsNull(config.Tools); + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_LanguageCodeMapped() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + TranscriptionOptions = new TranscriptionOptions { SpeechLanguage = "en-US" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.AreEqual(1, config.InputAudioTranscription.LanguageCodes.Count); + Assert.AreEqual("en-US", config.InputAudioTranscription.LanguageCodes[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_NoLanguage_NoLanguageCodes() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNull(config.InputAudioTranscription.LanguageCodes); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_VadEnabled() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + VoiceActivityDetection = new VoiceActivityDetectionOptions { Enabled = true, AllowInterruption = false }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + Assert.AreEqual(ActivityHandling.NoInterruption, config.RealtimeInputConfig.ActivityHandling); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_DefaultVadDisabled() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_ConversationMode_LanguageCodeMapped() + { + var options = new RealtimeSessionOptions + { + TranscriptionOptions = new TranscriptionOptions { SpeechLanguage = "ja-JP" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.AreEqual(1, config.InputAudioTranscription.LanguageCodes.Count); + Assert.AreEqual("ja-JP", config.InputAudioTranscription.LanguageCodes[0]); + // Output transcription is also enabled in conversation mode (no language codes) + Assert.IsNotNull(config.OutputAudioTranscription); + } + #endregion #region ExtractDataBytes Tests diff --git a/Google.GenAI/GoogleGenAIRealtimeClient.cs b/Google.GenAI/GoogleGenAIRealtimeClient.cs index 1ceeea95..dad6263b 100644 --- a/Google.GenAI/GoogleGenAIRealtimeClient.cs +++ b/Google.GenAI/GoogleGenAIRealtimeClient.cs @@ -135,6 +135,13 @@ internal static LiveConnectConfig BuildLiveConnectConfig(RealtimeSessionOptions? return config; } + // Transcription-only sessions use a minimal configuration with + // only input audio transcription enabled (no audio output, no tools). + if (options.SessionKind == RealtimeSessionKind.Transcription) + { + return BuildTranscriptionConnectConfig(options); + } + // System instructions if (!string.IsNullOrEmpty(options.Instructions)) { @@ -207,10 +214,16 @@ internal static LiveConnectConfig BuildLiveConnectConfig(RealtimeSessionOptions? } } - // Transcription + // Transcription (both directions for conversation sessions) if (options.TranscriptionOptions is not null) { - config.InputAudioTranscription = new AudioTranscriptionConfig(); + var inputTranscriptionConfig = new AudioTranscriptionConfig(); + if (!string.IsNullOrEmpty(options.TranscriptionOptions.SpeechLanguage)) + { + inputTranscriptionConfig.LanguageCodes = new List { options.TranscriptionOptions.SpeechLanguage }; + } + + config.InputAudioTranscription = inputTranscriptionConfig; config.OutputAudioTranscription = new AudioTranscriptionConfig(); } @@ -241,5 +254,44 @@ internal static LiveConnectConfig BuildLiveConnectConfig(RealtimeSessionOptions? return config; } + + /// + /// Builds a minimal for transcription-only sessions. + /// Only input audio transcription is enabled; no audio output, tools, or voice config. + /// + private static LiveConnectConfig BuildTranscriptionConnectConfig(RealtimeSessionOptions options) + { + var config = new LiveConnectConfig + { + // No audio output for transcription-only sessions + ResponseModalities = new List { Modality.Text }, + }; + + // Enable input transcription with optional language hint + var transcriptionConfig = new AudioTranscriptionConfig(); + if (!string.IsNullOrEmpty(options.TranscriptionOptions?.SpeechLanguage)) + { + transcriptionConfig.LanguageCodes = new List { options.TranscriptionOptions.SpeechLanguage }; + } + + config.InputAudioTranscription = transcriptionConfig; + + // VAD configuration still applies for speech boundary detection + config.RealtimeInputConfig = new RealtimeInputConfig(); + + if (options.VoiceActivityDetection is { Enabled: true } vad) + { + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = false }; + config.RealtimeInputConfig.ActivityHandling = vad.AllowInterruption + ? ActivityHandling.StartOfActivityInterrupts + : ActivityHandling.NoInterruption; + } + else + { + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; + } + + return config; + } } diff --git a/local-packages/Google.GenAI.1.0.0-local.nupkg b/local-packages/Google.GenAI.1.0.0-local.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..453e9418f4fc8f887519bf5a912e4058133ed109 GIT binary patch literal 996754 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 (EIaFA`EEKuq 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%0DFY4d6R#^d;!>0DKpsgi*Qx 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�()!c+sXmoUm{t7p=7516aAbv44A7{Ue!spEAhm7 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
KWSoY@=F^o1E)=aEVEMKAvC~H^uJ});N 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
^ZU=Z}kE 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 literal 0 HcmV?d00001 From 43795f059d9eb8b1c929ba1cc727f649d15df65d Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 2 Apr 2026 16:18:50 -0700 Subject: [PATCH 5/7] feat: Add source-generated JSON serialization for AOT compatibility Add GenAIJsonContext (JsonSerializerContext) with source-generated metadata for all types used in serialization, enabling AOT (Ahead-of-Time) compilation support without duplicating the SDK code in provider implementations. Changes: - Add GenAIJsonContext.cs with [JsonSerializable] entries for 93 root types (nested types are auto-discovered by the source generator) - Wire source-gen context into JsonConfig.JsonSerializerOptions with DefaultJsonTypeInfoResolver fallback for non-AOT scenarios - Add compact InternalSerializerOptions for intermediate serialize-then-parse round-trips (avoids WriteIndented overhead on internal transforms) - Update all bare JsonSerializer.Serialize/Deserialize calls (~90 sites) to use the configured options with source-gen type metadata - Add System.Text.Json PackageReference for source generator support on both netstandard2.0 and net8.0 targets - Add AotJsonContextTest.cs with 4 tests verifying context coverage, nested type auto-discovery, and serialization round-trips --- DemoApp/Files/packages.lock.json | 189 +- .../packages.lock.json | 189 +- .../packages.lock.json | 189 +- DemoApp/JsonParser/packages.lock.json | 189 +- .../packages.lock.json | 189 +- .../packages.lock.json | 189 +- DemoApp/LiveToolCall/packages.lock.json | 189 +- Google.GenAI.E2E.Tests/packages.lock.json | 623 +- Google.GenAI.Tests/AotJsonContextTest.cs | 266 + .../Netstandard2_0Tests/packages.lock.json | 595 +- Google.GenAI.Tests/packages.lock.json | 583 +- Google.GenAI/Batches.cs | 4290 +++--- Google.GenAI/Caches.cs | 2866 ++-- Google.GenAI/Common.cs | 1046 +- Google.GenAI/Files.cs | 1830 +-- Google.GenAI/GenAIJsonContext.cs | 109 + Google.GenAI/Google.GenAI.csproj | 83 +- Google.GenAI/JsonConfig.cs | 100 +- Google.GenAI/Live.cs | 872 +- Google.GenAI/Models.cs | 11964 ++++++++-------- Google.GenAI/Operations.cs | 852 +- Google.GenAI/TokensConverters.cs | 1626 +-- Google.GenAI/Transformers.cs | 1464 +- Google.GenAI/Tunings.cs | 3680 ++--- Google.GenAI/packages.lock.json | 478 +- 25 files changed, 17527 insertions(+), 17123 deletions(-) create mode 100644 Google.GenAI.Tests/AotJsonContextTest.cs create mode 100644 Google.GenAI/GenAIJsonContext.cs diff --git a/DemoApp/Files/packages.lock.json b/DemoApp/Files/packages.lock.json index e0388869..39dadc37 100644 --- a/DemoApp/Files/packages.lock.json +++ b/DemoApp/Files/packages.lock.json @@ -1,95 +1,96 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/DemoApp/GenerateContentSimpleText/packages.lock.json b/DemoApp/GenerateContentSimpleText/packages.lock.json index e0388869..39dadc37 100644 --- a/DemoApp/GenerateContentSimpleText/packages.lock.json +++ b/DemoApp/GenerateContentSimpleText/packages.lock.json @@ -1,95 +1,96 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/DemoApp/GenerateContentStreamSimpleText/packages.lock.json b/DemoApp/GenerateContentStreamSimpleText/packages.lock.json index e0388869..39dadc37 100644 --- a/DemoApp/GenerateContentStreamSimpleText/packages.lock.json +++ b/DemoApp/GenerateContentStreamSimpleText/packages.lock.json @@ -1,95 +1,96 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/DemoApp/JsonParser/packages.lock.json b/DemoApp/JsonParser/packages.lock.json index e0388869..39dadc37 100644 --- a/DemoApp/JsonParser/packages.lock.json +++ b/DemoApp/JsonParser/packages.lock.json @@ -1,95 +1,96 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json b/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json index e0388869..39dadc37 100644 --- a/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json +++ b/DemoApp/LiveAudioToAudioRealtimeInput/packages.lock.json @@ -1,95 +1,96 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/DemoApp/LiveTextToTextClientContent/packages.lock.json b/DemoApp/LiveTextToTextClientContent/packages.lock.json index e0388869..39dadc37 100644 --- a/DemoApp/LiveTextToTextClientContent/packages.lock.json +++ b/DemoApp/LiveTextToTextClientContent/packages.lock.json @@ -1,95 +1,96 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/DemoApp/LiveToolCall/packages.lock.json b/DemoApp/LiveToolCall/packages.lock.json index e0388869..39dadc37 100644 --- a/DemoApp/LiveToolCall/packages.lock.json +++ b/DemoApp/LiveToolCall/packages.lock.json @@ -1,95 +1,96 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/Google.GenAI.E2E.Tests/packages.lock.json b/Google.GenAI.E2E.Tests/packages.lock.json index b17583b7..66676549 100644 --- a/Google.GenAI.E2E.Tests/packages.lock.json +++ b/Google.GenAI.E2E.Tests/packages.lock.json @@ -1,312 +1,313 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "coverlet.collector": { - "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.10.0, )", - "resolved": "17.10.0", - "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.10.0", - "Microsoft.TestPlatform.TestHost": "17.10.0" - } - }, - "Microsoft.Testing.Extensions.CodeCoverage": { - "type": "Direct", - "requested": "[17.10.1, )", - "resolved": "17.10.1", - "contentHash": "EdPqUfB4GShYeXiivrjWrTAjZz93tmrF313QlyK/CI4afdBAcNCrJ2IqEWAQ1n+05sc+tEwZnyaZAdStnwQqcw==", - "dependencies": { - "Microsoft.DiaSymReader": "2.0.0", - "Microsoft.Extensions.DependencyModel": "6.0.0", - "Microsoft.Testing.Platform": "1.0.0", - "Mono.Cecil": "0.11.5", - "System.Reflection.Metadata": "6.0.1" - } - }, - "Moq": { - "type": "Direct", - "requested": "[4.20.70, )", - "resolved": "4.20.70", - "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", - "dependencies": { - "Castle.Core": "5.1.1" - } - }, - "MSTest.TestAdapter": { - "type": "Direct", - "requested": "[3.4.3, )", - "resolved": "3.4.3", - "contentHash": "5ul31wYr17590gDumPxWMiBLPREfPF/ggtdPGfaKoYSsO0EW6H1GWY+7xnVCKa2SB4I/dSEZLDYSwRLDjA0LEQ==", - "dependencies": { - "Microsoft.Testing.Extensions.VSTestBridge": "1.2.1", - "Microsoft.Testing.Platform.MSBuild": "1.2.1" - } - }, - "MSTest.TestFramework": { - "type": "Direct", - "requested": "[3.4.3, )", - "resolved": "3.4.3", - "contentHash": "hu7F0PyRe47LScY2SCjRFIzP2QYxq1oeHMAIdao9onUm5WhobO9tfZrFAAkJ4v+66EQjilloEbA4kspVHCZpTg==" - }, - "System.Text.Json": { - "type": "Direct", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - }, - "TestServerSdk": { - "type": "Direct", - "requested": "[0.1.5, )", - "resolved": "0.1.5", - "contentHash": "cQZJyJvSn12KSe5yzWDuq+YK1rKLT+ndNdUgmiCN+SmQtQIMy0uGbqhen6i7mKvgdF9K+J62QhvM6bT9TSqlyg==", - "dependencies": { - "SharpCompress": "0.29.0", - "YamlDotNet": "12.0.2" - } - }, - "Castle.Core": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", - "dependencies": { - "System.Diagnostics.EventLog": "6.0.0" - } - }, - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.22.0", - "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" - }, - "Microsoft.DiaSymReader": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "QcZrCETsBJqy/vQpFtJc+jSXQ0K5sucQ6NUFbTNVHD4vfZZOwjZ/3sBzczkC4DityhD3AVO/+K/+9ioLs1AgRA==" - }, - "Microsoft.Extensions.DependencyModel": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" - } - }, - "Microsoft.Testing.Extensions.Telemetry": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "MKGxwQhDDEoTS/ntFb21Z6Bxh9VvknmSLgEWH+NFD86fbcIqE2Al8lrXkQPeH+AqCvlhx2WnPLKd81T2PXc2dw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Extensions.TrxReport.Abstractions": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "46SnzaLR+SDaTtBWy49xdFm/rI40I8nZtziqnt2d4lgILKovWPnkM8Pehnga/uwl+OznVIh0XuRsN3NokkX1TQ==", - "dependencies": { - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Extensions.VSTestBridge": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "Tu8CWHEwV/92WM2DRr/qeIdH243diV5s43ODPLl13XeRqGbZlu9lk7X0a7kcxhp0BLRlA3fqMW3F6RynrnDrPw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.TestPlatform.ObjectModel": "17.5.0", - "Microsoft.Testing.Extensions.Telemetry": "1.2.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.2.1", - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Platform": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "mb7irPwqjgusJ05BxuQ5KP6uofWaoDr/dfjFNItX1Q1Ntv3EDMr3CeLInrlU2PNcPwwObw4X6bZG7wJvvFjKZQ==" - }, - "Microsoft.Testing.Platform.MSBuild": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "leUhW4iQNy7vmPk5uRHd4OROqfRtugWDQkWL/4AD17gxZwAAwGCaTcrqG0YVPi7uuZ+lj2Loa6kU7hBLA/v5+w==", - "dependencies": { - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.10.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Mono.Cecil": { - "type": "Transitive", - "resolved": "0.11.5", - "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "SharpCompress": { - "type": "Transitive", - "resolved": "0.29.0", - "contentHash": "k7iUB0mZWS6eEj6L0PuuxASzjpQvlYKfHJ0JtnHBJFbU3VeQJ4SNQC4eloiYsPbmFrJCJYiqFhV4AZuBXpIXiQ==" - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", - "dependencies": { - "System.Collections.Immutable": "6.0.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "YamlDotNet": { - "type": "Transitive", - "resolved": "12.0.2", - "contentHash": "IFj2oDZYJIv5sM8mqaj5edVrpgUyoXk/wCGqZQJrgys/0tBajajpjRSgFM+iA/9ILOfTsPYKtcDcwvqBnBcNIg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.10.0, )", + "resolved": "17.10.0", + "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "dependencies": { + "Microsoft.CodeCoverage": "17.10.0", + "Microsoft.TestPlatform.TestHost": "17.10.0" + } + }, + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[17.10.1, )", + "resolved": "17.10.1", + "contentHash": "EdPqUfB4GShYeXiivrjWrTAjZz93tmrF313QlyK/CI4afdBAcNCrJ2IqEWAQ1n+05sc+tEwZnyaZAdStnwQqcw==", + "dependencies": { + "Microsoft.DiaSymReader": "2.0.0", + "Microsoft.Extensions.DependencyModel": "6.0.0", + "Microsoft.Testing.Platform": "1.0.0", + "Mono.Cecil": "0.11.5", + "System.Reflection.Metadata": "6.0.1" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "MSTest.TestAdapter": { + "type": "Direct", + "requested": "[3.4.3, )", + "resolved": "3.4.3", + "contentHash": "5ul31wYr17590gDumPxWMiBLPREfPF/ggtdPGfaKoYSsO0EW6H1GWY+7xnVCKa2SB4I/dSEZLDYSwRLDjA0LEQ==", + "dependencies": { + "Microsoft.Testing.Extensions.VSTestBridge": "1.2.1", + "Microsoft.Testing.Platform.MSBuild": "1.2.1" + } + }, + "MSTest.TestFramework": { + "type": "Direct", + "requested": "[3.4.3, )", + "resolved": "3.4.3", + "contentHash": "hu7F0PyRe47LScY2SCjRFIzP2QYxq1oeHMAIdao9onUm5WhobO9tfZrFAAkJ4v+66EQjilloEbA4kspVHCZpTg==" + }, + "System.Text.Json": { + "type": "Direct", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + }, + "TestServerSdk": { + "type": "Direct", + "requested": "[0.1.5, )", + "resolved": "0.1.5", + "contentHash": "cQZJyJvSn12KSe5yzWDuq+YK1rKLT+ndNdUgmiCN+SmQtQIMy0uGbqhen6i7mKvgdF9K+J62QhvM6bT9TSqlyg==", + "dependencies": { + "SharpCompress": "0.29.0", + "YamlDotNet": "12.0.2" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.22.0", + "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "QcZrCETsBJqy/vQpFtJc+jSXQ0K5sucQ6NUFbTNVHD4vfZZOwjZ/3sBzczkC4DityhD3AVO/+K/+9ioLs1AgRA==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Testing.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "MKGxwQhDDEoTS/ntFb21Z6Bxh9VvknmSLgEWH+NFD86fbcIqE2Al8lrXkQPeH+AqCvlhx2WnPLKd81T2PXc2dw==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "46SnzaLR+SDaTtBWy49xdFm/rI40I8nZtziqnt2d4lgILKovWPnkM8Pehnga/uwl+OznVIh0XuRsN3NokkX1TQ==", + "dependencies": { + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Extensions.VSTestBridge": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "Tu8CWHEwV/92WM2DRr/qeIdH243diV5s43ODPLl13XeRqGbZlu9lk7X0a7kcxhp0BLRlA3fqMW3F6RynrnDrPw==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.TestPlatform.ObjectModel": "17.5.0", + "Microsoft.Testing.Extensions.Telemetry": "1.2.1", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.2.1", + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "mb7irPwqjgusJ05BxuQ5KP6uofWaoDr/dfjFNItX1Q1Ntv3EDMr3CeLInrlU2PNcPwwObw4X6bZG7wJvvFjKZQ==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "leUhW4iQNy7vmPk5uRHd4OROqfRtugWDQkWL/4AD17gxZwAAwGCaTcrqG0YVPi7uuZ+lj2Loa6kU7hBLA/v5+w==", + "dependencies": { + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.11.5", + "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.29.0", + "contentHash": "k7iUB0mZWS6eEj6L0PuuxASzjpQvlYKfHJ0JtnHBJFbU3VeQJ4SNQC4eloiYsPbmFrJCJYiqFhV4AZuBXpIXiQ==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "YamlDotNet": { + "type": "Transitive", + "resolved": "12.0.2", + "contentHash": "IFj2oDZYJIv5sM8mqaj5edVrpgUyoXk/wCGqZQJrgys/0tBajajpjRSgFM+iA/9ILOfTsPYKtcDcwvqBnBcNIg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + } + } + } } \ No newline at end of file diff --git a/Google.GenAI.Tests/AotJsonContextTest.cs b/Google.GenAI.Tests/AotJsonContextTest.cs new file mode 100644 index 00000000..9cd93760 --- /dev/null +++ b/Google.GenAI.Tests/AotJsonContextTest.cs @@ -0,0 +1,266 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using Google.GenAI; +using Google.GenAI.Types; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Type = System.Type; + +namespace Google.GenAI.Tests +{ + ///

+ /// Tests that the source-generated JSON context covers all types used in serialization. + /// This ensures AOT compatibility by verifying type metadata is available without reflection. + /// + [TestClass] + public class AotJsonContextTest + { + /// + /// Verifies that all types directly used in JsonSerializer.Serialize/Deserialize calls + /// are resolvable via the GenAIJsonContext source-generated context (without reflection fallback). + /// + [TestMethod] + public void GenAIJsonContext_CoversAllDirectlySerializedTypes() + { + var requiredTypes = new[] + { + // Service parameter/response types + typeof(BatchJob), + typeof(CreateBatchJobParameters), + typeof(CreateEmbeddingsBatchJobParameters), + typeof(GetBatchJobParameters), + typeof(CancelBatchJobParameters), + typeof(ListBatchJobsResponse), + typeof(DeleteBatchJobParameters), + typeof(DeleteResourceJob), + typeof(CachedContent), + typeof(CreateCachedContentParameters), + typeof(GetCachedContentParameters), + typeof(DeleteCachedContentParameters), + typeof(DeleteCachedContentResponse), + typeof(UpdateCachedContentParameters), + typeof(ListCachedContentsParameters), + typeof(ListCachedContentsResponse), + typeof(Google.GenAI.Types.File), + typeof(ListFilesParameters), + typeof(ListFilesResponse), + typeof(CreateFileParameters), + typeof(CreateFileResponse), + typeof(GetFileParameters), + typeof(DeleteFileParameters), + typeof(DeleteFileResponse), + typeof(InternalRegisterFilesParameters), + typeof(RegisterFilesResponse), + typeof(GenerateContentParameters), + typeof(GenerateContentResponse), + typeof(EmbedContentParametersPrivate), + typeof(EmbedContentResponse), + typeof(GenerateImagesParameters), + typeof(GenerateImagesResponse), + typeof(EditImageParameters), + typeof(EditImageResponse), + typeof(UpscaleImageAPIParameters), + typeof(UpscaleImageResponse), + typeof(RecontextImageParameters), + typeof(RecontextImageResponse), + typeof(SegmentImageParameters), + typeof(SegmentImageResponse), + typeof(Model), + typeof(GetModelParameters), + typeof(ListModelsParameters), + typeof(ListModelsResponse), + typeof(UpdateModelParameters), + typeof(DeleteModelParameters), + typeof(DeleteModelResponse), + typeof(CountTokensParameters), + typeof(CountTokensResponse), + typeof(ComputeTokensParameters), + typeof(ComputeTokensResponse), + typeof(GenerateVideosParameters), + typeof(GenerateVideosOperation), + typeof(GenerateVideosResponse), + typeof(GetOperationParameters), + typeof(FetchPredictOperationParameters), + typeof(TuningJob), + typeof(GetTuningJobParameters), + typeof(ListTuningJobsParameters), + typeof(ListTuningJobsResponse), + typeof(CancelTuningJobParameters), + typeof(CancelTuningJobResponse), + typeof(CreateTuningJobParametersPrivate), + typeof(TuningOperation), + // Live/Realtime types + typeof(LiveConnectParameters), + typeof(LiveServerMessage), + typeof(LiveClientMessage), + typeof(LiveClientRealtimeInput), + typeof(LiveClientSetup), + typeof(LiveClientContent), + typeof(LiveClientToolResponse), + typeof(LiveConnectConfig), + typeof(LiveSendClientContentParameters), + typeof(LiveSendRealtimeInputParameters), + typeof(LiveSendToolResponseParameters), + typeof(LiveServerContent), + typeof(LiveServerGoAway), + typeof(LiveServerSetupComplete), + typeof(LiveServerToolCall), + typeof(LiveServerToolCallCancellation), + typeof(LiveServerSessionResumptionUpdate), + typeof(RealtimeInputConfig), + // Transformer types + typeof(Content), + typeof(Schema), + typeof(SpeechConfig), + typeof(Tool), + typeof(Blob), + // Collection types + typeof(List), + typeof(List), + }; + + var missingTypes = new List(); + + foreach (var type in requiredTypes) + { + var typeInfo = GenAIJsonContext.Default.GetTypeInfo(type); + if (typeInfo == null) + { + missingTypes.Add(type); + } + } + + Assert.AreEqual(0, missingTypes.Count, + $"The following types are missing from GenAIJsonContext and will fail in AOT: " + + $"{string.Join(", ", missingTypes.Select(t => t.Name))}"); + } + + /// + /// Verifies that key nested types (transitively reachable from root types) + /// are auto-discovered by the source generator. + /// + [TestMethod] + public void GenAIJsonContext_AutoDiscoversNestedTypes() + { + // These types are NOT explicitly registered but should be auto-discovered + // as properties of registered root types + var nestedTypes = new[] + { + typeof(Part), + typeof(Candidate), + typeof(SafetyRating), + typeof(FunctionDeclaration), + typeof(GenerationConfig), + typeof(ToolConfig), + typeof(FunctionCall), + typeof(FunctionResponse), + typeof(CitationMetadata), + typeof(GenerateContentResponseUsageMetadata), + }; + + var missingTypes = new List(); + + foreach (var type in nestedTypes) + { + var typeInfo = GenAIJsonContext.Default.GetTypeInfo(type); + if (typeInfo == null) + { + missingTypes.Add(type); + } + } + + Assert.AreEqual(0, missingTypes.Count, + $"The following nested types are NOT auto-discovered by the source generator. " + + $"Add [JsonSerializable(typeof(X))] to GenAIJsonContext: " + + $"{string.Join(", ", missingTypes.Select(t => t.Name))}"); + } + + /// + /// Verifies that serializing and deserializing a realtime message round-trips correctly + /// using only the source-generated context (no reflection fallback). + /// + [TestMethod] + public void GenAIJsonContext_RoundTripsLiveServerMessage() + { + var message = new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Role = "model", + Parts = new List + { + new Part { Text = "Hello from the model" } + } + } + } + }; + + // Serialize using source-gen context only (no reflection) + var json = JsonSerializer.Serialize(message, GenAIJsonContext.Default.LiveServerMessage); + Assert.IsFalse(string.IsNullOrEmpty(json)); + + // Deserialize using source-gen context + var deserialized = JsonSerializer.Deserialize(json, GenAIJsonContext.Default.LiveServerMessage); + Assert.IsNotNull(deserialized); + Assert.IsNotNull(deserialized.ServerContent); + Assert.AreEqual("model", deserialized.ServerContent.ModelTurn?.Role); + Assert.AreEqual("Hello from the model", deserialized.ServerContent.ModelTurn?.Parts?[0]?.Text); + } + + /// + /// Verifies that serializing a GenerateContentParameters type round-trips correctly, + /// exercising a deep type graph (Content, Part, Tool, FunctionDeclaration, Schema, etc). + /// + [TestMethod] + public void GenAIJsonContext_RoundTripsGenerateContentParameters() + { + var parameters = new GenerateContentParameters + { + Model = "gemini-2.0-flash", + Contents = new List + { + new Content + { + Role = "user", + Parts = new List { new Part { Text = "Hello" } } + } + }, + Config = new GenerateContentConfig + { + MaxOutputTokens = 100, + Temperature = 0.7f, + } + }; + + var json = JsonSerializer.Serialize(parameters, GenAIJsonContext.Default.GenerateContentParameters); + Assert.IsFalse(string.IsNullOrEmpty(json)); + Assert.IsTrue(json.Contains("gemini-2.0-flash")); + + var deserialized = JsonSerializer.Deserialize(json, GenAIJsonContext.Default.GenerateContentParameters); + Assert.IsNotNull(deserialized); + Assert.AreEqual("gemini-2.0-flash", deserialized.Model); + Assert.AreEqual(1, deserialized.Contents?.Count); + } + } +} diff --git a/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json b/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json index 7d38165e..5e2a83f9 100644 --- a/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json +++ b/Google.GenAI.Tests/Netstandard2_0Tests/packages.lock.json @@ -1,298 +1,299 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "coverlet.collector": { - "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Direct", - "requested": "[10.0.3, )", - "resolved": "10.0.3", - "contentHash": "TV62UsrJZPX6gbt3c4WrtXh7bmaDIcMqf9uft1cc4L6gJXOU07hDGEh+bFQh/L2Az0R1WVOkiT66lFqS6G2NmA==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.10.0, )", - "resolved": "17.10.0", - "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.10.0", - "Microsoft.TestPlatform.TestHost": "17.10.0" - } - }, - "Microsoft.Testing.Extensions.CodeCoverage": { - "type": "Direct", - "requested": "[17.10.1, )", - "resolved": "17.10.1", - "contentHash": "EdPqUfB4GShYeXiivrjWrTAjZz93tmrF313QlyK/CI4afdBAcNCrJ2IqEWAQ1n+05sc+tEwZnyaZAdStnwQqcw==", - "dependencies": { - "Microsoft.DiaSymReader": "2.0.0", - "Microsoft.Extensions.DependencyModel": "6.0.0", - "Microsoft.Testing.Platform": "1.0.0", - "Mono.Cecil": "0.11.5", - "System.Reflection.Metadata": "6.0.1" - } - }, - "Moq": { - "type": "Direct", - "requested": "[4.20.70, )", - "resolved": "4.20.70", - "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", - "dependencies": { - "Castle.Core": "5.1.1" - } - }, - "MSTest.TestAdapter": { - "type": "Direct", - "requested": "[3.4.3, )", - "resolved": "3.4.3", - "contentHash": "5ul31wYr17590gDumPxWMiBLPREfPF/ggtdPGfaKoYSsO0EW6H1GWY+7xnVCKa2SB4I/dSEZLDYSwRLDjA0LEQ==", - "dependencies": { - "Microsoft.Testing.Extensions.VSTestBridge": "1.2.1", - "Microsoft.Testing.Platform.MSBuild": "1.2.1" - } - }, - "MSTest.TestFramework": { - "type": "Direct", - "requested": "[3.4.3, )", - "resolved": "3.4.3", - "contentHash": "hu7F0PyRe47LScY2SCjRFIzP2QYxq1oeHMAIdao9onUm5WhobO9tfZrFAAkJ4v+66EQjilloEbA4kspVHCZpTg==" - }, - "System.Text.Json": { - "type": "Direct", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - }, - "Castle.Core": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", - "dependencies": { - "System.Diagnostics.EventLog": "6.0.0" - } - }, - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.22.0", - "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" - }, - "Microsoft.DiaSymReader": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "QcZrCETsBJqy/vQpFtJc+jSXQ0K5sucQ6NUFbTNVHD4vfZZOwjZ/3sBzczkC4DityhD3AVO/+K/+9ioLs1AgRA==" - }, - "Microsoft.Extensions.DependencyModel": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" - } - }, - "Microsoft.Testing.Extensions.Telemetry": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "MKGxwQhDDEoTS/ntFb21Z6Bxh9VvknmSLgEWH+NFD86fbcIqE2Al8lrXkQPeH+AqCvlhx2WnPLKd81T2PXc2dw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Extensions.TrxReport.Abstractions": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "46SnzaLR+SDaTtBWy49xdFm/rI40I8nZtziqnt2d4lgILKovWPnkM8Pehnga/uwl+OznVIh0XuRsN3NokkX1TQ==", - "dependencies": { - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Extensions.VSTestBridge": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "Tu8CWHEwV/92WM2DRr/qeIdH243diV5s43ODPLl13XeRqGbZlu9lk7X0a7kcxhp0BLRlA3fqMW3F6RynrnDrPw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.TestPlatform.ObjectModel": "17.5.0", - "Microsoft.Testing.Extensions.Telemetry": "1.2.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.2.1", - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Platform": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "mb7irPwqjgusJ05BxuQ5KP6uofWaoDr/dfjFNItX1Q1Ntv3EDMr3CeLInrlU2PNcPwwObw4X6bZG7wJvvFjKZQ==" - }, - "Microsoft.Testing.Platform.MSBuild": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "leUhW4iQNy7vmPk5uRHd4OROqfRtugWDQkWL/4AD17gxZwAAwGCaTcrqG0YVPi7uuZ+lj2Loa6kU7hBLA/v5+w==", - "dependencies": { - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.10.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Mono.Cecil": { - "type": "Transitive", - "resolved": "0.11.5", - "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", - "dependencies": { - "System.Collections.Immutable": "6.0.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Direct", + "requested": "[10.0.3, )", + "resolved": "10.0.3", + "contentHash": "TV62UsrJZPX6gbt3c4WrtXh7bmaDIcMqf9uft1cc4L6gJXOU07hDGEh+bFQh/L2Az0R1WVOkiT66lFqS6G2NmA==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.10.0, )", + "resolved": "17.10.0", + "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "dependencies": { + "Microsoft.CodeCoverage": "17.10.0", + "Microsoft.TestPlatform.TestHost": "17.10.0" + } + }, + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[17.10.1, )", + "resolved": "17.10.1", + "contentHash": "EdPqUfB4GShYeXiivrjWrTAjZz93tmrF313QlyK/CI4afdBAcNCrJ2IqEWAQ1n+05sc+tEwZnyaZAdStnwQqcw==", + "dependencies": { + "Microsoft.DiaSymReader": "2.0.0", + "Microsoft.Extensions.DependencyModel": "6.0.0", + "Microsoft.Testing.Platform": "1.0.0", + "Mono.Cecil": "0.11.5", + "System.Reflection.Metadata": "6.0.1" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "MSTest.TestAdapter": { + "type": "Direct", + "requested": "[3.4.3, )", + "resolved": "3.4.3", + "contentHash": "5ul31wYr17590gDumPxWMiBLPREfPF/ggtdPGfaKoYSsO0EW6H1GWY+7xnVCKa2SB4I/dSEZLDYSwRLDjA0LEQ==", + "dependencies": { + "Microsoft.Testing.Extensions.VSTestBridge": "1.2.1", + "Microsoft.Testing.Platform.MSBuild": "1.2.1" + } + }, + "MSTest.TestFramework": { + "type": "Direct", + "requested": "[3.4.3, )", + "resolved": "3.4.3", + "contentHash": "hu7F0PyRe47LScY2SCjRFIzP2QYxq1oeHMAIdao9onUm5WhobO9tfZrFAAkJ4v+66EQjilloEbA4kspVHCZpTg==" + }, + "System.Text.Json": { + "type": "Direct", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.22.0", + "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "QcZrCETsBJqy/vQpFtJc+jSXQ0K5sucQ6NUFbTNVHD4vfZZOwjZ/3sBzczkC4DityhD3AVO/+K/+9ioLs1AgRA==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Testing.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "MKGxwQhDDEoTS/ntFb21Z6Bxh9VvknmSLgEWH+NFD86fbcIqE2Al8lrXkQPeH+AqCvlhx2WnPLKd81T2PXc2dw==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "46SnzaLR+SDaTtBWy49xdFm/rI40I8nZtziqnt2d4lgILKovWPnkM8Pehnga/uwl+OznVIh0XuRsN3NokkX1TQ==", + "dependencies": { + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Extensions.VSTestBridge": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "Tu8CWHEwV/92WM2DRr/qeIdH243diV5s43ODPLl13XeRqGbZlu9lk7X0a7kcxhp0BLRlA3fqMW3F6RynrnDrPw==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.TestPlatform.ObjectModel": "17.5.0", + "Microsoft.Testing.Extensions.Telemetry": "1.2.1", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.2.1", + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "mb7irPwqjgusJ05BxuQ5KP6uofWaoDr/dfjFNItX1Q1Ntv3EDMr3CeLInrlU2PNcPwwObw4X6bZG7wJvvFjKZQ==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "leUhW4iQNy7vmPk5uRHd4OROqfRtugWDQkWL/4AD17gxZwAAwGCaTcrqG0YVPi7uuZ+lj2Loa6kU7hBLA/v5+w==", + "dependencies": { + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.11.5", + "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + } + } + } } \ No newline at end of file diff --git a/Google.GenAI.Tests/packages.lock.json b/Google.GenAI.Tests/packages.lock.json index adf25881..a81a9052 100644 --- a/Google.GenAI.Tests/packages.lock.json +++ b/Google.GenAI.Tests/packages.lock.json @@ -1,292 +1,293 @@ -{ - "version": 2, - "dependencies": { - "net8.0": { - "coverlet.collector": { - "type": "Direct", - "requested": "[6.0.4, )", - "resolved": "6.0.4", - "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.10.0, )", - "resolved": "17.10.0", - "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.10.0", - "Microsoft.TestPlatform.TestHost": "17.10.0" - } - }, - "Microsoft.Testing.Extensions.CodeCoverage": { - "type": "Direct", - "requested": "[17.10.1, )", - "resolved": "17.10.1", - "contentHash": "EdPqUfB4GShYeXiivrjWrTAjZz93tmrF313QlyK/CI4afdBAcNCrJ2IqEWAQ1n+05sc+tEwZnyaZAdStnwQqcw==", - "dependencies": { - "Microsoft.DiaSymReader": "2.0.0", - "Microsoft.Extensions.DependencyModel": "6.0.0", - "Microsoft.Testing.Platform": "1.0.0", - "Mono.Cecil": "0.11.5", - "System.Reflection.Metadata": "6.0.1" - } - }, - "Moq": { - "type": "Direct", - "requested": "[4.20.70, )", - "resolved": "4.20.70", - "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", - "dependencies": { - "Castle.Core": "5.1.1" - } - }, - "MSTest.TestAdapter": { - "type": "Direct", - "requested": "[3.4.3, )", - "resolved": "3.4.3", - "contentHash": "5ul31wYr17590gDumPxWMiBLPREfPF/ggtdPGfaKoYSsO0EW6H1GWY+7xnVCKa2SB4I/dSEZLDYSwRLDjA0LEQ==", - "dependencies": { - "Microsoft.Testing.Extensions.VSTestBridge": "1.2.1", - "Microsoft.Testing.Platform.MSBuild": "1.2.1" - } - }, - "MSTest.TestFramework": { - "type": "Direct", - "requested": "[3.4.3, )", - "resolved": "3.4.3", - "contentHash": "hu7F0PyRe47LScY2SCjRFIzP2QYxq1oeHMAIdao9onUm5WhobO9tfZrFAAkJ4v+66EQjilloEbA4kspVHCZpTg==" - }, - "Castle.Core": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", - "dependencies": { - "System.Diagnostics.EventLog": "6.0.0" - } - }, - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Microsoft.ApplicationInsights": { - "type": "Transitive", - "resolved": "2.22.0", - "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "5.0.0" - } - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" - }, - "Microsoft.DiaSymReader": { - "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "QcZrCETsBJqy/vQpFtJc+jSXQ0K5sucQ6NUFbTNVHD4vfZZOwjZ/3sBzczkC4DityhD3AVO/+K/+9ioLs1AgRA==" - }, - "Microsoft.Extensions.DependencyModel": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" - } - }, - "Microsoft.Testing.Extensions.Telemetry": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "MKGxwQhDDEoTS/ntFb21Z6Bxh9VvknmSLgEWH+NFD86fbcIqE2Al8lrXkQPeH+AqCvlhx2WnPLKd81T2PXc2dw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Extensions.TrxReport.Abstractions": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "46SnzaLR+SDaTtBWy49xdFm/rI40I8nZtziqnt2d4lgILKovWPnkM8Pehnga/uwl+OznVIh0XuRsN3NokkX1TQ==", - "dependencies": { - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Extensions.VSTestBridge": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "Tu8CWHEwV/92WM2DRr/qeIdH243diV5s43ODPLl13XeRqGbZlu9lk7X0a7kcxhp0BLRlA3fqMW3F6RynrnDrPw==", - "dependencies": { - "Microsoft.ApplicationInsights": "2.22.0", - "Microsoft.TestPlatform.ObjectModel": "17.5.0", - "Microsoft.Testing.Extensions.Telemetry": "1.2.1", - "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.2.1", - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.Testing.Platform": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "mb7irPwqjgusJ05BxuQ5KP6uofWaoDr/dfjFNItX1Q1Ntv3EDMr3CeLInrlU2PNcPwwObw4X6bZG7wJvvFjKZQ==" - }, - "Microsoft.Testing.Platform.MSBuild": { - "type": "Transitive", - "resolved": "1.2.1", - "contentHash": "leUhW4iQNy7vmPk5uRHd4OROqfRtugWDQkWL/4AD17gxZwAAwGCaTcrqG0YVPi7uuZ+lj2Loa6kU7hBLA/v5+w==", - "dependencies": { - "Microsoft.Testing.Platform": "1.2.1" - } - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", - "dependencies": { - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.10.0", - "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.10.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Mono.Cecil": { - "type": "Transitive", - "resolved": "0.11.5", - "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" - }, - "System.Diagnostics.EventLog": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", - "dependencies": { - "System.Collections.Immutable": "6.0.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "google.genai": { - "type": "Project", - "dependencies": { - "Google.Apis.Auth": "[1.69.0, )", - "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", - "MimeTypes": "[2.5.2, )" - } - }, - "Google.Apis.Auth": { - "type": "CentralTransitive", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "CentralTransitive", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "CentralTransitive", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + "net8.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.10.0, )", + "resolved": "17.10.0", + "contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==", + "dependencies": { + "Microsoft.CodeCoverage": "17.10.0", + "Microsoft.TestPlatform.TestHost": "17.10.0" + } + }, + "Microsoft.Testing.Extensions.CodeCoverage": { + "type": "Direct", + "requested": "[17.10.1, )", + "resolved": "17.10.1", + "contentHash": "EdPqUfB4GShYeXiivrjWrTAjZz93tmrF313QlyK/CI4afdBAcNCrJ2IqEWAQ1n+05sc+tEwZnyaZAdStnwQqcw==", + "dependencies": { + "Microsoft.DiaSymReader": "2.0.0", + "Microsoft.Extensions.DependencyModel": "6.0.0", + "Microsoft.Testing.Platform": "1.0.0", + "Mono.Cecil": "0.11.5", + "System.Reflection.Metadata": "6.0.1" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "MSTest.TestAdapter": { + "type": "Direct", + "requested": "[3.4.3, )", + "resolved": "3.4.3", + "contentHash": "5ul31wYr17590gDumPxWMiBLPREfPF/ggtdPGfaKoYSsO0EW6H1GWY+7xnVCKa2SB4I/dSEZLDYSwRLDjA0LEQ==", + "dependencies": { + "Microsoft.Testing.Extensions.VSTestBridge": "1.2.1", + "Microsoft.Testing.Platform.MSBuild": "1.2.1" + } + }, + "MSTest.TestFramework": { + "type": "Direct", + "requested": "[3.4.3, )", + "resolved": "3.4.3", + "contentHash": "hu7F0PyRe47LScY2SCjRFIzP2QYxq1oeHMAIdao9onUm5WhobO9tfZrFAAkJ4v+66EQjilloEbA4kspVHCZpTg==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.22.0", + "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA==" + }, + "Microsoft.DiaSymReader": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "QcZrCETsBJqy/vQpFtJc+jSXQ0K5sucQ6NUFbTNVHD4vfZZOwjZ/3sBzczkC4DityhD3AVO/+K/+9ioLs1AgRA==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, + "Microsoft.Testing.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "MKGxwQhDDEoTS/ntFb21Z6Bxh9VvknmSLgEWH+NFD86fbcIqE2Al8lrXkQPeH+AqCvlhx2WnPLKd81T2PXc2dw==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "46SnzaLR+SDaTtBWy49xdFm/rI40I8nZtziqnt2d4lgILKovWPnkM8Pehnga/uwl+OznVIh0XuRsN3NokkX1TQ==", + "dependencies": { + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Extensions.VSTestBridge": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "Tu8CWHEwV/92WM2DRr/qeIdH243diV5s43ODPLl13XeRqGbZlu9lk7X0a7kcxhp0BLRlA3fqMW3F6RynrnDrPw==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.22.0", + "Microsoft.TestPlatform.ObjectModel": "17.5.0", + "Microsoft.Testing.Extensions.Telemetry": "1.2.1", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.2.1", + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "mb7irPwqjgusJ05BxuQ5KP6uofWaoDr/dfjFNItX1Q1Ntv3EDMr3CeLInrlU2PNcPwwObw4X6bZG7wJvvFjKZQ==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "1.2.1", + "contentHash": "leUhW4iQNy7vmPk5uRHd4OROqfRtugWDQkWL/4AD17gxZwAAwGCaTcrqG0YVPi7uuZ+lj2Loa6kU7hBLA/v5+w==", + "dependencies": { + "Microsoft.Testing.Platform": "1.2.1" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.10.0", + "contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.10.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.11.5", + "contentHash": "fxfX+0JGTZ8YQeu1MYjbBiK2CYTSzDyEeIixt+yqKKTn7FW8rv7JMY70qevup4ZJfD7Kk/VG/jDzQQTpfch87g==" + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + }, + "google.genai": { + "type": "Project", + "dependencies": { + "Google.Apis.Auth": "[1.69.0, )", + "Microsoft.Extensions.AI.Abstractions": "[10.4.1, )", + "MimeTypes": "[2.5.2, )", + "System.Text.Json": "[10.0.4, )" + } + }, + "Google.Apis.Auth": { + "type": "CentralTransitive", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "CentralTransitive", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "CentralTransitive", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "CentralTransitive", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + } + } + } } \ No newline at end of file diff --git a/Google.GenAI/Batches.cs b/Google.GenAI/Batches.cs index 022aac92..493d11b9 100644 --- a/Google.GenAI/Batches.cs +++ b/Google.GenAI/Batches.cs @@ -1,2145 +1,2145 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Google.GenAI { - - public sealed class Batches { - private readonly ApiClient _apiClient; - - internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { - Common.SetValueByPath(toObject, new string[] { "apiKey" }, - Common.GetValueByPath(fromObject, new string[] { "apiKey" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { - throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { - throw new NotSupportedException("authType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { - throw new NotSupportedException( - "googleServiceAccountConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { - throw new NotSupportedException( - "httpBasicAuthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { - throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { - throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode BatchJobDestinationFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "responsesFile" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileName" }, - Common.GetValueByPath(fromObject, new string[] { "responsesFile" })); - } - - if (Common.GetValueByPath(fromObject, - new string[] { "inlinedResponses", "inlinedResponses" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath( - fromObject, new string[] { "inlinedResponses", "inlinedResponses" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(InlinedResponseFromMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "inlinedResponses" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlinedEmbedContentResponses", - "inlinedResponses" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "inlinedEmbedContentResponses" }, - Common.GetValueByPath( - fromObject, new string[] { "inlinedEmbedContentResponses", "inlinedResponses" })); - } - - return toObject; - } - - internal JsonNode BatchJobDestinationFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "predictionsFormat" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "format" }, - Common.GetValueByPath(fromObject, new string[] { "predictionsFormat" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "gcsDestination", "outputUriPrefix" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "gcsUri" }, - Common.GetValueByPath(fromObject, - new string[] { "gcsDestination", "outputUriPrefix" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bigqueryDestination", "outputUri" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "bigqueryUri" }, - Common.GetValueByPath(fromObject, new string[] { "bigqueryDestination", "outputUri" })); - } - - return toObject; - } - - internal JsonNode BatchJobDestinationToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "format" }) != null) { - Common.SetValueByPath(toObject, new string[] { "predictionsFormat" }, - Common.GetValueByPath(fromObject, new string[] { "format" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "gcsDestination", "outputUriPrefix" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "bigqueryDestination", "outputUri" }, - Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileName" }))) { - throw new NotSupportedException("fileName parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "inlinedResponses" }))) { - throw new NotSupportedException( - "inlinedResponses parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "inlinedEmbedContentResponses" }))) { - throw new NotSupportedException( - "inlinedEmbedContentResponses parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode BatchJobFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata", "displayName" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "metadata", "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata", "state" }) != null) { - Common.SetValueByPath(toObject, new string[] { "state" }, - Transformers.TJobState(Common.GetValueByPath( - fromObject, new string[] { "metadata", "state" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata", "createTime" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "createTime" }, - Common.GetValueByPath(fromObject, new string[] { "metadata", "createTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata", "endTime" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "endTime" }, - Common.GetValueByPath(fromObject, new string[] { "metadata", "endTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata", "updateTime" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "updateTime" }, - Common.GetValueByPath(fromObject, new string[] { "metadata", "updateTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata", "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "model" }, - Common.GetValueByPath(fromObject, new string[] { "metadata", "model" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata", "output" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "dest" }, - BatchJobDestinationFromMldev( - Common.ParseToJsonNode(Transformers.TRecvBatchJobDestination( - Common.GetValueByPath(fromObject, new string[] { "metadata", "output" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode BatchJobFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "state" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "state" }, - Transformers.TJobState(Common.GetValueByPath(fromObject, new string[] { "state" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "createTime" }, - Common.GetValueByPath(fromObject, new string[] { "createTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "startTime" }, - Common.GetValueByPath(fromObject, new string[] { "startTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "endTime" }, - Common.GetValueByPath(fromObject, new string[] { "endTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "updateTime" }, - Common.GetValueByPath(fromObject, new string[] { "updateTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath(toObject, new string[] { "model" }, - Common.GetValueByPath(fromObject, new string[] { "model" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inputConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "src" }, - BatchJobSourceFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "inputConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "dest" }, - BatchJobDestinationFromVertex( - Common.ParseToJsonNode(Transformers.TRecvBatchJobDestination( - Common.GetValueByPath(fromObject, new string[] { "outputConfig" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "completionStats" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "completionStats" }, - Common.GetValueByPath(fromObject, new string[] { "completionStats" })); - } - - return toObject; - } - - internal JsonNode BatchJobSourceFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "instancesFormat" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "format" }, - Common.GetValueByPath(fromObject, new string[] { "instancesFormat" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "gcsSource", "uris" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "gcsUri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsSource", "uris" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bigquerySource", "inputUri" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "bigqueryUri" }, - Common.GetValueByPath(fromObject, new string[] { "bigquerySource", "inputUri" })); - } - - return toObject; - } - - internal JsonNode BatchJobSourceToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "format" }))) { - throw new NotSupportedException("format parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "gcsUri" }))) { - throw new NotSupportedException("gcsUri parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" }))) { - throw new NotSupportedException("bigqueryUri parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileName" }, - Common.GetValueByPath(fromObject, new string[] { "fileName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(InlinedRequestToMldev(apiClient, Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "requests", "requests" }, result); - } - - return toObject; - } - - internal JsonNode BatchJobSourceToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "format" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instancesFormat" }, - Common.GetValueByPath(fromObject, new string[] { "format" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "gcsSource", "uris" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "bigquerySource", "inputUri" }, - Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileName" }))) { - throw new NotSupportedException("fileName parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }))) { - throw new NotSupportedException("inlinedRequests parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { - Common.SetValueByPath(toObject, new string[] { "data" }, - Common.GetValueByPath(fromObject, new string[] { "data" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode CancelBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TBatchJobName(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode CancelBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TBatchJobName(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode CandidateFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { - Common.SetValueByPath(toObject, new string[] { "content" }, - Common.GetValueByPath(fromObject, new string[] { "content" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "citationMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "citationMetadata" }, - CitationMetadataFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "citationMetadata" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tokenCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tokenCount" }, - Common.GetValueByPath(fromObject, new string[] { "tokenCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "finishReason" }) != null) { - Common.SetValueByPath(toObject, new string[] { "finishReason" }, - Common.GetValueByPath(fromObject, new string[] { "finishReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "groundingMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "avgLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "index" }) != null) { - Common.SetValueByPath(toObject, new string[] { "index" }, - Common.GetValueByPath(fromObject, new string[] { "index" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobsResult" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobsResult" }, - Common.GetValueByPath(fromObject, new string[] { "logprobsResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyRatings" }) != null) { - Common.SetValueByPath(toObject, new string[] { "safetyRatings" }, - Common.GetValueByPath(fromObject, new string[] { "safetyRatings" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "urlContextMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" })); - } - - return toObject; - } - - internal JsonNode CitationMetadataFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "citationSources" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "citations" }, - Common.GetValueByPath(fromObject, new string[] { "citationSources" })); - } - - return toObject; - } - - internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode CreateBatchJobConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "batch", "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "dest" }))) { - throw new NotSupportedException("dest parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode CreateBatchJobConfigToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "dest" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "outputConfig" }, - BatchJobDestinationToVertex( - Common.ParseToJsonNode(Transformers.TBatchJobDestination( - Common.GetValueByPath(fromObject, new string[] { "dest" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode CreateBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "src" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "batch", "inputConfig" }, - BatchJobSourceToMldev(apiClient, - Common.ParseToJsonNode(Transformers.TBatchJobSource( - Common.GetValueByPath(fromObject, new string[] { "src" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateBatchJobConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode CreateBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "src" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "inputConfig" }, - BatchJobSourceToVertex(Common.ParseToJsonNode(Transformers.TBatchJobSource( - Common.GetValueByPath(fromObject, new string[] { "src" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateBatchJobConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode CreateEmbeddingsBatchJobConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "batch", "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - return toObject; - } - - internal JsonNode CreateEmbeddingsBatchJobParametersToMldev(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "src" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "batch", "inputConfig" }, - EmbeddingsBatchJobSourceToMldev( - apiClient, - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "src" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateEmbeddingsBatchJobConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode DeleteBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TBatchJobName(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TBatchJobName(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteResourceJobFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - return toObject; - } - - internal JsonNode DeleteResourceJobFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - return toObject; - } - - internal JsonNode EmbedContentBatchToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "requests[]", "request", "content" }, - Transformers.TContentsForEmbed( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "contents" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_self" }, - EmbedContentConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "config" })), - toObject)); - Common.MoveValueByPath( - toObject, - new Dictionary { { "requests[].*", "requests[].request.*" } }); - } - - return toObject; - } - - internal JsonNode EmbedContentConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "requests[]", "taskType" }, - Common.GetValueByPath(fromObject, new string[] { "taskType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "requests[]", "title" }, - Common.GetValueByPath(fromObject, new string[] { "title" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "requests[]", "outputDimensionality" }, - Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mimeType" }))) { - throw new NotSupportedException("mimeType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }))) { - throw new NotSupportedException("autoTruncate parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode EmbeddingsBatchJobSourceToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "fileName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "file_name" }, - Common.GetValueByPath(fromObject, new string[] { "fileName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "requests" }, - EmbedContentBatchToMldev(apiClient, - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "inlinedRequests" })), - toObject)); - } - - return toObject; - } - - internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileUri" }, - Common.GetValueByPath(fromObject, new string[] { "fileUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { - Common.SetValueByPath(toObject, new string[] { "id" }, - Common.GetValueByPath(fromObject, new string[] { "id" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { - Common.SetValueByPath(toObject, new string[] { "args" }, - Common.GetValueByPath(fromObject, new string[] { "args" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { - throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { - throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "allowedFunctionNames" }, - Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { - throw new NotSupportedException( - "streamFunctionCallArguments parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode GenerateContentConfigToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "candidateCount" }, - Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { - Common.SetValueByPath(toObject, new string[] { "stopSequences" }, - Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobs" }, - Common.GetValueByPath(fromObject, new string[] { "logprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "presencePenalty" }, - Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "frequencyPenalty" }, - Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(toObject, new string[] { "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseMimeType" }, - Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseSchema" }, - Transformers.TSchema(Common.GetValueByPath( - fromObject, new string[] { "responseSchema" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "routingConfig" }))) { - throw new NotSupportedException("routingConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }))) { - throw new NotSupportedException( - "modelSelectionConfig parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetySettings" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "safetySettings" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(SafetySettingToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "safetySettings" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - var keyList = - Transformers.TTools(Common.GetValueByPath(fromObject, new string[] { "tools" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ToolToMldev(Common.ParseToJsonNode(Transformers.TTool(record)), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, - ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "toolConfig" })), - toObject)); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { - throw new NotSupportedException("labels parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContent" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "cachedContent" }, - Transformers.TCachedContentName( - this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "cachedContent" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "speechConfig" }, - Transformers.TSpeechConfig(Common.GetValueByPath( - fromObject, new string[] { "speechConfig" }))); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }))) { - throw new NotSupportedException("audioTimestamp parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageConfig" }, - ImageConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "imageConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "enableEnhancedCivicAnswers" }, - Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" }))) { - throw new NotSupportedException( - "modelArmorConfig parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode GenerateContentResponseFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidates" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "candidates" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(CandidateFromMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "candidates" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "modelVersion" }) != null) { - Common.SetValueByPath(toObject, new string[] { "modelVersion" }, - Common.GetValueByPath(fromObject, new string[] { "modelVersion" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "promptFeedback" }) != null) { - Common.SetValueByPath(toObject, new string[] { "promptFeedback" }, - Common.GetValueByPath(fromObject, new string[] { "promptFeedback" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseId" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseId" }, - Common.GetValueByPath(fromObject, new string[] { "responseId" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "usageMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "usageMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "modelStatus" }) != null) { - Common.SetValueByPath(toObject, new string[] { "modelStatus" }, - Common.GetValueByPath(fromObject, new string[] { "modelStatus" })); - } - - return toObject; - } - - internal JsonNode GetBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TBatchJobName(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode GetBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TBatchJobName(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "authConfig" }, - AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "authConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { - Common.SetValueByPath(toObject, new string[] { "enableWidget" }, - Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { - Common.SetValueByPath(toObject, new string[] { "searchTypes" }, - Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { - throw new NotSupportedException( - "blockingConfidence parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { - throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "timeRangeFilter" }, - Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); - } - - return toObject; - } - - internal JsonNode ImageConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(toObject, new string[] { "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageSize" }, - Common.GetValueByPath(fromObject, new string[] { "imageSize" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "personGeneration" }))) { - throw new NotSupportedException( - "personGeneration parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "prominentPeople" }))) { - throw new NotSupportedException( - "prominentPeople parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }))) { - throw new NotSupportedException("outputMimeType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }))) { - throw new NotSupportedException( - "outputCompressionQuality parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" }))) { - throw new NotSupportedException( - "imageOutputOptions parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode InlinedRequestToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "request", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "request", "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "request", "generationConfig" }, - GenerateContentConfigToMldev( - apiClient, - Common.ParseToJsonNode( - Common.GetValueByPath(fromObject, new string[] { "config" })), - (JsonObject?)(Common.GetValueByPath(toObject, new string[] { "request" }) ?? - new JsonObject()))); - } - - return toObject; - } - - internal JsonNode InlinedResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "response" }, - GenerateContentResponseFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "response" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - return toObject; - } - - internal JsonNode ListBatchJobsConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "filter" }))) { - throw new NotSupportedException("filter parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode ListBatchJobsConfigToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, - Common.GetValueByPath(fromObject, new string[] { "filter" })); - } - - return toObject; - } - - internal JsonNode ListBatchJobsParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListBatchJobsConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListBatchJobsParametersToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListBatchJobsConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListBatchJobsResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "operations" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "operations" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(BatchJobFromMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "batchJobs" }, result); - } - - return toObject; - } - - internal JsonNode ListBatchJobsResponseFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "batchPredictionJobs" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "batchPredictionJobs" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(BatchJobFromVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "batchJobs" }, result); - } - - return toObject; - } - - internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileData" }, - FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "fileData" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCall" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "inlineData" }, - BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "inlineData" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolCall" }, - Common.GetValueByPath(fromObject, new string[] { "toolCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolResponse" }, - Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "partMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); - } - - return toObject; - } - - internal JsonNode SafetySettingToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "category" }) != null) { - Common.SetValueByPath(toObject, new string[] { "category" }, - Common.GetValueByPath(fromObject, new string[] { "category" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "method" }))) { - throw new NotSupportedException("method parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "threshold" }) != null) { - Common.SetValueByPath(toObject, new string[] { "threshold" }, - Common.GetValueByPath(fromObject, new string[] { "threshold" })); - } - - return toObject; - } - - internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "retrievalConfig" }, - Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionCallingConfig" }, - FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCallingConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "includeServerSideToolInvocations" }, - Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); - } - - return toObject; - } - - internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { - throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "computerUse" }, - Common.GetValueByPath(fromObject, new string[] { "computerUse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileSearch" }, - Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearch" }, - GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleSearch" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleMaps" }, - GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleMaps" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { - throw new NotSupportedException( - "enterpriseWebSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionDeclarations" }, - Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearchRetrieval" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { - throw new NotSupportedException( - "parallelAiSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath(toObject, new string[] { "urlContext" }, - Common.GetValueByPath(fromObject, new string[] { "urlContext" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mcpServers" }, - Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); - } - - return toObject; - } - - public Batches(ApiClient apiClient) { - _apiClient = apiClient; - } - - private async Task PrivateCreateAsync(string? model, BatchJobSource src, - CreateBatchJobConfig? config, - CancellationToken cancellationToken = default) { - CreateBatchJobParameters parameter = new CreateBatchJobParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(src)) { - parameter.Src = src; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse CreateBatchJobParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = CreateBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("batchPredictionJobs", body["_url"]); - } else { - body = CreateBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{model}:batchGenerateContent", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = BatchJobFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = BatchJobFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateCreateEmbeddingsAsync( - string? model, EmbeddingsBatchJobSource src, CreateEmbeddingsBatchJobConfig? config, - CancellationToken cancellationToken = default) { - CreateEmbeddingsBatchJobParameters parameter = new CreateEmbeddingsBatchJobParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(src)) { - parameter.Src = src; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse CreateEmbeddingsBatchJobParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = CreateEmbeddingsBatchJobParametersToMldev(this._apiClient, parameterNode, - new JsonObject()); - path = Common.FormatMap("{model}:asyncBatchEmbedContent", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = BatchJobFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - /// - /// Gets a batch job resource. - /// - /// A fully-qualified BatchJob resource name or ID. - /// Example: "projects/.../locations/.../batchPredictionJobs/456" - /// or "456" when project and location are initialized in the - /// Vertex AI client. Or "batches/abc" using the Gemini Developer AI client. - /// A for configuring the get - /// request. The for the - /// request. A object that contains the info of the - /// batch job. - - public async Task GetAsync(string name, GetBatchJobConfig? config = null, - CancellationToken cancellationToken = default) { - GetBatchJobParameters parameter = new GetBatchJobParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetBatchJobParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GetBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("batchPredictionJobs/{name}", body["_url"]); - } else { - body = GetBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("batches/{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = BatchJobFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = BatchJobFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - /// - /// Cancels a batch job resource. - /// - /// A fully-qualified BatchJob resource name or ID. - /// Example: "projects/.../locations/.../batchPredictionJobs/456" - /// or "456" when project and location are initialized in the - /// Vertex AI client. Or "batches/abc" using the Gemini Developer AI client. - /// A for configuring the cancel - /// request. The for the - /// request. A that represents the asynchronous - /// operation. - - public async Task CancelAsync(string name, CancelBatchJobConfig? config = null, - CancellationToken cancellationToken = default) { - CancelBatchJobParameters parameter = new CancelBatchJobParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse CancelBatchJobParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = CancelBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("batchPredictionJobs/{name}:cancel", body["_url"]); - } else { - body = CancelBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("batches/{name}:cancel", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return; - } - - private async Task PrivateListAsync( - ListBatchJobsConfig? config, CancellationToken cancellationToken = default) { - ListBatchJobsParameters parameter = new ListBatchJobsParameters(); - - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse ListBatchJobsParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = ListBatchJobsParametersToVertex(parameterNode, new JsonObject()); - path = Common.FormatMap("batchPredictionJobs", body["_url"]); - } else { - body = ListBatchJobsParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("batches", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = ListBatchJobsResponseFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = ListBatchJobsResponseFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Deletes a batch job resource. - /// - /// A fully-qualified BatchJob resource name or ID. - /// Example: "projects/.../locations/.../batchPredictionJobs/456" - /// or "456" when project and location are initialized in the - /// Vertex AI client. Or "batches/abc" using the Gemini Developer AI client. - /// A for configuring the delete - /// request. The for the - /// request. A object that shows the status of - /// the deletion. - - public async Task DeleteAsync( - string name, DeleteBatchJobConfig? config = null, - CancellationToken cancellationToken = default) { - DeleteBatchJobParameters parameter = new DeleteBatchJobParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse DeleteBatchJobParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = DeleteBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("batchPredictionJobs/{name}", body["_url"]); - } else { - body = DeleteBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("batches/{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = DeleteResourceJobFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = DeleteResourceJobFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - /// - /// Makes an API request to list the available batch jobs. - /// - /// A for configuring the list - /// request. The for the - /// request. A object that contains the list of batch jobs. The pager is an - /// iterable and automatically queries the next page once the current page is - /// exhausted. - - public async Task> ListAsync( - ListBatchJobsConfig? config = null, CancellationToken cancellationToken = default) { - config ??= new ListBatchJobsConfig(); - var initialResponse = await PrivateListAsync(config, cancellationToken); - - return new Pager( - requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), - extractItems: response => response.BatchJobs, - extractNextPageToken: response => response.NextPageToken, - extractHttpResponse: response => response.SdkHttpResponse, - updateConfigPageToken: (cfg, token) => { - cfg.PageToken = token; - return cfg; - }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); - } - - /// - /// Creates a batch job. - /// - /// The model to use for the batch job. - /// The of the batch job. - /// Currently Vertex AI supports GCS URIs or BigQuery URI. Example: "gs://path/to/input/data" or - /// "bq://projectId.bqDatasetId.bqTableId". Gemini Developer API supports List of - /// inlined_request, or file name. Example: "files/file_name". Optional to configure the batch - /// job. The cancellation token for the request. - /// A that represents the asynchronous operation. The task - /// result contains a instance with batch job details. - public async Task CreateAsync(string model, BatchJobSource src, - CreateBatchJobConfig config, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - if (src.InlinedRequests != null) { - throw new NotSupportedException("inlinedRequests is not supported for Vertex AI."); - } - if (src.FileName != null) { - throw new NotSupportedException("fileName is not supported for Vertex AI."); - } - if (src.GcsUri != null && src.BigqueryUri != null) { - throw new ArgumentException("Only one of gcsUri and bigqueryUri can be set."); - } - if (src.GcsUri == null && src.BigqueryUri == null) { - throw new ArgumentException("One of gcsUri and bigqueryUri must be set."); - } - } else { - if (src.FileName != null && src.InlinedRequests != null) { - throw new ArgumentException("Only one of fileName and InlinedRequests can be set."); - } - if (src.FileName == null && src.InlinedRequests == null) { - throw new ArgumentException("one of fileName and InlinedRequests must be set."); - } - } - return await this.PrivateCreateAsync(model, src, config, cancellationToken); - } - - /// - /// Makes an API request to create the batch embeddings job. This method is experimental. - /// - /// The model to use for the embeddings. - /// The of the batch job. - /// Gemini Developer API supports List of inlined_request, or file name. Example: - /// "files/file_name". Optional to configure the batch job. The cancellation token for the request. A that represents the asynchronous operation. The task result contains - /// a instance with batch job details. - public async Task CreateEmbeddingsAsync( - string model, EmbeddingsBatchJobSource src, CreateEmbeddingsBatchJobConfig config, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException("Vertex AI does not support batches.createEmbeddings."); - } - return await this.PrivateCreateEmbeddingsAsync(model, src, config, cancellationToken); - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Google.GenAI { + + public sealed class Batches { + private readonly ApiClient _apiClient; + + internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { + Common.SetValueByPath(toObject, new string[] { "apiKey" }, + Common.GetValueByPath(fromObject, new string[] { "apiKey" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { + throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { + throw new NotSupportedException("authType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { + throw new NotSupportedException( + "googleServiceAccountConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { + throw new NotSupportedException( + "httpBasicAuthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { + throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { + throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode BatchJobDestinationFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "responsesFile" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileName" }, + Common.GetValueByPath(fromObject, new string[] { "responsesFile" })); + } + + if (Common.GetValueByPath(fromObject, + new string[] { "inlinedResponses", "inlinedResponses" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath( + fromObject, new string[] { "inlinedResponses", "inlinedResponses" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(InlinedResponseFromMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "inlinedResponses" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlinedEmbedContentResponses", + "inlinedResponses" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "inlinedEmbedContentResponses" }, + Common.GetValueByPath( + fromObject, new string[] { "inlinedEmbedContentResponses", "inlinedResponses" })); + } + + return toObject; + } + + internal JsonNode BatchJobDestinationFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "predictionsFormat" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "format" }, + Common.GetValueByPath(fromObject, new string[] { "predictionsFormat" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "gcsDestination", "outputUriPrefix" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "gcsUri" }, + Common.GetValueByPath(fromObject, + new string[] { "gcsDestination", "outputUriPrefix" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bigqueryDestination", "outputUri" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "bigqueryUri" }, + Common.GetValueByPath(fromObject, new string[] { "bigqueryDestination", "outputUri" })); + } + + return toObject; + } + + internal JsonNode BatchJobDestinationToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "format" }) != null) { + Common.SetValueByPath(toObject, new string[] { "predictionsFormat" }, + Common.GetValueByPath(fromObject, new string[] { "format" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "gcsDestination", "outputUriPrefix" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "bigqueryDestination", "outputUri" }, + Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileName" }))) { + throw new NotSupportedException("fileName parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "inlinedResponses" }))) { + throw new NotSupportedException( + "inlinedResponses parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "inlinedEmbedContentResponses" }))) { + throw new NotSupportedException( + "inlinedEmbedContentResponses parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode BatchJobFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata", "displayName" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "metadata", "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata", "state" }) != null) { + Common.SetValueByPath(toObject, new string[] { "state" }, + Transformers.TJobState(Common.GetValueByPath( + fromObject, new string[] { "metadata", "state" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata", "createTime" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "createTime" }, + Common.GetValueByPath(fromObject, new string[] { "metadata", "createTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata", "endTime" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "endTime" }, + Common.GetValueByPath(fromObject, new string[] { "metadata", "endTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata", "updateTime" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "updateTime" }, + Common.GetValueByPath(fromObject, new string[] { "metadata", "updateTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata", "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "model" }, + Common.GetValueByPath(fromObject, new string[] { "metadata", "model" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata", "output" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "dest" }, + BatchJobDestinationFromMldev( + Common.ParseToJsonNode(Transformers.TRecvBatchJobDestination( + Common.GetValueByPath(fromObject, new string[] { "metadata", "output" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode BatchJobFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "state" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "state" }, + Transformers.TJobState(Common.GetValueByPath(fromObject, new string[] { "state" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "createTime" }, + Common.GetValueByPath(fromObject, new string[] { "createTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "startTime" }, + Common.GetValueByPath(fromObject, new string[] { "startTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "endTime" }, + Common.GetValueByPath(fromObject, new string[] { "endTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "updateTime" }, + Common.GetValueByPath(fromObject, new string[] { "updateTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath(toObject, new string[] { "model" }, + Common.GetValueByPath(fromObject, new string[] { "model" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inputConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "src" }, + BatchJobSourceFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "inputConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "dest" }, + BatchJobDestinationFromVertex( + Common.ParseToJsonNode(Transformers.TRecvBatchJobDestination( + Common.GetValueByPath(fromObject, new string[] { "outputConfig" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "completionStats" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "completionStats" }, + Common.GetValueByPath(fromObject, new string[] { "completionStats" })); + } + + return toObject; + } + + internal JsonNode BatchJobSourceFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "instancesFormat" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "format" }, + Common.GetValueByPath(fromObject, new string[] { "instancesFormat" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "gcsSource", "uris" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "gcsUri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsSource", "uris" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bigquerySource", "inputUri" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "bigqueryUri" }, + Common.GetValueByPath(fromObject, new string[] { "bigquerySource", "inputUri" })); + } + + return toObject; + } + + internal JsonNode BatchJobSourceToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "format" }))) { + throw new NotSupportedException("format parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "gcsUri" }))) { + throw new NotSupportedException("gcsUri parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" }))) { + throw new NotSupportedException("bigqueryUri parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileName" }, + Common.GetValueByPath(fromObject, new string[] { "fileName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(InlinedRequestToMldev(apiClient, Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "requests", "requests" }, result); + } + + return toObject; + } + + internal JsonNode BatchJobSourceToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "format" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instancesFormat" }, + Common.GetValueByPath(fromObject, new string[] { "format" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "gcsSource", "uris" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "bigquerySource", "inputUri" }, + Common.GetValueByPath(fromObject, new string[] { "bigqueryUri" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileName" }))) { + throw new NotSupportedException("fileName parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }))) { + throw new NotSupportedException("inlinedRequests parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { + Common.SetValueByPath(toObject, new string[] { "data" }, + Common.GetValueByPath(fromObject, new string[] { "data" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode CancelBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TBatchJobName(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode CancelBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TBatchJobName(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode CandidateFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { + Common.SetValueByPath(toObject, new string[] { "content" }, + Common.GetValueByPath(fromObject, new string[] { "content" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "citationMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "citationMetadata" }, + CitationMetadataFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "citationMetadata" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tokenCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tokenCount" }, + Common.GetValueByPath(fromObject, new string[] { "tokenCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "finishReason" }) != null) { + Common.SetValueByPath(toObject, new string[] { "finishReason" }, + Common.GetValueByPath(fromObject, new string[] { "finishReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "groundingMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "avgLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "index" }) != null) { + Common.SetValueByPath(toObject, new string[] { "index" }, + Common.GetValueByPath(fromObject, new string[] { "index" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobsResult" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobsResult" }, + Common.GetValueByPath(fromObject, new string[] { "logprobsResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyRatings" }) != null) { + Common.SetValueByPath(toObject, new string[] { "safetyRatings" }, + Common.GetValueByPath(fromObject, new string[] { "safetyRatings" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "urlContextMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" })); + } + + return toObject; + } + + internal JsonNode CitationMetadataFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "citationSources" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "citations" }, + Common.GetValueByPath(fromObject, new string[] { "citationSources" })); + } + + return toObject; + } + + internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode CreateBatchJobConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "batch", "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "dest" }))) { + throw new NotSupportedException("dest parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode CreateBatchJobConfigToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "dest" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "outputConfig" }, + BatchJobDestinationToVertex( + Common.ParseToJsonNode(Transformers.TBatchJobDestination( + Common.GetValueByPath(fromObject, new string[] { "dest" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode CreateBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "src" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "batch", "inputConfig" }, + BatchJobSourceToMldev(apiClient, + Common.ParseToJsonNode(Transformers.TBatchJobSource( + Common.GetValueByPath(fromObject, new string[] { "src" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateBatchJobConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode CreateBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "src" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "inputConfig" }, + BatchJobSourceToVertex(Common.ParseToJsonNode(Transformers.TBatchJobSource( + Common.GetValueByPath(fromObject, new string[] { "src" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateBatchJobConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode CreateEmbeddingsBatchJobConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "batch", "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + return toObject; + } + + internal JsonNode CreateEmbeddingsBatchJobParametersToMldev(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "src" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "batch", "inputConfig" }, + EmbeddingsBatchJobSourceToMldev( + apiClient, + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "src" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateEmbeddingsBatchJobConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode DeleteBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TBatchJobName(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TBatchJobName(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteResourceJobFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + return toObject; + } + + internal JsonNode DeleteResourceJobFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + return toObject; + } + + internal JsonNode EmbedContentBatchToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "requests[]", "request", "content" }, + Transformers.TContentsForEmbed( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "contents" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_self" }, + EmbedContentConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "config" })), + toObject)); + Common.MoveValueByPath( + toObject, + new Dictionary { { "requests[].*", "requests[].request.*" } }); + } + + return toObject; + } + + internal JsonNode EmbedContentConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "requests[]", "taskType" }, + Common.GetValueByPath(fromObject, new string[] { "taskType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "requests[]", "title" }, + Common.GetValueByPath(fromObject, new string[] { "title" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "requests[]", "outputDimensionality" }, + Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mimeType" }))) { + throw new NotSupportedException("mimeType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }))) { + throw new NotSupportedException("autoTruncate parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode EmbeddingsBatchJobSourceToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "fileName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "file_name" }, + Common.GetValueByPath(fromObject, new string[] { "fileName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlinedRequests" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "requests" }, + EmbedContentBatchToMldev(apiClient, + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "inlinedRequests" })), + toObject)); + } + + return toObject; + } + + internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileUri" }, + Common.GetValueByPath(fromObject, new string[] { "fileUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { + Common.SetValueByPath(toObject, new string[] { "id" }, + Common.GetValueByPath(fromObject, new string[] { "id" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { + Common.SetValueByPath(toObject, new string[] { "args" }, + Common.GetValueByPath(fromObject, new string[] { "args" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { + throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { + throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "allowedFunctionNames" }, + Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { + throw new NotSupportedException( + "streamFunctionCallArguments parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode GenerateContentConfigToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "candidateCount" }, + Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { + Common.SetValueByPath(toObject, new string[] { "stopSequences" }, + Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobs" }, + Common.GetValueByPath(fromObject, new string[] { "logprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "presencePenalty" }, + Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "frequencyPenalty" }, + Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(toObject, new string[] { "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseMimeType" }, + Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseSchema" }, + Transformers.TSchema(Common.GetValueByPath( + fromObject, new string[] { "responseSchema" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "routingConfig" }))) { + throw new NotSupportedException("routingConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }))) { + throw new NotSupportedException( + "modelSelectionConfig parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetySettings" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "safetySettings" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(SafetySettingToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "safetySettings" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + var keyList = + Transformers.TTools(Common.GetValueByPath(fromObject, new string[] { "tools" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ToolToMldev(Common.ParseToJsonNode(Transformers.TTool(record)), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, + ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "toolConfig" })), + toObject)); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { + throw new NotSupportedException("labels parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContent" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "cachedContent" }, + Transformers.TCachedContentName( + this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "cachedContent" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "speechConfig" }, + Transformers.TSpeechConfig(Common.GetValueByPath( + fromObject, new string[] { "speechConfig" }))); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }))) { + throw new NotSupportedException("audioTimestamp parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageConfig" }, + ImageConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "imageConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "enableEnhancedCivicAnswers" }, + Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" }))) { + throw new NotSupportedException( + "modelArmorConfig parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode GenerateContentResponseFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidates" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "candidates" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(CandidateFromMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "candidates" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "modelVersion" }) != null) { + Common.SetValueByPath(toObject, new string[] { "modelVersion" }, + Common.GetValueByPath(fromObject, new string[] { "modelVersion" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "promptFeedback" }) != null) { + Common.SetValueByPath(toObject, new string[] { "promptFeedback" }, + Common.GetValueByPath(fromObject, new string[] { "promptFeedback" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseId" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseId" }, + Common.GetValueByPath(fromObject, new string[] { "responseId" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "usageMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "usageMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "modelStatus" }) != null) { + Common.SetValueByPath(toObject, new string[] { "modelStatus" }, + Common.GetValueByPath(fromObject, new string[] { "modelStatus" })); + } + + return toObject; + } + + internal JsonNode GetBatchJobParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TBatchJobName(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode GetBatchJobParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TBatchJobName(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "authConfig" }, + AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "authConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { + Common.SetValueByPath(toObject, new string[] { "enableWidget" }, + Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { + Common.SetValueByPath(toObject, new string[] { "searchTypes" }, + Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { + throw new NotSupportedException( + "blockingConfidence parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { + throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "timeRangeFilter" }, + Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); + } + + return toObject; + } + + internal JsonNode ImageConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(toObject, new string[] { "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageSize" }, + Common.GetValueByPath(fromObject, new string[] { "imageSize" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "personGeneration" }))) { + throw new NotSupportedException( + "personGeneration parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "prominentPeople" }))) { + throw new NotSupportedException( + "prominentPeople parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }))) { + throw new NotSupportedException("outputMimeType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }))) { + throw new NotSupportedException( + "outputCompressionQuality parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" }))) { + throw new NotSupportedException( + "imageOutputOptions parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode InlinedRequestToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "request", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "request", "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "request", "generationConfig" }, + GenerateContentConfigToMldev( + apiClient, + Common.ParseToJsonNode( + Common.GetValueByPath(fromObject, new string[] { "config" })), + (JsonObject?)(Common.GetValueByPath(toObject, new string[] { "request" }) ?? + new JsonObject()))); + } + + return toObject; + } + + internal JsonNode InlinedResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "response" }, + GenerateContentResponseFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "response" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + return toObject; + } + + internal JsonNode ListBatchJobsConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "filter" }))) { + throw new NotSupportedException("filter parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode ListBatchJobsConfigToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, + Common.GetValueByPath(fromObject, new string[] { "filter" })); + } + + return toObject; + } + + internal JsonNode ListBatchJobsParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListBatchJobsConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListBatchJobsParametersToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListBatchJobsConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListBatchJobsResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "operations" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "operations" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(BatchJobFromMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "batchJobs" }, result); + } + + return toObject; + } + + internal JsonNode ListBatchJobsResponseFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "batchPredictionJobs" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "batchPredictionJobs" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(BatchJobFromVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "batchJobs" }, result); + } + + return toObject; + } + + internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileData" }, + FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "fileData" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCall" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "inlineData" }, + BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "inlineData" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolCall" }, + Common.GetValueByPath(fromObject, new string[] { "toolCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolResponse" }, + Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "partMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); + } + + return toObject; + } + + internal JsonNode SafetySettingToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "category" }) != null) { + Common.SetValueByPath(toObject, new string[] { "category" }, + Common.GetValueByPath(fromObject, new string[] { "category" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "method" }))) { + throw new NotSupportedException("method parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "threshold" }) != null) { + Common.SetValueByPath(toObject, new string[] { "threshold" }, + Common.GetValueByPath(fromObject, new string[] { "threshold" })); + } + + return toObject; + } + + internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "retrievalConfig" }, + Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionCallingConfig" }, + FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCallingConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "includeServerSideToolInvocations" }, + Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); + } + + return toObject; + } + + internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { + throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "computerUse" }, + Common.GetValueByPath(fromObject, new string[] { "computerUse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileSearch" }, + Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearch" }, + GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleSearch" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleMaps" }, + GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleMaps" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { + throw new NotSupportedException( + "enterpriseWebSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionDeclarations" }, + Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearchRetrieval" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { + throw new NotSupportedException( + "parallelAiSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath(toObject, new string[] { "urlContext" }, + Common.GetValueByPath(fromObject, new string[] { "urlContext" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mcpServers" }, + Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); + } + + return toObject; + } + + public Batches(ApiClient apiClient) { + _apiClient = apiClient; + } + + private async Task PrivateCreateAsync(string? model, BatchJobSource src, + CreateBatchJobConfig? config, + CancellationToken cancellationToken = default) { + CreateBatchJobParameters parameter = new CreateBatchJobParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(src)) { + parameter.Src = src; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse CreateBatchJobParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = CreateBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("batchPredictionJobs", body["_url"]); + } else { + body = CreateBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{model}:batchGenerateContent", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = BatchJobFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = BatchJobFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateCreateEmbeddingsAsync( + string? model, EmbeddingsBatchJobSource src, CreateEmbeddingsBatchJobConfig? config, + CancellationToken cancellationToken = default) { + CreateEmbeddingsBatchJobParameters parameter = new CreateEmbeddingsBatchJobParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(src)) { + parameter.Src = src; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse CreateEmbeddingsBatchJobParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = CreateEmbeddingsBatchJobParametersToMldev(this._apiClient, parameterNode, + new JsonObject()); + path = Common.FormatMap("{model}:asyncBatchEmbedContent", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = BatchJobFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + /// + /// Gets a batch job resource. + /// + /// A fully-qualified BatchJob resource name or ID. + /// Example: "projects/.../locations/.../batchPredictionJobs/456" + /// or "456" when project and location are initialized in the + /// Vertex AI client. Or "batches/abc" using the Gemini Developer AI client. + /// A for configuring the get + /// request. The for the + /// request. A object that contains the info of the + /// batch job. + + public async Task GetAsync(string name, GetBatchJobConfig? config = null, + CancellationToken cancellationToken = default) { + GetBatchJobParameters parameter = new GetBatchJobParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetBatchJobParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GetBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("batchPredictionJobs/{name}", body["_url"]); + } else { + body = GetBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("batches/{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = BatchJobFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = BatchJobFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + /// + /// Cancels a batch job resource. + /// + /// A fully-qualified BatchJob resource name or ID. + /// Example: "projects/.../locations/.../batchPredictionJobs/456" + /// or "456" when project and location are initialized in the + /// Vertex AI client. Or "batches/abc" using the Gemini Developer AI client. + /// A for configuring the cancel + /// request. The for the + /// request. A that represents the asynchronous + /// operation. + + public async Task CancelAsync(string name, CancelBatchJobConfig? config = null, + CancellationToken cancellationToken = default) { + CancelBatchJobParameters parameter = new CancelBatchJobParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse CancelBatchJobParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = CancelBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("batchPredictionJobs/{name}:cancel", body["_url"]); + } else { + body = CancelBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("batches/{name}:cancel", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return; + } + + private async Task PrivateListAsync( + ListBatchJobsConfig? config, CancellationToken cancellationToken = default) { + ListBatchJobsParameters parameter = new ListBatchJobsParameters(); + + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse ListBatchJobsParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = ListBatchJobsParametersToVertex(parameterNode, new JsonObject()); + path = Common.FormatMap("batchPredictionJobs", body["_url"]); + } else { + body = ListBatchJobsParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("batches", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = ListBatchJobsResponseFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = ListBatchJobsResponseFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Deletes a batch job resource. + /// + /// A fully-qualified BatchJob resource name or ID. + /// Example: "projects/.../locations/.../batchPredictionJobs/456" + /// or "456" when project and location are initialized in the + /// Vertex AI client. Or "batches/abc" using the Gemini Developer AI client. + /// A for configuring the delete + /// request. The for the + /// request. A object that shows the status of + /// the deletion. + + public async Task DeleteAsync( + string name, DeleteBatchJobConfig? config = null, + CancellationToken cancellationToken = default) { + DeleteBatchJobParameters parameter = new DeleteBatchJobParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse DeleteBatchJobParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = DeleteBatchJobParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("batchPredictionJobs/{name}", body["_url"]); + } else { + body = DeleteBatchJobParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("batches/{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = DeleteResourceJobFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = DeleteResourceJobFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + /// + /// Makes an API request to list the available batch jobs. + /// + /// A for configuring the list + /// request. The for the + /// request. A object that contains the list of batch jobs. The pager is an + /// iterable and automatically queries the next page once the current page is + /// exhausted. + + public async Task> ListAsync( + ListBatchJobsConfig? config = null, CancellationToken cancellationToken = default) { + config ??= new ListBatchJobsConfig(); + var initialResponse = await PrivateListAsync(config, cancellationToken); + + return new Pager( + requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), + extractItems: response => response.BatchJobs, + extractNextPageToken: response => response.NextPageToken, + extractHttpResponse: response => response.SdkHttpResponse, + updateConfigPageToken: (cfg, token) => { + cfg.PageToken = token; + return cfg; + }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); + } + + /// + /// Creates a batch job. + /// + /// The model to use for the batch job. + /// The of the batch job. + /// Currently Vertex AI supports GCS URIs or BigQuery URI. Example: "gs://path/to/input/data" or + /// "bq://projectId.bqDatasetId.bqTableId". Gemini Developer API supports List of + /// inlined_request, or file name. Example: "files/file_name". Optional to configure the batch + /// job. The cancellation token for the request. + /// A that represents the asynchronous operation. The task + /// result contains a instance with batch job details. + public async Task CreateAsync(string model, BatchJobSource src, + CreateBatchJobConfig config, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + if (src.InlinedRequests != null) { + throw new NotSupportedException("inlinedRequests is not supported for Vertex AI."); + } + if (src.FileName != null) { + throw new NotSupportedException("fileName is not supported for Vertex AI."); + } + if (src.GcsUri != null && src.BigqueryUri != null) { + throw new ArgumentException("Only one of gcsUri and bigqueryUri can be set."); + } + if (src.GcsUri == null && src.BigqueryUri == null) { + throw new ArgumentException("One of gcsUri and bigqueryUri must be set."); + } + } else { + if (src.FileName != null && src.InlinedRequests != null) { + throw new ArgumentException("Only one of fileName and InlinedRequests can be set."); + } + if (src.FileName == null && src.InlinedRequests == null) { + throw new ArgumentException("one of fileName and InlinedRequests must be set."); + } + } + return await this.PrivateCreateAsync(model, src, config, cancellationToken); + } + + /// + /// Makes an API request to create the batch embeddings job. This method is experimental. + /// + /// The model to use for the embeddings. + /// The of the batch job. + /// Gemini Developer API supports List of inlined_request, or file name. Example: + /// "files/file_name". Optional to configure the batch job. The cancellation token for the request. A that represents the asynchronous operation. The task result contains + /// a instance with batch job details. + public async Task CreateEmbeddingsAsync( + string model, EmbeddingsBatchJobSource src, CreateEmbeddingsBatchJobConfig config, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException("Vertex AI does not support batches.createEmbeddings."); + } + return await this.PrivateCreateEmbeddingsAsync(model, src, config, cancellationToken); + } + } +} diff --git a/Google.GenAI/Caches.cs b/Google.GenAI/Caches.cs index 0a5f17a8..14c6aab8 100644 --- a/Google.GenAI/Caches.cs +++ b/Google.GenAI/Caches.cs @@ -1,1433 +1,1433 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - - public sealed class Caches { - private readonly ApiClient _apiClient; - - internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { - Common.SetValueByPath(toObject, new string[] { "apiKey" }, - Common.GetValueByPath(fromObject, new string[] { "apiKey" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { - throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { - throw new NotSupportedException("authType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { - throw new NotSupportedException( - "googleServiceAccountConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { - throw new NotSupportedException( - "httpBasicAuthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { - throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { - throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { - Common.SetValueByPath(toObject, new string[] { "data" }, - Common.GetValueByPath(fromObject, new string[] { "data" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode ContentToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode CreateCachedContentConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ToolToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, - ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "toolConfig" })), - toObject)); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }))) { - throw new NotSupportedException("kmsKeyName parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode CreateCachedContentConfigToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ToolToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, - ToolConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "toolConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "encryption_spec", "kmsKeyName" }, - Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" })); - } - - return toObject; - } - - internal JsonNode CreateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "model" }, - Transformers.TCachesModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateCachedContentConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode CreateCachedContentParametersToVertex(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "model" }, - Transformers.TCachesModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateCachedContentConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentParametersToVertex(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentResponseFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentResponseFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileUri" }, - Common.GetValueByPath(fromObject, new string[] { "fileUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { - Common.SetValueByPath(toObject, new string[] { "id" }, - Common.GetValueByPath(fromObject, new string[] { "id" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { - Common.SetValueByPath(toObject, new string[] { "args" }, - Common.GetValueByPath(fromObject, new string[] { "args" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { - throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { - throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "allowedFunctionNames" }, - Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { - throw new NotSupportedException( - "streamFunctionCallArguments parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode FunctionDeclarationToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { - Common.SetValueByPath(toObject, new string[] { "parameters" }, - Common.GetValueByPath(fromObject, new string[] { "parameters" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parametersJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath(toObject, new string[] { "response" }, - Common.GetValueByPath(fromObject, new string[] { "response" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "behavior" }))) { - throw new NotSupportedException("behavior parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode GetCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode GetCachedContentParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "authConfig" }, - AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "authConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { - Common.SetValueByPath(toObject, new string[] { "enableWidget" }, - Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { - Common.SetValueByPath(toObject, new string[] { "searchTypes" }, - Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { - throw new NotSupportedException( - "blockingConfidence parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { - throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "timeRangeFilter" }, - Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsConfigToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsParametersToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListCachedContentsConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListCachedContentsParametersToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListCachedContentsConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListCachedContentsResponseFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { - Common.SetValueByPath(toObject, new string[] { "cachedContents" }, - Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsResponseFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { - Common.SetValueByPath(toObject, new string[] { "cachedContents" }, - Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); - } - - return toObject; - } - - internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileData" }, - FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "fileData" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCall" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "inlineData" }, - BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "inlineData" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolCall" }, - Common.GetValueByPath(fromObject, new string[] { "toolCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolResponse" }, - Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "partMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); - } - - return toObject; - } - - internal JsonNode PartToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileData" }, - Common.GetValueByPath(fromObject, new string[] { "fileData" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - Common.GetValueByPath(fromObject, new string[] { "functionCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "inlineData" }, - Common.GetValueByPath(fromObject, new string[] { "inlineData" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolCall" }))) { - throw new NotSupportedException("toolCall parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolResponse" }))) { - throw new NotSupportedException("toolResponse parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partMetadata" }))) { - throw new NotSupportedException("partMetadata parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "retrievalConfig" }, - Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionCallingConfig" }, - FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCallingConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "includeServerSideToolInvocations" }, - Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); - } - - return toObject; - } - - internal JsonNode ToolConfigToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "retrievalConfig" }, - Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionCallingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" })); - } - - if (!Common.IsZero(Common.GetValueByPath( - fromObject, new string[] { "includeServerSideToolInvocations" }))) { - throw new NotSupportedException( - "includeServerSideToolInvocations parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { - throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "computerUse" }, - Common.GetValueByPath(fromObject, new string[] { "computerUse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileSearch" }, - Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearch" }, - GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleSearch" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleMaps" }, - GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleMaps" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { - throw new NotSupportedException( - "enterpriseWebSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionDeclarations" }, - Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearchRetrieval" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { - throw new NotSupportedException( - "parallelAiSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath(toObject, new string[] { "urlContext" }, - Common.GetValueByPath(fromObject, new string[] { "urlContext" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mcpServers" }, - Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); - } - - return toObject; - } - - internal JsonNode ToolToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrieval" }) != null) { - Common.SetValueByPath(toObject, new string[] { "retrieval" }, - Common.GetValueByPath(fromObject, new string[] { "retrieval" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "computerUse" }, - Common.GetValueByPath(fromObject, new string[] { "computerUse" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileSearch" }))) { - throw new NotSupportedException("fileSearch parameter is not supported in Vertex AI."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearch" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleMaps" }, - Common.GetValueByPath(fromObject, new string[] { "googleMaps" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "enterpriseWebSearch" }, - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(FunctionDeclarationToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearchRetrieval" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parallelAiSearch" }, - Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath(toObject, new string[] { "urlContext" }, - Common.GetValueByPath(fromObject, new string[] { "urlContext" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mcpServers" }))) { - throw new NotSupportedException("mcpServers parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentConfigToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = UpdateCachedContentConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentParametersToVertex(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = UpdateCachedContentConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - public Caches(ApiClient apiClient) { - _apiClient = apiClient; - } - - public async Task CreateAsync(string model, - CreateCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - CreateCachedContentParameters parameter = new CreateCachedContentParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse CreateCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = - CreateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } else { - body = - CreateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - public async Task GetAsync(string name, GetCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - GetCachedContentParameters parameter = new GetCachedContentParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GetCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = GetCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - public async Task DeleteAsync( - string name, DeleteCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - DeleteCachedContentParameters parameter = new DeleteCachedContentParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse DeleteCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = - DeleteCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = - DeleteCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = DeleteCachedContentResponseFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = DeleteCachedContentResponseFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task UpdateAsync(string name, - UpdateCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - UpdateCachedContentParameters parameter = new UpdateCachedContentParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse UpdateCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = - UpdateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = - UpdateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateListAsync( - ListCachedContentsConfig? config, CancellationToken cancellationToken = default) { - ListCachedContentsParameters parameter = new ListCachedContentsParameters(); - - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse ListCachedContentsParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = ListCachedContentsParametersToVertex(parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } else { - body = ListCachedContentsParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = ListCachedContentsResponseFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = ListCachedContentsResponseFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Lists cached contents asynchronously. - /// - /// A instance that specifies the - /// optional configuration for the list request. The - /// for the request. A Pager object that - /// contains one page of cached contents. When iterating over the pager, it automatically - /// fetches the next page if there are more. - - public async Task> - ListAsync(ListCachedContentsConfig? config = null, - CancellationToken cancellationToken = default) { - config ??= new ListCachedContentsConfig(); - var initialResponse = await PrivateListAsync(config, cancellationToken); - - return new Pager( - requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), - extractItems: response => response.CachedContents, - extractNextPageToken: response => response.NextPageToken, - extractHttpResponse: response => response.SdkHttpResponse, - updateConfigPageToken: (cfg, token) => { - cfg.PageToken = token; - return cfg; - }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + + public sealed class Caches { + private readonly ApiClient _apiClient; + + internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { + Common.SetValueByPath(toObject, new string[] { "apiKey" }, + Common.GetValueByPath(fromObject, new string[] { "apiKey" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { + throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { + throw new NotSupportedException("authType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { + throw new NotSupportedException( + "googleServiceAccountConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { + throw new NotSupportedException( + "httpBasicAuthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { + throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { + throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { + Common.SetValueByPath(toObject, new string[] { "data" }, + Common.GetValueByPath(fromObject, new string[] { "data" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode ContentToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode CreateCachedContentConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ToolToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, + ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "toolConfig" })), + toObject)); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }))) { + throw new NotSupportedException("kmsKeyName parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode CreateCachedContentConfigToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ToolToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, + ToolConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "toolConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "encryption_spec", "kmsKeyName" }, + Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" })); + } + + return toObject; + } + + internal JsonNode CreateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "model" }, + Transformers.TCachesModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateCachedContentConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode CreateCachedContentParametersToVertex(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "model" }, + Transformers.TCachesModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateCachedContentConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentParametersToVertex(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentResponseFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentResponseFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileUri" }, + Common.GetValueByPath(fromObject, new string[] { "fileUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { + Common.SetValueByPath(toObject, new string[] { "id" }, + Common.GetValueByPath(fromObject, new string[] { "id" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { + Common.SetValueByPath(toObject, new string[] { "args" }, + Common.GetValueByPath(fromObject, new string[] { "args" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { + throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { + throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "allowedFunctionNames" }, + Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { + throw new NotSupportedException( + "streamFunctionCallArguments parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode FunctionDeclarationToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { + Common.SetValueByPath(toObject, new string[] { "parameters" }, + Common.GetValueByPath(fromObject, new string[] { "parameters" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parametersJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath(toObject, new string[] { "response" }, + Common.GetValueByPath(fromObject, new string[] { "response" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "behavior" }))) { + throw new NotSupportedException("behavior parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode GetCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode GetCachedContentParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "authConfig" }, + AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "authConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { + Common.SetValueByPath(toObject, new string[] { "enableWidget" }, + Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { + Common.SetValueByPath(toObject, new string[] { "searchTypes" }, + Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { + throw new NotSupportedException( + "blockingConfidence parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { + throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "timeRangeFilter" }, + Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsConfigToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsParametersToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListCachedContentsConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListCachedContentsParametersToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListCachedContentsConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListCachedContentsResponseFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { + Common.SetValueByPath(toObject, new string[] { "cachedContents" }, + Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsResponseFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { + Common.SetValueByPath(toObject, new string[] { "cachedContents" }, + Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); + } + + return toObject; + } + + internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileData" }, + FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "fileData" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCall" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "inlineData" }, + BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "inlineData" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolCall" }, + Common.GetValueByPath(fromObject, new string[] { "toolCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolResponse" }, + Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "partMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); + } + + return toObject; + } + + internal JsonNode PartToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileData" }, + Common.GetValueByPath(fromObject, new string[] { "fileData" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + Common.GetValueByPath(fromObject, new string[] { "functionCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "inlineData" }, + Common.GetValueByPath(fromObject, new string[] { "inlineData" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolCall" }))) { + throw new NotSupportedException("toolCall parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolResponse" }))) { + throw new NotSupportedException("toolResponse parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partMetadata" }))) { + throw new NotSupportedException("partMetadata parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "retrievalConfig" }, + Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionCallingConfig" }, + FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCallingConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "includeServerSideToolInvocations" }, + Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); + } + + return toObject; + } + + internal JsonNode ToolConfigToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "retrievalConfig" }, + Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionCallingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" })); + } + + if (!Common.IsZero(Common.GetValueByPath( + fromObject, new string[] { "includeServerSideToolInvocations" }))) { + throw new NotSupportedException( + "includeServerSideToolInvocations parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { + throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "computerUse" }, + Common.GetValueByPath(fromObject, new string[] { "computerUse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileSearch" }, + Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearch" }, + GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleSearch" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleMaps" }, + GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleMaps" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { + throw new NotSupportedException( + "enterpriseWebSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionDeclarations" }, + Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearchRetrieval" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { + throw new NotSupportedException( + "parallelAiSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath(toObject, new string[] { "urlContext" }, + Common.GetValueByPath(fromObject, new string[] { "urlContext" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mcpServers" }, + Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); + } + + return toObject; + } + + internal JsonNode ToolToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrieval" }) != null) { + Common.SetValueByPath(toObject, new string[] { "retrieval" }, + Common.GetValueByPath(fromObject, new string[] { "retrieval" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "computerUse" }, + Common.GetValueByPath(fromObject, new string[] { "computerUse" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileSearch" }))) { + throw new NotSupportedException("fileSearch parameter is not supported in Vertex AI."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearch" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleMaps" }, + Common.GetValueByPath(fromObject, new string[] { "googleMaps" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "enterpriseWebSearch" }, + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(FunctionDeclarationToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearchRetrieval" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parallelAiSearch" }, + Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath(toObject, new string[] { "urlContext" }, + Common.GetValueByPath(fromObject, new string[] { "urlContext" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mcpServers" }))) { + throw new NotSupportedException("mcpServers parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentConfigToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = UpdateCachedContentConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentParametersToVertex(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = UpdateCachedContentConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + public Caches(ApiClient apiClient) { + _apiClient = apiClient; + } + + public async Task CreateAsync(string model, + CreateCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + CreateCachedContentParameters parameter = new CreateCachedContentParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse CreateCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = + CreateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } else { + body = + CreateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + public async Task GetAsync(string name, GetCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + GetCachedContentParameters parameter = new GetCachedContentParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GetCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = GetCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + public async Task DeleteAsync( + string name, DeleteCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + DeleteCachedContentParameters parameter = new DeleteCachedContentParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse DeleteCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = + DeleteCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = + DeleteCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = DeleteCachedContentResponseFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = DeleteCachedContentResponseFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task UpdateAsync(string name, + UpdateCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + UpdateCachedContentParameters parameter = new UpdateCachedContentParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse UpdateCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = + UpdateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = + UpdateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateListAsync( + ListCachedContentsConfig? config, CancellationToken cancellationToken = default) { + ListCachedContentsParameters parameter = new ListCachedContentsParameters(); + + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse ListCachedContentsParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = ListCachedContentsParametersToVertex(parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } else { + body = ListCachedContentsParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = ListCachedContentsResponseFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = ListCachedContentsResponseFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Lists cached contents asynchronously. + /// + /// A instance that specifies the + /// optional configuration for the list request. The + /// for the request. A Pager object that + /// contains one page of cached contents. When iterating over the pager, it automatically + /// fetches the next page if there are more. + + public async Task> + ListAsync(ListCachedContentsConfig? config = null, + CancellationToken cancellationToken = default) { + config ??= new ListCachedContentsConfig(); + var initialResponse = await PrivateListAsync(config, cancellationToken); + + return new Pager( + requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), + extractItems: response => response.CachedContents, + extractNextPageToken: response => response.NextPageToken, + extractHttpResponse: response => response.SdkHttpResponse, + updateConfigPageToken: (cfg, token) => { + cfg.PageToken = token; + return cfg; + }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); + } + } +} diff --git a/Google.GenAI/Common.cs b/Google.GenAI/Common.cs index 78604dbf..c4ead0ea 100644 --- a/Google.GenAI/Common.cs +++ b/Google.GenAI/Common.cs @@ -1,524 +1,524 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace Google.GenAI -{ - - /// - /// Common utility methods for the GenAI SDK to work with JSON. - /// - // TODO(b/413510963): make this method internal to only be used in converters. - public static class Common - { - /// - /// Sets the value of an object by a path. - /// - /// Common.SetValueByPath(containerJsonObject, new string[]{"secondMember", "childMember"}, 42); - /// - // TODO(b/413510963): make this method internal to only be used in converters. - public static void SetValueByPath(JsonObject jsonObject, string[] path, object value) - { - if (path == null || path.Length == 0) - { - throw new ArgumentException("Path cannot be empty."); - } - if (jsonObject == null) - { - throw new ArgumentException("JsonObject cannot be null."); - } - - JsonObject currentObject = jsonObject; - for (int i = 0; i < path.Length - 1; i++) - { - string key = path[i]; - - if (key.EndsWith("[]")) - { - string keyName = key.Substring(0, key.Length - 2); - if (!currentObject.ContainsKey(keyName)) - { - currentObject[keyName] = new JsonArray(); - } - JsonArray arrayNode = (JsonArray)currentObject[keyName]; - if (value is System.Collections.IList listValue) - { - if (arrayNode.Count != listValue.Count) - { - arrayNode.Clear(); - for (int j = 0; j < listValue.Count; j++) - { - arrayNode.Add(new JsonObject()); - } - } - for (int j = 0; j < arrayNode.Count; j++) - { - SetValueByPath( - (JsonObject)arrayNode[j], - path.Skip(i + 1).ToArray(), - listValue[j]); - } - } - else - { - if (arrayNode.Count == 0) - { - arrayNode.Add(new JsonObject()); - } - for (int j = 0; j < arrayNode.Count; j++) - { - SetValueByPath( - (JsonObject)arrayNode[j], path.Skip(i + 1).ToArray(), value); - } - } - return; - } - else if (key.EndsWith("[0]")) - { - string keyName = key.Substring(0, key.Length - 3); - if (!currentObject.ContainsKey(keyName)) - { - currentObject[keyName] = new JsonArray(new[] { (JsonNode)new JsonObject() }); - } - currentObject = (JsonObject)((JsonArray)currentObject[keyName])[0]; - } - else - { - if (!currentObject.ContainsKey(key)) - { - currentObject[key] = new JsonObject(); - } - currentObject = (JsonObject)currentObject[key]; - } - } - - string finalKey = path[path.Length - 1]; - if (finalKey.Equals("_self") && value is JsonObject selfNode) - { - foreach (var property in selfNode.ToList()) - { - currentObject[property.Key] = property.Value == null ? null : property.Value.DeepClone(); - } - return; - } - JsonNode? newNode = ToJsonNode(value); - - if (currentObject.ContainsKey(finalKey) && currentObject[finalKey] is JsonObject existingObject && newNode is JsonObject newObject) - { - foreach (KeyValuePair property in newObject) - { - existingObject[property.Key] = property.Value == null ? null : property.Value.DeepClone(); - } - } - else if(currentObject.ContainsKey(finalKey) && IsZero(value)) - { - return; - } - else - { - currentObject[finalKey] = newNode; - } - } - - /// - /// Gets the value of an object by a path. - /// - /// Common.GetValueByPath(containerJsonNode, new string[]{"secondMember", "childMember"}) - /// - // TODO(b/413510963): make this method internal to only be used in converters. - public static JsonNode? GetValueByPath(JsonNode obj, string[] keys) - { - if (obj == null || keys == null) - { - return null; - } - if (keys.Length == 1 && keys[0].Equals("_self")) - { - return obj; - } - - JsonNode? currentObject = obj; - for (int i = 0; i < keys.Length; i++) - { - string key = keys[i]; - - if (currentObject == null) - { - return null; - } - - if (key.EndsWith("[]")) - { - string keyName = key.Substring(0, key.Length - 2); - if (currentObject is JsonObject objNode - && objNode.ContainsKey(keyName) - && objNode[keyName] is JsonArray arrayNode) - { - if (keys.Length - 1 == i) - { - return arrayNode; - } - JsonArray result = new JsonArray(); - foreach (JsonNode? element in arrayNode) - { - JsonNode? node = - GetValueByPath(element, keys.Skip(i + 1).ToArray()); - if (node != null) - { - result.Add(node.DeepClone()); - } - } - return result; - } - else - { - return null; - } - } - else if (key.EndsWith("[0]")) - { - string keyName = key.Substring(0, key.Length - 3); - if (currentObject is JsonObject objNode - && objNode.ContainsKey(keyName) - && objNode[keyName] is JsonArray arrayNode - && arrayNode.Count > 0) - { - currentObject = arrayNode[0]; - } - else - { - return null; - } - } - else - { - if (currentObject is JsonObject objNode && objNode.ContainsKey(key)) - { - currentObject = objNode[key]; - } - else - { - return null; - } - } - } - - return currentObject; - } - - /// - /// Moves values from source paths to destination paths. - /// Example: MoveValueByPath( {'requests': [{'content': v1}, {'content': v2}]}, {'requests[].*': 'requests[].request.*'} ) -> {'requests': [{'request': {'content': v1}}, {'request': {'content': v2}}]} - /// - public static void MoveValueByPath(JsonNode data, IDictionary paths) - { - if (data == null || paths == null) - { - return; - } - - foreach (KeyValuePair entry in paths) - { - string sourcePath = entry.Key; - string destPath = entry.Value; - - string[] sourceKeys = sourcePath.Split('.'); - string[] destKeys = destPath.Split('.'); - - HashSet excludeKeys = new HashSet(); - int wildcardIdx = -1; - - for (int i = 0; i < sourceKeys.Length; i++) - { - if (sourceKeys[i].Equals("*")) - { - wildcardIdx = i; - break; - } - } - - if (wildcardIdx != -1 && destKeys.Length > wildcardIdx) - { - // Extract the intermediate key between source and dest paths - // Example: source=['requests[]', '*'], dest=['requests[]', 'request', '*'] - // We want to exclude 'request' - for (int i = wildcardIdx; i < destKeys.Length; i++) - { - string key = destKeys[i]; - if (!key.Equals("*") && !key.EndsWith("[]") && !key.EndsWith("[0]")) - { - excludeKeys.Add(key); - } - } - } - - MoveValueRecursive(data, sourceKeys, destKeys, 0, excludeKeys); - } - } - - /// - /// Efficiently converts a value to a JsonNode, using DeepClone() when the value is - /// already a JsonNode to avoid the expensive serialize-to-string-then-parse round-trip - /// that causes OutOfMemoryException with large payloads (e.g. base64 inline image data). - /// - internal static JsonNode? ParseToJsonNode(object? value) - { - if (value == null) - { - return null; - } - if (value is JsonNode node) - { - return node.DeepClone(); - } - return JsonSerializer.SerializeToNode(value); - } - - internal static string FormatQuery(JsonObject queryParams) - { - var queryParts = new List(); - foreach (var param in queryParams) - { - if (param.Value != null) - { - queryParts.Add($"{param.Key}={Uri.EscapeDataString(param.Value.ToString())}"); - } - } - return string.Join("&", queryParts); - } - - internal static string FormatMap(string template, JsonNode? data) - { - if (data is not JsonObject jsonObject) - { - return template; - } - - foreach (var field in jsonObject) - { - string key = field.Key; - string placeholder = "{" + key + "}"; - if (template.Contains(placeholder)) - { - template = template.Replace(placeholder, field.Value?.GetValue() ?? string.Empty); - } - } - return template; - } - - /// - /// Converts a JsonObject into a URL-encoded query string. - /// - /// The JsonObject containing the parameters to encode. - /// A URL-encoded string (e.g., "key1=value1&key2=value2"). - internal static string UrlEncode(JsonObject? paramsNode) - { - if (paramsNode == null || paramsNode.Count == 0) - { - return string.Empty; - } - - var queryParts = new List(); - - foreach (var field in paramsNode) - { - string encodedKey = Uri.EscapeDataString(field.Key); - var valueNode = field.Value; - - if (valueNode == null) - { - queryParts.Add($"{encodedKey}="); - } - else - { - string valueStr = valueNode.GetValueKind() == JsonValueKind.String - ? valueNode.GetValue() - : valueNode.ToJsonString().Trim('"'); - // In Python (and replay files), "*" is encoded as "%2A" although it is not required. - // So we keep the same behavior here. - string encodedValue = Uri.EscapeDataString(valueStr).Replace("*", "%2A"); - queryParts.Add($"{encodedKey}={encodedValue}"); - } - } - - return string.Join("&", queryParts); - } - - internal static bool IsZero(object? obj) - { - if (obj == null) - { - return true; - } - - if (obj is int i) - { - return i == 0; - } - else if (obj is long l) - { - return l == 0L; - } - else if (obj is float f) - { - return f == 0.0f; - } - else if (obj is double d) - { - return d == 0.0; - } - else if (obj is char ch) - { - return ch == '\0'; - } - else if (obj is bool b) - { - return !b; - } - else if (obj is System.Collections.ICollection c) - { - return c.Count == 0; - } - else if (obj is JsonArray a) - { - return a.Count == 0; - } - else if (obj is JsonObject jo) - { - return jo.Count == 0; - } - - return false; - } - - private static JsonNode? ToJsonNode(object value) - { - // TODO: evaluate using System.Text.Json to handle conversion of object to JSON. - switch (value) - { - case null: - return null; - case string s: - return JsonValue.Create(s); - case int i: - return JsonValue.Create(i); - case long l: - return JsonValue.Create(l); - case double d: - return JsonValue.Create(d); - case bool b: - return JsonValue.Create(b); - case JsonNode node: - return node.DeepClone(); - case System.Collections.IEnumerable enumerable: - JsonArray array = new JsonArray(); - foreach (var item in enumerable) - { - array.Add(ToJsonNode(item)); - } - return array; - default: - return JsonNode.Parse(JsonSerializer.Serialize(value)); - } - } - - private static void MoveValueRecursive( - JsonNode data, - string[] sourceKeys, - string[] destKeys, - int keyIdx, - HashSet excludeKeys) - { - if (keyIdx >= sourceKeys.Length || data == null) - { - return; - } - - string key = sourceKeys[keyIdx]; - - if (key.EndsWith("[]")) - { - string keyName = key.Substring(0, key.Length - 2); - if (data is JsonObject dataObj && dataObj.ContainsKey(keyName) && dataObj[keyName] is JsonArray arrayNode) - { - foreach (JsonNode item in arrayNode) - { - MoveValueRecursive(item, sourceKeys, destKeys, keyIdx + 1, excludeKeys); - } - } - } - else if (key.Equals("*")) - { - if (data is JsonObject objectNode) - { - List keysToMove = new List(); - foreach (var property in objectNode) - { - string fieldName = property.Key; - if (!fieldName.StartsWith("_") && !excludeKeys.Contains(fieldName)) - { - keysToMove.Add(fieldName); - } - } - - Dictionary valuesToMove = new Dictionary(); - foreach (string k in keysToMove) - { - valuesToMove.Add(k, objectNode[k]); - } - - foreach (KeyValuePair valueEntry in valuesToMove) - { - string k = valueEntry.Key; - JsonNode v = valueEntry.Value; - - List newDestKeysList = new List(); - for (int i = keyIdx; i < destKeys.Length; i++) - { - string dk = destKeys[i]; - if (dk.Equals("*")) - { - newDestKeysList.Add(k); - } - else - { - newDestKeysList.Add(dk); - } - } - - string[] newDestKeys = newDestKeysList.ToArray(); - SetValueByPath(objectNode, newDestKeys, v); - } - - foreach (string k in keysToMove) - { - objectNode.Remove(k); - } - } - } - else - { - if (data is JsonObject dataObj && dataObj.ContainsKey(key)) - { - JsonNode nextNode = dataObj[key]; - MoveValueRecursive(nextNode, sourceKeys, destKeys, keyIdx + 1, excludeKeys); - } - } - } - } +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Google.GenAI +{ + + /// + /// Common utility methods for the GenAI SDK to work with JSON. + /// + // TODO(b/413510963): make this method internal to only be used in converters. + public static class Common + { + /// + /// Sets the value of an object by a path. + /// + /// Common.SetValueByPath(containerJsonObject, new string[]{"secondMember", "childMember"}, 42); + /// + // TODO(b/413510963): make this method internal to only be used in converters. + public static void SetValueByPath(JsonObject jsonObject, string[] path, object value) + { + if (path == null || path.Length == 0) + { + throw new ArgumentException("Path cannot be empty."); + } + if (jsonObject == null) + { + throw new ArgumentException("JsonObject cannot be null."); + } + + JsonObject currentObject = jsonObject; + for (int i = 0; i < path.Length - 1; i++) + { + string key = path[i]; + + if (key.EndsWith("[]")) + { + string keyName = key.Substring(0, key.Length - 2); + if (!currentObject.ContainsKey(keyName)) + { + currentObject[keyName] = new JsonArray(); + } + JsonArray arrayNode = (JsonArray)currentObject[keyName]; + if (value is System.Collections.IList listValue) + { + if (arrayNode.Count != listValue.Count) + { + arrayNode.Clear(); + for (int j = 0; j < listValue.Count; j++) + { + arrayNode.Add(new JsonObject()); + } + } + for (int j = 0; j < arrayNode.Count; j++) + { + SetValueByPath( + (JsonObject)arrayNode[j], + path.Skip(i + 1).ToArray(), + listValue[j]); + } + } + else + { + if (arrayNode.Count == 0) + { + arrayNode.Add(new JsonObject()); + } + for (int j = 0; j < arrayNode.Count; j++) + { + SetValueByPath( + (JsonObject)arrayNode[j], path.Skip(i + 1).ToArray(), value); + } + } + return; + } + else if (key.EndsWith("[0]")) + { + string keyName = key.Substring(0, key.Length - 3); + if (!currentObject.ContainsKey(keyName)) + { + currentObject[keyName] = new JsonArray(new[] { (JsonNode)new JsonObject() }); + } + currentObject = (JsonObject)((JsonArray)currentObject[keyName])[0]; + } + else + { + if (!currentObject.ContainsKey(key)) + { + currentObject[key] = new JsonObject(); + } + currentObject = (JsonObject)currentObject[key]; + } + } + + string finalKey = path[path.Length - 1]; + if (finalKey.Equals("_self") && value is JsonObject selfNode) + { + foreach (var property in selfNode.ToList()) + { + currentObject[property.Key] = property.Value == null ? null : property.Value.DeepClone(); + } + return; + } + JsonNode? newNode = ToJsonNode(value); + + if (currentObject.ContainsKey(finalKey) && currentObject[finalKey] is JsonObject existingObject && newNode is JsonObject newObject) + { + foreach (KeyValuePair property in newObject) + { + existingObject[property.Key] = property.Value == null ? null : property.Value.DeepClone(); + } + } + else if(currentObject.ContainsKey(finalKey) && IsZero(value)) + { + return; + } + else + { + currentObject[finalKey] = newNode; + } + } + + /// + /// Gets the value of an object by a path. + /// + /// Common.GetValueByPath(containerJsonNode, new string[]{"secondMember", "childMember"}) + /// + // TODO(b/413510963): make this method internal to only be used in converters. + public static JsonNode? GetValueByPath(JsonNode obj, string[] keys) + { + if (obj == null || keys == null) + { + return null; + } + if (keys.Length == 1 && keys[0].Equals("_self")) + { + return obj; + } + + JsonNode? currentObject = obj; + for (int i = 0; i < keys.Length; i++) + { + string key = keys[i]; + + if (currentObject == null) + { + return null; + } + + if (key.EndsWith("[]")) + { + string keyName = key.Substring(0, key.Length - 2); + if (currentObject is JsonObject objNode + && objNode.ContainsKey(keyName) + && objNode[keyName] is JsonArray arrayNode) + { + if (keys.Length - 1 == i) + { + return arrayNode; + } + JsonArray result = new JsonArray(); + foreach (JsonNode? element in arrayNode) + { + JsonNode? node = + GetValueByPath(element, keys.Skip(i + 1).ToArray()); + if (node != null) + { + result.Add(node.DeepClone()); + } + } + return result; + } + else + { + return null; + } + } + else if (key.EndsWith("[0]")) + { + string keyName = key.Substring(0, key.Length - 3); + if (currentObject is JsonObject objNode + && objNode.ContainsKey(keyName) + && objNode[keyName] is JsonArray arrayNode + && arrayNode.Count > 0) + { + currentObject = arrayNode[0]; + } + else + { + return null; + } + } + else + { + if (currentObject is JsonObject objNode && objNode.ContainsKey(key)) + { + currentObject = objNode[key]; + } + else + { + return null; + } + } + } + + return currentObject; + } + + /// + /// Moves values from source paths to destination paths. + /// Example: MoveValueByPath( {'requests': [{'content': v1}, {'content': v2}]}, {'requests[].*': 'requests[].request.*'} ) -> {'requests': [{'request': {'content': v1}}, {'request': {'content': v2}}]} + /// + public static void MoveValueByPath(JsonNode data, IDictionary paths) + { + if (data == null || paths == null) + { + return; + } + + foreach (KeyValuePair entry in paths) + { + string sourcePath = entry.Key; + string destPath = entry.Value; + + string[] sourceKeys = sourcePath.Split('.'); + string[] destKeys = destPath.Split('.'); + + HashSet excludeKeys = new HashSet(); + int wildcardIdx = -1; + + for (int i = 0; i < sourceKeys.Length; i++) + { + if (sourceKeys[i].Equals("*")) + { + wildcardIdx = i; + break; + } + } + + if (wildcardIdx != -1 && destKeys.Length > wildcardIdx) + { + // Extract the intermediate key between source and dest paths + // Example: source=['requests[]', '*'], dest=['requests[]', 'request', '*'] + // We want to exclude 'request' + for (int i = wildcardIdx; i < destKeys.Length; i++) + { + string key = destKeys[i]; + if (!key.Equals("*") && !key.EndsWith("[]") && !key.EndsWith("[0]")) + { + excludeKeys.Add(key); + } + } + } + + MoveValueRecursive(data, sourceKeys, destKeys, 0, excludeKeys); + } + } + + /// + /// Efficiently converts a value to a JsonNode, using DeepClone() when the value is + /// already a JsonNode to avoid the expensive serialize-to-string-then-parse round-trip + /// that causes OutOfMemoryException with large payloads (e.g. base64 inline image data). + /// + internal static JsonNode? ParseToJsonNode(object? value) + { + if (value == null) + { + return null; + } + if (value is JsonNode node) + { + return node.DeepClone(); + } + return JsonSerializer.SerializeToNode(value, JsonConfig.InternalSerializerOptions); + } + + internal static string FormatQuery(JsonObject queryParams) + { + var queryParts = new List(); + foreach (var param in queryParams) + { + if (param.Value != null) + { + queryParts.Add($"{param.Key}={Uri.EscapeDataString(param.Value.ToString())}"); + } + } + return string.Join("&", queryParts); + } + + internal static string FormatMap(string template, JsonNode? data) + { + if (data is not JsonObject jsonObject) + { + return template; + } + + foreach (var field in jsonObject) + { + string key = field.Key; + string placeholder = "{" + key + "}"; + if (template.Contains(placeholder)) + { + template = template.Replace(placeholder, field.Value?.GetValue() ?? string.Empty); + } + } + return template; + } + + /// + /// Converts a JsonObject into a URL-encoded query string. + /// + /// The JsonObject containing the parameters to encode. + /// A URL-encoded string (e.g., "key1=value1&key2=value2"). + internal static string UrlEncode(JsonObject? paramsNode) + { + if (paramsNode == null || paramsNode.Count == 0) + { + return string.Empty; + } + + var queryParts = new List(); + + foreach (var field in paramsNode) + { + string encodedKey = Uri.EscapeDataString(field.Key); + var valueNode = field.Value; + + if (valueNode == null) + { + queryParts.Add($"{encodedKey}="); + } + else + { + string valueStr = valueNode.GetValueKind() == JsonValueKind.String + ? valueNode.GetValue() + : valueNode.ToJsonString().Trim('"'); + // In Python (and replay files), "*" is encoded as "%2A" although it is not required. + // So we keep the same behavior here. + string encodedValue = Uri.EscapeDataString(valueStr).Replace("*", "%2A"); + queryParts.Add($"{encodedKey}={encodedValue}"); + } + } + + return string.Join("&", queryParts); + } + + internal static bool IsZero(object? obj) + { + if (obj == null) + { + return true; + } + + if (obj is int i) + { + return i == 0; + } + else if (obj is long l) + { + return l == 0L; + } + else if (obj is float f) + { + return f == 0.0f; + } + else if (obj is double d) + { + return d == 0.0; + } + else if (obj is char ch) + { + return ch == '\0'; + } + else if (obj is bool b) + { + return !b; + } + else if (obj is System.Collections.ICollection c) + { + return c.Count == 0; + } + else if (obj is JsonArray a) + { + return a.Count == 0; + } + else if (obj is JsonObject jo) + { + return jo.Count == 0; + } + + return false; + } + + private static JsonNode? ToJsonNode(object value) + { + // TODO: evaluate using System.Text.Json to handle conversion of object to JSON. + switch (value) + { + case null: + return null; + case string s: + return JsonValue.Create(s); + case int i: + return JsonValue.Create(i); + case long l: + return JsonValue.Create(l); + case double d: + return JsonValue.Create(d); + case bool b: + return JsonValue.Create(b); + case JsonNode node: + return node.DeepClone(); + case System.Collections.IEnumerable enumerable: + JsonArray array = new JsonArray(); + foreach (var item in enumerable) + { + array.Add(ToJsonNode(item)); + } + return array; + default: + return JsonNode.Parse(JsonSerializer.Serialize(value, JsonConfig.InternalSerializerOptions)); + } + } + + private static void MoveValueRecursive( + JsonNode data, + string[] sourceKeys, + string[] destKeys, + int keyIdx, + HashSet excludeKeys) + { + if (keyIdx >= sourceKeys.Length || data == null) + { + return; + } + + string key = sourceKeys[keyIdx]; + + if (key.EndsWith("[]")) + { + string keyName = key.Substring(0, key.Length - 2); + if (data is JsonObject dataObj && dataObj.ContainsKey(keyName) && dataObj[keyName] is JsonArray arrayNode) + { + foreach (JsonNode item in arrayNode) + { + MoveValueRecursive(item, sourceKeys, destKeys, keyIdx + 1, excludeKeys); + } + } + } + else if (key.Equals("*")) + { + if (data is JsonObject objectNode) + { + List keysToMove = new List(); + foreach (var property in objectNode) + { + string fieldName = property.Key; + if (!fieldName.StartsWith("_") && !excludeKeys.Contains(fieldName)) + { + keysToMove.Add(fieldName); + } + } + + Dictionary valuesToMove = new Dictionary(); + foreach (string k in keysToMove) + { + valuesToMove.Add(k, objectNode[k]); + } + + foreach (KeyValuePair valueEntry in valuesToMove) + { + string k = valueEntry.Key; + JsonNode v = valueEntry.Value; + + List newDestKeysList = new List(); + for (int i = keyIdx; i < destKeys.Length; i++) + { + string dk = destKeys[i]; + if (dk.Equals("*")) + { + newDestKeysList.Add(k); + } + else + { + newDestKeysList.Add(dk); + } + } + + string[] newDestKeys = newDestKeysList.ToArray(); + SetValueByPath(objectNode, newDestKeys, v); + } + + foreach (string k in keysToMove) + { + objectNode.Remove(k); + } + } + } + else + { + if (data is JsonObject dataObj && dataObj.ContainsKey(key)) + { + JsonNode nextNode = dataObj[key]; + MoveValueRecursive(nextNode, sourceKeys, destKeys, keyIdx + 1, excludeKeys); + } + } + } + } } \ No newline at end of file diff --git a/Google.GenAI/Files.cs b/Google.GenAI/Files.cs index fc716c2a..a04369d3 100644 --- a/Google.GenAI/Files.cs +++ b/Google.GenAI/Files.cs @@ -1,915 +1,915 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -using System.IO; -using System.Collections.Generic; -using Google.Apis.Auth.OAuth2; - -namespace Google.GenAI { - - public sealed class Files { - private readonly UploadClient _uploadClient; - private readonly ApiClient _apiClient; - - internal JsonNode CreateFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "file" }) != null) { - Common.SetValueByPath(toObject, new string[] { "file" }, - Common.GetValueByPath(fromObject, new string[] { "file" })); - } - - return toObject; - } - - internal JsonNode CreateFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode DeleteFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "file" }, - Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode GetFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "file" }, - Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode InternalRegisterFilesParametersToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "uris" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uris" }, - Common.GetValueByPath(fromObject, new string[] { "uris" })); - } - - return toObject; - } - - internal JsonNode ListFilesConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - return toObject; - } - - internal JsonNode ListFilesParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListFilesConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { - Common.SetValueByPath(toObject, new string[] { "files" }, - Common.GetValueByPath(fromObject, new string[] { "files" })); - } - - return toObject; - } - - internal JsonNode RegisterFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { - Common.SetValueByPath(toObject, new string[] { "files" }, - Common.GetValueByPath(fromObject, new string[] { "files" })); - } - - return toObject; - } - - public Files(ApiClient apiClient) { - { - _apiClient = apiClient; - _uploadClient = new UploadClient(_apiClient); - } - } - - private async Task PrivateListAsync( - ListFilesConfig? config, CancellationToken cancellationToken = default) { - ListFilesParameters parameter = new ListFilesParameters(); - - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse ListFilesParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = ListFilesParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateCreateAsync( - Google.GenAI.Types.File file, CreateFileConfig? config, - CancellationToken cancellationToken = default) { - CreateFileParameters parameter = new CreateFileParameters(); - - if (!Common.IsZero(file)) { - parameter.File = file; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse CreateFileParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = CreateFileParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("upload/v1beta/files", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - - if (config?.ShouldReturnHttpResponse == true) { - var httpHeaders = response.GetHeaders(); - Dictionary? headers = null; - - if (httpHeaders != null) { - headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var header in httpHeaders) { - headers[header.Key] = string.Join(", ", header.Value); - } - } - - return new CreateFileResponse { - SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } - }; - } - - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - public async Task GetAsync( - string name, GetFileConfig? config = null, CancellationToken cancellationToken = default) { - GetFileParameters parameter = new GetFileParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetFileParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = GetFileParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files/{file}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task DeleteAsync( - string name, DeleteFileConfig? config = null, - CancellationToken cancellationToken = default) { - DeleteFileParameters parameter = new DeleteFileParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse DeleteFileParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = DeleteFileParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files/{file}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateRegisterFilesAsync( - List uris, RegisterFilesConfig? config, - CancellationToken cancellationToken = default) { - InternalRegisterFilesParameters parameter = new InternalRegisterFilesParameters(); - - if (!Common.IsZero(uris)) { - parameter.Uris = uris; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse InternalRegisterFilesParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = InternalRegisterFilesParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files:register", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - - if (config?.ShouldReturnHttpResponse == true) { - var httpHeaders = response.GetHeaders(); - Dictionary? headers = null; - - if (httpHeaders != null) { - headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var header in httpHeaders) { - headers[header.Key] = string.Join(", ", header.Value); - } - } - - return new RegisterFilesResponse { - SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } - }; - } - - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task> ListAsync( - ListFilesConfig? config = null, CancellationToken cancellationToken = default) { - config ??= new ListFilesConfig(); - var initialResponse = await PrivateListAsync(config, cancellationToken); - - return new Pager( - requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), - extractItems: response => response.Files, - extractNextPageToken: response => response.NextPageToken, - extractHttpResponse: response => response.SdkHttpResponse, - updateConfigPageToken: (cfg, token) => { - cfg.PageToken = token; - return cfg; - }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); - } - - /// - /// Registers Google Cloud Storage files for use with the API. - /// - /// The list of GCS URIs to register. - /// The to use for - /// authorization. A instance - /// that specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains the . - public async Task RegisterFilesAsync( - IEnumerable uris, GoogleCredential credential, RegisterFilesConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (uris == null) - throw new ArgumentNullException(nameof(uris)); - if (credential == null) - throw new ArgumentNullException(nameof(credential)); - - ICredential cred = (ICredential)credential; - string accessToken = - await cred.GetAccessTokenForRequestAsync(cancellationToken: cancellationToken); - if (string.IsNullOrEmpty(accessToken)) { - throw new InvalidOperationException("Failed to obtain access token from credentials."); - } - - var localConfig = config ?? new RegisterFilesConfig(); - var httpOptions = localConfig.HttpOptions ?? new HttpOptions(); - var headers = httpOptions.Headers != null - ? new Dictionary(httpOptions.Headers, - StringComparer.OrdinalIgnoreCase) - : new Dictionary(StringComparer.OrdinalIgnoreCase); - - headers["Authorization"] = $"Bearer {accessToken}"; - if (!string.IsNullOrEmpty(credential.QuotaProject)) { - headers["x-goog-user-project"] = credential.QuotaProject; - } - - // Create an effective configuration with updated headers without mutating the input object. - var effectiveConfig = - localConfig with { HttpOptions = httpOptions with { Headers = headers } }; - - return await PrivateRegisterFilesAsync(uris.ToList(), effectiveConfig, cancellationToken); - } - - /// - /// Uploads a file from a file path. - /// - /// The path to the file to upload. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains the uploaded - /// metadata. - public async Task UploadAsync( - string filePath, UploadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - var fileInfo = new FileInfo(filePath); - using var stream = fileInfo.OpenRead(); - string mimeType = MimeTypes.GetMimeType(Path.GetExtension(filePath)); - - return await UploadAsync(stream, fileInfo.Length, fileInfo.Name, mimeType, config, - cancellationToken); - } - - /// - /// Uploads a file from a byte array. - /// - /// The file content as a byte array. - /// Optional file name to use. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains the uploaded - /// metadata. - public async Task UploadAsync( - byte[] bytes, string? fileName = null, UploadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - using var stream = new MemoryStream(bytes); - return await UploadAsync(stream, bytes.Length, fileName, null, config, cancellationToken); - } - - /// - /// Uploads a file from a stream. - /// - /// The stream containing the file data. - /// The size of the file in bytes. - /// Optional file name to use. - /// Optional MIME type. If not provided, defaults to - /// application/octet-stream. A - /// instance that specifies the optional configurations. A to cancel the operation. - /// A that represents the asynchronous operation. The task - /// result contains the uploaded metadata. - public async Task UploadAsync( - Stream stream, long size, string? fileName = null, string? mimeType = null, - UploadFileConfig? config = null, CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - string uploadUrl = - await CreateFileInApiAsync(config, mimeType, fileName, size, cancellationToken); - HttpContent responseContent = await _uploadClient.UploadAsync( - uploadUrl, stream, size, config?.HttpOptions, cancellationToken); - return await FileFromUploadResponseBodyAsync(responseContent); - } - - /// - /// Downloads a file and returns it as a . Caller is responsible for - /// disposing the returned stream. - /// - /// The name of the file to download (e.g., "files/abc123" or - /// "abc123"). A instance that - /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task result contains a - /// with the file data. - public async Task DownloadAsync(string fileName, DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - string extractedFileName = Transformers.TFileName(fileName); - return await DownloadStreamAsync(extractedFileName, config, cancellationToken); - } - - /// - /// Downloads a object and returns it as a . Caller is - /// responsible for disposing the returned stream. - /// - /// The object to download. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains a with the file - /// data. - public async Task DownloadAsync(Google.GenAI.Types.File file, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(file.Name)) - throw new ArgumentException("File.Name is required", nameof(file)); - - return await DownloadAsync(file.Name, config, cancellationToken); - } - - /// - /// Downloads a object and returns it as a . Caller is - /// responsible for disposing the returned stream. - /// - /// The object to download. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains a with the file - /// data. - public async Task DownloadAsync(Video video, DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(video.Uri)) - throw new ArgumentException("Video.Uri is required", nameof(video)); - - return await DownloadAsync(video.Uri, config, cancellationToken); - } - - /// - /// Downloads a object and returns it as a . - /// Caller is responsible for disposing the returned stream. - /// - /// The object to download. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains a with the file - /// data. - public async Task DownloadAsync(GeneratedVideo generatedVideo, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (generatedVideo.Video == null) - throw new ArgumentException("Video is empty", nameof(generatedVideo)); - - return await DownloadAsync(generatedVideo.Video, config, cancellationToken); - } - - /// - /// Downloads a file directly to a file path. - /// - /// The name of the file to download (e.g., "files/abc123" or - /// "abc123"). The path where the file should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(string fileName, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - using var stream = await DownloadAsync(fileName, config, cancellationToken); - using var fileStream = - new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None, - bufferSize: UploadClient.DEFAULT_CHUNK_SIZE, useAsync: true); - -#if NETSTANDARD2_0 - await stream.CopyToAsync(fileStream, bufferSize: 81920, cancellationToken); -#else - await stream.CopyToAsync(fileStream, cancellationToken); -#endif - } - - /// - /// Downloads a object directly to a file path. - /// - /// The object to download. - /// The path where the file should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(Google.GenAI.Types.File file, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(file.Name)) - throw new ArgumentException("Google.GenAI.Types.File.Name is required", nameof(file)); - - await DownloadToFileAsync(file.Name, outputPath, config, cancellationToken); - } - - /// - /// Downloads a object directly to a file path. - /// - /// The object to download. - /// The path where the Video should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(GeneratedVideo generatedVideo, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (generatedVideo.Video == null) - throw new ArgumentException("Video is empty", nameof(generatedVideo)); - - await DownloadToFileAsync(generatedVideo.Video, outputPath, config, cancellationToken); - } - - /// - /// Downloads a object directly to a file path. - /// - /// The object to download. - /// The path where the Video should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(Video video, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(video.Uri)) - throw new ArgumentException("Video.Uri is required", nameof(video)); - - await DownloadToFileAsync(video.Uri, outputPath, config, cancellationToken); - } - - private async Task FileFromUploadResponseBodyAsync( - HttpContent responseContent) { - string responseString = await responseContent.ReadAsStringAsync(); - JsonNode? responseNode = JsonNode.Parse(responseString); - - if (responseNode?["file"] is not JsonNode fileNode) { - throw new InvalidOperationException("Upload response does not contain file object"); - } - - return JsonSerializer.Deserialize(fileNode.ToString()) ?? - throw new InvalidOperationException("Failed to deserialize File"); - } - - private async Task CreateFileInApiAsync(UploadFileConfig? config, string? mimeType, - string? fileName, long size, - CancellationToken cancellationToken = default) { - var fileBuilder = new Google.GenAI.Types.File(); - - if (config != null) { - if (!string.IsNullOrEmpty(config.Name)) { - fileBuilder.Name = - config.Name.StartsWith("files/") ? config.Name : $"files/{config.Name}"; - } - - mimeType = config.MimeType ?? mimeType; - - if (!string.IsNullOrEmpty(config.DisplayName)) { - fileBuilder.DisplayName = config.DisplayName; - } - } - - var createFileHttpOptions = UploadClient.BuildResumableUploadHttpOptions( - config?.HttpOptions, mimeType, fileName, size); - - var createFileConfig = new CreateFileConfig { HttpOptions = createFileHttpOptions, - ShouldReturnHttpResponse = true }; - - var createFileResponse = - await PrivateCreateAsync(fileBuilder, createFileConfig, cancellationToken); - - string errorMessage = "Upload URL not found in create file response"; - if (createFileResponse.SdkHttpResponse?.Headers == null) { - throw new InvalidOperationException(errorMessage); - } - var headers = new Dictionary(createFileResponse.SdkHttpResponse.Headers, - StringComparer.OrdinalIgnoreCase); - if (!headers.TryGetValue("x-goog-upload-url", out string? uploadUrl)) { - throw new InvalidOperationException(errorMessage); - } - return uploadUrl; - } - - private async Task DownloadStreamAsync(string fileName, DownloadFileConfig? config, - CancellationToken cancellationToken = default) { - string path = $"files/{fileName}:download?alt=media"; - var response = await _apiClient.RequestAsync(HttpMethod.Get, path, "", config?.HttpOptions, - cancellationToken); -#if NETSTANDARD2_0 - return await response.GetEntity().ReadAsStreamAsync(); -#else - return await response.GetEntity().ReadAsStreamAsync(cancellationToken); -#endif - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +using System.IO; +using System.Collections.Generic; +using Google.Apis.Auth.OAuth2; + +namespace Google.GenAI { + + public sealed class Files { + private readonly UploadClient _uploadClient; + private readonly ApiClient _apiClient; + + internal JsonNode CreateFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "file" }) != null) { + Common.SetValueByPath(toObject, new string[] { "file" }, + Common.GetValueByPath(fromObject, new string[] { "file" })); + } + + return toObject; + } + + internal JsonNode CreateFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode DeleteFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "file" }, + Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode GetFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "file" }, + Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode InternalRegisterFilesParametersToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "uris" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uris" }, + Common.GetValueByPath(fromObject, new string[] { "uris" })); + } + + return toObject; + } + + internal JsonNode ListFilesConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + return toObject; + } + + internal JsonNode ListFilesParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListFilesConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { + Common.SetValueByPath(toObject, new string[] { "files" }, + Common.GetValueByPath(fromObject, new string[] { "files" })); + } + + return toObject; + } + + internal JsonNode RegisterFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { + Common.SetValueByPath(toObject, new string[] { "files" }, + Common.GetValueByPath(fromObject, new string[] { "files" })); + } + + return toObject; + } + + public Files(ApiClient apiClient) { + { + _apiClient = apiClient; + _uploadClient = new UploadClient(_apiClient); + } + } + + private async Task PrivateListAsync( + ListFilesConfig? config, CancellationToken cancellationToken = default) { + ListFilesParameters parameter = new ListFilesParameters(); + + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse ListFilesParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = ListFilesParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateCreateAsync( + Google.GenAI.Types.File file, CreateFileConfig? config, + CancellationToken cancellationToken = default) { + CreateFileParameters parameter = new CreateFileParameters(); + + if (!Common.IsZero(file)) { + parameter.File = file; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse CreateFileParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = CreateFileParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("upload/v1beta/files", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + + if (config?.ShouldReturnHttpResponse == true) { + var httpHeaders = response.GetHeaders(); + Dictionary? headers = null; + + if (httpHeaders != null) { + headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var header in httpHeaders) { + headers[header.Key] = string.Join(", ", header.Value); + } + } + + return new CreateFileResponse { + SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } + }; + } + + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + public async Task GetAsync( + string name, GetFileConfig? config = null, CancellationToken cancellationToken = default) { + GetFileParameters parameter = new GetFileParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetFileParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = GetFileParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files/{file}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task DeleteAsync( + string name, DeleteFileConfig? config = null, + CancellationToken cancellationToken = default) { + DeleteFileParameters parameter = new DeleteFileParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse DeleteFileParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = DeleteFileParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files/{file}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateRegisterFilesAsync( + List uris, RegisterFilesConfig? config, + CancellationToken cancellationToken = default) { + InternalRegisterFilesParameters parameter = new InternalRegisterFilesParameters(); + + if (!Common.IsZero(uris)) { + parameter.Uris = uris; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse InternalRegisterFilesParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = InternalRegisterFilesParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files:register", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + + if (config?.ShouldReturnHttpResponse == true) { + var httpHeaders = response.GetHeaders(); + Dictionary? headers = null; + + if (httpHeaders != null) { + headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var header in httpHeaders) { + headers[header.Key] = string.Join(", ", header.Value); + } + } + + return new RegisterFilesResponse { + SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } + }; + } + + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task> ListAsync( + ListFilesConfig? config = null, CancellationToken cancellationToken = default) { + config ??= new ListFilesConfig(); + var initialResponse = await PrivateListAsync(config, cancellationToken); + + return new Pager( + requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), + extractItems: response => response.Files, + extractNextPageToken: response => response.NextPageToken, + extractHttpResponse: response => response.SdkHttpResponse, + updateConfigPageToken: (cfg, token) => { + cfg.PageToken = token; + return cfg; + }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); + } + + /// + /// Registers Google Cloud Storage files for use with the API. + /// + /// The list of GCS URIs to register. + /// The to use for + /// authorization. A instance + /// that specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains the . + public async Task RegisterFilesAsync( + IEnumerable uris, GoogleCredential credential, RegisterFilesConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (uris == null) + throw new ArgumentNullException(nameof(uris)); + if (credential == null) + throw new ArgumentNullException(nameof(credential)); + + ICredential cred = (ICredential)credential; + string accessToken = + await cred.GetAccessTokenForRequestAsync(cancellationToken: cancellationToken); + if (string.IsNullOrEmpty(accessToken)) { + throw new InvalidOperationException("Failed to obtain access token from credentials."); + } + + var localConfig = config ?? new RegisterFilesConfig(); + var httpOptions = localConfig.HttpOptions ?? new HttpOptions(); + var headers = httpOptions.Headers != null + ? new Dictionary(httpOptions.Headers, + StringComparer.OrdinalIgnoreCase) + : new Dictionary(StringComparer.OrdinalIgnoreCase); + + headers["Authorization"] = $"Bearer {accessToken}"; + if (!string.IsNullOrEmpty(credential.QuotaProject)) { + headers["x-goog-user-project"] = credential.QuotaProject; + } + + // Create an effective configuration with updated headers without mutating the input object. + var effectiveConfig = + localConfig with { HttpOptions = httpOptions with { Headers = headers } }; + + return await PrivateRegisterFilesAsync(uris.ToList(), effectiveConfig, cancellationToken); + } + + /// + /// Uploads a file from a file path. + /// + /// The path to the file to upload. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains the uploaded + /// metadata. + public async Task UploadAsync( + string filePath, UploadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + var fileInfo = new FileInfo(filePath); + using var stream = fileInfo.OpenRead(); + string mimeType = MimeTypes.GetMimeType(Path.GetExtension(filePath)); + + return await UploadAsync(stream, fileInfo.Length, fileInfo.Name, mimeType, config, + cancellationToken); + } + + /// + /// Uploads a file from a byte array. + /// + /// The file content as a byte array. + /// Optional file name to use. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains the uploaded + /// metadata. + public async Task UploadAsync( + byte[] bytes, string? fileName = null, UploadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + using var stream = new MemoryStream(bytes); + return await UploadAsync(stream, bytes.Length, fileName, null, config, cancellationToken); + } + + /// + /// Uploads a file from a stream. + /// + /// The stream containing the file data. + /// The size of the file in bytes. + /// Optional file name to use. + /// Optional MIME type. If not provided, defaults to + /// application/octet-stream. A + /// instance that specifies the optional configurations. A to cancel the operation. + /// A that represents the asynchronous operation. The task + /// result contains the uploaded metadata. + public async Task UploadAsync( + Stream stream, long size, string? fileName = null, string? mimeType = null, + UploadFileConfig? config = null, CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + string uploadUrl = + await CreateFileInApiAsync(config, mimeType, fileName, size, cancellationToken); + HttpContent responseContent = await _uploadClient.UploadAsync( + uploadUrl, stream, size, config?.HttpOptions, cancellationToken); + return await FileFromUploadResponseBodyAsync(responseContent); + } + + /// + /// Downloads a file and returns it as a . Caller is responsible for + /// disposing the returned stream. + /// + /// The name of the file to download (e.g., "files/abc123" or + /// "abc123"). A instance that + /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task result contains a + /// with the file data. + public async Task DownloadAsync(string fileName, DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + string extractedFileName = Transformers.TFileName(fileName); + return await DownloadStreamAsync(extractedFileName, config, cancellationToken); + } + + /// + /// Downloads a object and returns it as a . Caller is + /// responsible for disposing the returned stream. + /// + /// The object to download. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains a with the file + /// data. + public async Task DownloadAsync(Google.GenAI.Types.File file, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(file.Name)) + throw new ArgumentException("File.Name is required", nameof(file)); + + return await DownloadAsync(file.Name, config, cancellationToken); + } + + /// + /// Downloads a object and returns it as a . Caller is + /// responsible for disposing the returned stream. + /// + /// The object to download. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains a with the file + /// data. + public async Task DownloadAsync(Video video, DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(video.Uri)) + throw new ArgumentException("Video.Uri is required", nameof(video)); + + return await DownloadAsync(video.Uri, config, cancellationToken); + } + + /// + /// Downloads a object and returns it as a . + /// Caller is responsible for disposing the returned stream. + /// + /// The object to download. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains a with the file + /// data. + public async Task DownloadAsync(GeneratedVideo generatedVideo, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (generatedVideo.Video == null) + throw new ArgumentException("Video is empty", nameof(generatedVideo)); + + return await DownloadAsync(generatedVideo.Video, config, cancellationToken); + } + + /// + /// Downloads a file directly to a file path. + /// + /// The name of the file to download (e.g., "files/abc123" or + /// "abc123"). The path where the file should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(string fileName, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + using var stream = await DownloadAsync(fileName, config, cancellationToken); + using var fileStream = + new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None, + bufferSize: UploadClient.DEFAULT_CHUNK_SIZE, useAsync: true); + +#if NETSTANDARD2_0 + await stream.CopyToAsync(fileStream, bufferSize: 81920, cancellationToken); +#else + await stream.CopyToAsync(fileStream, cancellationToken); +#endif + } + + /// + /// Downloads a object directly to a file path. + /// + /// The object to download. + /// The path where the file should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(Google.GenAI.Types.File file, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(file.Name)) + throw new ArgumentException("Google.GenAI.Types.File.Name is required", nameof(file)); + + await DownloadToFileAsync(file.Name, outputPath, config, cancellationToken); + } + + /// + /// Downloads a object directly to a file path. + /// + /// The object to download. + /// The path where the Video should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(GeneratedVideo generatedVideo, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (generatedVideo.Video == null) + throw new ArgumentException("Video is empty", nameof(generatedVideo)); + + await DownloadToFileAsync(generatedVideo.Video, outputPath, config, cancellationToken); + } + + /// + /// Downloads a object directly to a file path. + /// + /// The object to download. + /// The path where the Video should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(Video video, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(video.Uri)) + throw new ArgumentException("Video.Uri is required", nameof(video)); + + await DownloadToFileAsync(video.Uri, outputPath, config, cancellationToken); + } + + private async Task FileFromUploadResponseBodyAsync( + HttpContent responseContent) { + string responseString = await responseContent.ReadAsStringAsync(); + JsonNode? responseNode = JsonNode.Parse(responseString); + + if (responseNode?["file"] is not JsonNode fileNode) { + throw new InvalidOperationException("Upload response does not contain file object"); + } + + return JsonSerializer.Deserialize(fileNode.ToString(), JsonConfig.JsonSerializerOptions) ?? + throw new InvalidOperationException("Failed to deserialize File"); + } + + private async Task CreateFileInApiAsync(UploadFileConfig? config, string? mimeType, + string? fileName, long size, + CancellationToken cancellationToken = default) { + var fileBuilder = new Google.GenAI.Types.File(); + + if (config != null) { + if (!string.IsNullOrEmpty(config.Name)) { + fileBuilder.Name = + config.Name.StartsWith("files/") ? config.Name : $"files/{config.Name}"; + } + + mimeType = config.MimeType ?? mimeType; + + if (!string.IsNullOrEmpty(config.DisplayName)) { + fileBuilder.DisplayName = config.DisplayName; + } + } + + var createFileHttpOptions = UploadClient.BuildResumableUploadHttpOptions( + config?.HttpOptions, mimeType, fileName, size); + + var createFileConfig = new CreateFileConfig { HttpOptions = createFileHttpOptions, + ShouldReturnHttpResponse = true }; + + var createFileResponse = + await PrivateCreateAsync(fileBuilder, createFileConfig, cancellationToken); + + string errorMessage = "Upload URL not found in create file response"; + if (createFileResponse.SdkHttpResponse?.Headers == null) { + throw new InvalidOperationException(errorMessage); + } + var headers = new Dictionary(createFileResponse.SdkHttpResponse.Headers, + StringComparer.OrdinalIgnoreCase); + if (!headers.TryGetValue("x-goog-upload-url", out string? uploadUrl)) { + throw new InvalidOperationException(errorMessage); + } + return uploadUrl; + } + + private async Task DownloadStreamAsync(string fileName, DownloadFileConfig? config, + CancellationToken cancellationToken = default) { + string path = $"files/{fileName}:download?alt=media"; + var response = await _apiClient.RequestAsync(HttpMethod.Get, path, "", config?.HttpOptions, + cancellationToken); +#if NETSTANDARD2_0 + return await response.GetEntity().ReadAsStreamAsync(); +#else + return await response.GetEntity().ReadAsStreamAsync(cancellationToken); +#endif + } + } +} diff --git a/Google.GenAI/GenAIJsonContext.cs b/Google.GenAI/GenAIJsonContext.cs new file mode 100644 index 00000000..c96747b4 --- /dev/null +++ b/Google.GenAI/GenAIJsonContext.cs @@ -0,0 +1,109 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI +{ + [JsonSourceGenerationOptions( + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + WriteIndented = true)] + [JsonSerializable(typeof(BatchJob))] + [JsonSerializable(typeof(Blob))] + [JsonSerializable(typeof(CachedContent))] + [JsonSerializable(typeof(CancelBatchJobParameters))] + [JsonSerializable(typeof(CancelTuningJobParameters))] + [JsonSerializable(typeof(CancelTuningJobResponse))] + [JsonSerializable(typeof(ComputeTokensParameters))] + [JsonSerializable(typeof(ComputeTokensResponse))] + [JsonSerializable(typeof(Content))] + [JsonSerializable(typeof(CountTokensParameters))] + [JsonSerializable(typeof(CountTokensResponse))] + [JsonSerializable(typeof(CreateBatchJobParameters))] + [JsonSerializable(typeof(CreateCachedContentParameters))] + [JsonSerializable(typeof(CreateEmbeddingsBatchJobParameters))] + [JsonSerializable(typeof(CreateFileParameters))] + [JsonSerializable(typeof(CreateFileResponse))] + [JsonSerializable(typeof(CreateTuningJobParametersPrivate))] + [JsonSerializable(typeof(DeleteBatchJobParameters))] + [JsonSerializable(typeof(DeleteCachedContentParameters))] + [JsonSerializable(typeof(DeleteCachedContentResponse))] + [JsonSerializable(typeof(DeleteFileParameters))] + [JsonSerializable(typeof(DeleteFileResponse))] + [JsonSerializable(typeof(DeleteModelParameters))] + [JsonSerializable(typeof(DeleteModelResponse))] + [JsonSerializable(typeof(DeleteResourceJob))] + [JsonSerializable(typeof(EditImageParameters))] + [JsonSerializable(typeof(EditImageResponse))] + [JsonSerializable(typeof(EmbedContentParametersPrivate))] + [JsonSerializable(typeof(EmbedContentResponse))] + [JsonSerializable(typeof(FetchPredictOperationParameters))] + [JsonSerializable(typeof(GenerateContentParameters))] + [JsonSerializable(typeof(GenerateContentResponse))] + [JsonSerializable(typeof(GenerateImagesParameters))] + [JsonSerializable(typeof(GenerateImagesResponse))] + [JsonSerializable(typeof(GenerateVideosOperation))] + [JsonSerializable(typeof(GenerateVideosParameters))] + [JsonSerializable(typeof(GenerateVideosResponse))] + [JsonSerializable(typeof(GetBatchJobParameters))] + [JsonSerializable(typeof(GetCachedContentParameters))] + [JsonSerializable(typeof(GetFileParameters))] + [JsonSerializable(typeof(GetModelParameters))] + [JsonSerializable(typeof(GetOperationParameters))] + [JsonSerializable(typeof(GetTuningJobParameters))] + [JsonSerializable(typeof(InternalRegisterFilesParameters))] + [JsonSerializable(typeof(ListBatchJobsResponse))] + [JsonSerializable(typeof(ListCachedContentsParameters))] + [JsonSerializable(typeof(ListCachedContentsResponse))] + [JsonSerializable(typeof(ListFilesParameters))] + [JsonSerializable(typeof(ListFilesResponse))] + [JsonSerializable(typeof(ListModelsParameters))] + [JsonSerializable(typeof(ListModelsResponse))] + [JsonSerializable(typeof(ListTuningJobsParameters))] + [JsonSerializable(typeof(ListTuningJobsResponse))] + [JsonSerializable(typeof(LiveClientContent))] + [JsonSerializable(typeof(LiveClientMessage))] + [JsonSerializable(typeof(LiveClientRealtimeInput))] + [JsonSerializable(typeof(LiveClientSetup))] + [JsonSerializable(typeof(LiveClientToolResponse))] + [JsonSerializable(typeof(LiveConnectConfig))] + [JsonSerializable(typeof(LiveConnectParameters))] + [JsonSerializable(typeof(LiveSendClientContentParameters))] + [JsonSerializable(typeof(LiveSendRealtimeInputParameters))] + [JsonSerializable(typeof(LiveSendToolResponseParameters))] + [JsonSerializable(typeof(LiveServerContent))] + [JsonSerializable(typeof(LiveServerGoAway))] + [JsonSerializable(typeof(LiveServerMessage))] + [JsonSerializable(typeof(LiveServerSessionResumptionUpdate))] + [JsonSerializable(typeof(LiveServerSetupComplete))] + [JsonSerializable(typeof(LiveServerToolCall))] + [JsonSerializable(typeof(LiveServerToolCallCancellation))] + [JsonSerializable(typeof(Model))] + [JsonSerializable(typeof(RealtimeInputConfig))] + [JsonSerializable(typeof(RecontextImageParameters))] + [JsonSerializable(typeof(RecontextImageResponse))] + [JsonSerializable(typeof(RegisterFilesResponse))] + [JsonSerializable(typeof(Schema))] + [JsonSerializable(typeof(SegmentImageParameters))] + [JsonSerializable(typeof(SegmentImageResponse))] + [JsonSerializable(typeof(SpeechConfig))] + [JsonSerializable(typeof(Tool))] + [JsonSerializable(typeof(TuningJob))] + [JsonSerializable(typeof(TuningOperation))] + [JsonSerializable(typeof(UpdateCachedContentParameters))] + [JsonSerializable(typeof(UpdateModelParameters))] + [JsonSerializable(typeof(UpscaleImageAPIParameters))] + [JsonSerializable(typeof(UpscaleImageResponse))] + [JsonSerializable(typeof(Google.GenAI.Types.File))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(System.Text.Json.Nodes.JsonNode))] + [JsonSerializable(typeof(System.Text.Json.Nodes.JsonObject))] + [JsonSerializable(typeof(System.Text.Json.Nodes.JsonArray))] + internal partial class GenAIJsonContext : JsonSerializerContext + { + } +} diff --git a/Google.GenAI/Google.GenAI.csproj b/Google.GenAI/Google.GenAI.csproj index cebfc17f..c5b72144 100644 --- a/Google.GenAI/Google.GenAI.csproj +++ b/Google.GenAI/Google.GenAI.csproj @@ -1,41 +1,42 @@ - - - - - Library - netstandard2.0;net8.0 - 10.0 - enable - enable - true - true - Google.GenAI - Google LLC - Google GenAI SDK for .NET - true - Apache-2.0 - https://github.com/googleapis/dotnet-genai - https://github.com/googleapis/dotnet-genai - git - README.md - - - - $(WarningsAsErrors);CS1570$(NoWarn);CS1591 - IDE0005 - $(NoWarn);MEAI001 - - - - - - - - - - - - - - - + + + + + Library + netstandard2.0;net8.0 + 10.0 + enable + enable + true + true + Google.GenAI + Google LLC + Google GenAI SDK for .NET + true + Apache-2.0 + https://github.com/googleapis/dotnet-genai + https://github.com/googleapis/dotnet-genai + git + README.md + + + + $(WarningsAsErrors);CS1570$(NoWarn);CS1591 + IDE0005 + $(NoWarn);MEAI001 + + + + + + + + + + + + + + + + diff --git a/Google.GenAI/JsonConfig.cs b/Google.GenAI/JsonConfig.cs index 1fe48286..b6170696 100644 --- a/Google.GenAI/JsonConfig.cs +++ b/Google.GenAI/JsonConfig.cs @@ -1,41 +1,59 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Text.Json; -using System.Text.Json.Serialization; - -using Google.GenAI.Serialization; - -namespace Google.GenAI -{ - /// - /// Configuration for JSON serialization. - /// - internal static class JsonConfig - { - public static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - WriteIndented = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - Converters = - { - new StringToLongConverter(), - new StringToNullableLongConverter(), - } - }; - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +using Google.GenAI.Serialization; + +namespace Google.GenAI +{ + /// + /// Configuration for JSON serialization. + /// + internal static class JsonConfig + { + /// + /// Options for external API output (indented, camelCase, no nulls). + /// + public static readonly JsonSerializerOptions JsonSerializerOptions = CreateOptions(writeIndented: true); + + /// + /// Options for internal serialization (compact, camelCase, no nulls). + /// Used for intermediate serialize-then-parse round-trips where indentation is wasteful. + /// + internal static readonly JsonSerializerOptions InternalSerializerOptions = CreateOptions(writeIndented: false); + + private static JsonSerializerOptions CreateOptions(bool writeIndented) + { + var options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = writeIndented, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = + { + new StringToLongConverter(), + new StringToNullableLongConverter(), + } + }; + options.TypeInfoResolverChain.Insert(0, GenAIJsonContext.Default); + options.TypeInfoResolverChain.Add(new DefaultJsonTypeInfoResolver()); + return options; + } + } +} diff --git a/Google.GenAI/Live.cs b/Google.GenAI/Live.cs index 476165d8..17d68395 100644 --- a/Google.GenAI/Live.cs +++ b/Google.GenAI/Live.cs @@ -1,436 +1,436 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Net.WebSockets; -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; - -using Google.GenAI.Types; - -namespace Google.GenAI -{ - /// - /// Live class encapsulates the logic for connecting to Google's GenAI Live API. - /// Use to establish a websocket connection session. - /// - public class Live - { - private readonly ApiClient _apiClient; - - public Live(ApiClient apiClient) - { - _apiClient = apiClient; - } - - /// - /// Establishes a websocket connection to the specified model with the given configuration. - /// - /// - /// The name of the model to connect to. For example "gemini-2.0-flash-live-preview-04-09". - /// - /// - /// The parameters for establishing a connection to the model. - /// - /// The cancellation token to use for the connection. - /// - public async Task ConnectAsync(string model, LiveConnectConfig config, CancellationToken cancellationToken = default) - { - var clientWebSocket = new ClientWebSocket(); - bool success = false; - try - { - await SetRequestHeadersAsync(clientWebSocket, cancellationToken); - Uri serverUri = GetServerUri(); - await clientWebSocket.ConnectAsync(serverUri, cancellationToken); - string setupClientMessage = getSetupMessage(model, config); - byte[] buffer = Encoding.UTF8.GetBytes(setupClientMessage); - - await clientWebSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken); - - var session = new AsyncSession(clientWebSocket, _apiClient); - success = true; - return session; - } - finally - { - if (!success) - { - clientWebSocket.Dispose(); - } - } - } - - private async Task SetRequestHeadersAsync(ClientWebSocket clientWebSocket, CancellationToken cancellationToken = default) - { - if (_apiClient.VertexAI) - { - if (_apiClient.Credentials == null) - { - throw new InvalidOperationException("GoogleAuth credentials are required for Vertex AI."); - } - - string accessToken = await _apiClient.Credentials.GetAccessTokenForRequestAsync(cancellationToken: cancellationToken); - if (string.IsNullOrEmpty(accessToken)) - { - throw new InvalidOperationException("Failed to retrieve access token from credentials."); - } - clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {accessToken}"); - } - else - { - if (string.IsNullOrEmpty(_apiClient.ApiKey)) - { - throw new InvalidOperationException("An API key is required for Gemini API connections."); - } - clientWebSocket.Options.SetRequestHeader("x-goog-api-key", _apiClient.ApiKey); - } - - foreach (var header in _apiClient?.HttpOptions?.Headers ?? new Dictionary()) - { - clientWebSocket.Options.SetRequestHeader(header.Key, header.Value); - } - } - - private Uri GetServerUri() - { - string baseUrl = _apiClient.HttpOptions?.BaseUrl; - if (string.IsNullOrEmpty(baseUrl)) - { - throw new InvalidOperationException("BaseUrl is not set in Client."); - } - try - { - bool hasSufficientAuth = (_apiClient.Project != null && _apiClient.Location != null) || _apiClient.ApiKey != null; - if (_apiClient.CustomBaseUrl != null && !_apiClient.CustomBaseUrl.EndsWith(".googleapis.com") && !hasSufficientAuth) - { - var customUri = new Uri(_apiClient.CustomBaseUrl); - return new UriBuilder( customUri ) { Scheme = customUri.Scheme == "http" ? "ws" : "wss" }.Uri; - } - - var baseUri = new Uri(baseUrl); - var uriBuilder = new UriBuilder(baseUri) - { - Scheme = baseUri.Scheme == "http" ? "ws" : "wss" - }; - - string wsBaseUrl = uriBuilder.Uri.ToString().TrimEnd('/'); - - if (_apiClient.VertexAI) - { - string apiVersion = _apiClient.HttpOptions?.ApiVersion ?? "v1beta1"; - return new Uri($"{wsBaseUrl}/ws/google.cloud.aiplatform.{apiVersion}.LlmBidiService/BidiGenerateContent"); - } - else - { - string apiVersion = _apiClient.HttpOptions?.ApiVersion ?? "v1beta"; - return new Uri($"{wsBaseUrl}/ws/google.ai.generativelanguage.{apiVersion}.GenerativeService.BidiGenerateContent"); - } - } - catch (UriFormatException e) - { - throw new InvalidOperationException("Failed to parse URL.", e); - } - } - private string getSetupMessage(string model, LiveConnectConfig config) - { - var transformedModel = Transformers.TModel(this._apiClient, model); - bool shouldPrependVertexProjectPath = _apiClient.VertexAI && string.IsNullOrEmpty(_apiClient.ApiKey) && - !((_apiClient.HttpOptions?.BaseUrlResourceScope == Types.ResourceScope.Collection) && !string.IsNullOrEmpty(_apiClient.HttpOptions?.BaseUrl)); - - if (shouldPrependVertexProjectPath && transformedModel != null && transformedModel.StartsWith("publishers/")) - { - transformedModel = string.Format( - "projects/{0}/locations/{1}/{2}", - _apiClient.Project, - _apiClient.Location, - transformedModel - ); - } - LiveConnectParameters parameters = new LiveConnectParameters - { - Model = transformedModel, - Config = config, - }; - LiveConverters liveConverters = new LiveConverters(_apiClient); - string jsonString = JsonSerializer.Serialize(parameters); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) - { - throw new InvalidOperationException("Failed to parse jsonString into a JsonNode."); - } - JsonNode body; - if (_apiClient.VertexAI) - { - body = liveConverters.LiveConnectParametersToVertex(_apiClient, parameterNode, new JsonObject()); - } - else - { - body = liveConverters.LiveConnectParametersToMldev(_apiClient, parameterNode, new JsonObject()); - } - body?.AsObject().Remove("config"); - return JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions); - } - - } - /// - /// Represents a websocket connection to the Google's GenAI Live API. - /// This class is not meant to be instantiated directly. - /// Instead, use to create an instance. - /// - public class AsyncSession : IAsyncDisposable - { - private readonly WebSocket _webSocket; - private readonly ApiClient _apiClient; - private int _isDisposed = 0; // 0 = false, 1 = true. Used with Interlocked. - - public AsyncSession(WebSocket webSocket, ApiClient apiClient) - { - _webSocket = webSocket; - _apiClient = apiClient; - } - - /// - /// Sends non-realtime, turn-based content to the model. - /// - /// There are two ways to send messages to the live API: - /// SendClientContentAsync and SendRealtimeInputAsync. - /// - /// - /// SendClientContentAsync messages are added to the model context - /// in order. Because SendClientContentAsync guarantees the order - /// of messages between the client and the server, the model cannot respond as - /// quickly as with SendRealtimeInputAsync. This is most noticeable when - /// sending objects that require significant preprocessing time (typically images). - /// - /// - /// SendRealtimeInputAsync sends a list of objects, - /// which offers more options than the objects sent by - /// SendClientContentAsync. - /// - /// - /// The main use cases for SendClientContentAsync over - /// SendRealtimeInputAsync are: - /// - /// - /// Prefilling a conversation context (including sending anything that can't be - /// represented as a realtime message) before starting a realtime conversation. - /// - /// - /// Conducting a non-realtime conversation with the live API. - /// - /// - /// Caution: Interleaving SendClientContentAsync and - /// SendRealtimeInputAsync in the same conversation is not recommended and - /// can lead to unexpected behavior. - /// - /// - /// - /// The client content to send to the model. - /// - /// The cancellation token to use for the send operation. - /// - public async Task SendClientContentAsync(LiveSendClientContentParameters clientContent, CancellationToken cancellationToken = default) - { - LiveClientMessage liveClientMessage = new LiveClientMessage(); - liveClientMessage.ClientContent = new LiveClientContent(); - liveClientMessage.ClientContent.Turns = clientContent.Turns; - liveClientMessage.ClientContent.TurnComplete = clientContent.TurnComplete; - - await send(liveClientMessage, cancellationToken); - } - - /// - /// Sends realtime input to the model. With SendRealtimeInputAsync, - /// Google's GenAI Live API will respond to audio automatically based on voice - /// activity detection (VAD). SendRealtimeInputAsync is optimized for - /// responsiveness at the expense of deterministic ordering of the conversation - /// messages. Response tokens are added to the context as they become available. - /// - /// - /// The realtime input to send to the model. - /// - /// The cancellation token to use for the send operation. - /// - public async Task SendRealtimeInputAsync(LiveSendRealtimeInputParameters realtimeInput, CancellationToken cancellationToken = default) - { - LiveClientMessage liveClientMessage = new LiveClientMessage(); - liveClientMessage.RealtimeInputParameters = realtimeInput; - await send(liveClientMessage, cancellationToken); - } - - public async Task SendToolResponseAsync(LiveSendToolResponseParameters toolResponse, CancellationToken cancellationToken = default) - { - LiveClientMessage liveClientMessage = new LiveClientMessage(); - liveClientMessage.ToolResponse = new LiveClientToolResponse - { - FunctionResponses = toolResponse.FunctionResponses - }; - await send(liveClientMessage, cancellationToken); - } - - /// - /// Receives model responses from the server. - /// - /// - /// A containing the model's response, or null if the - /// connection has been gracefully closed. - /// - /// Thrown if an empty or invalid message is received. - /// Thrown for underlying WebSocket errors that are not a graceful close. - public async Task ReceiveAsync(CancellationToken cancellationToken = default) - { - if (_isDisposed == 1) - { - return null; - } - - switch (_webSocket.State) - { - case WebSocketState.Connecting: - throw new InvalidOperationException("Cannot receive data while the WebSocket is still connecting. Ensure that the ConnectAsync method has completed."); - case WebSocketState.Open: - break; // Proceed with receiving. - default: - return null; - } - - var buffer = new byte[4096]; - using var messageStream = new MemoryStream(); - WebSocketReceiveResult result; - - try - { - do - { - result = await _webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken); - if (result.MessageType == WebSocketMessageType.Close) - { - return null; - } - messageStream.Write(buffer, 0, result.Count); - } - while (!result.EndOfMessage); - } - catch (WebSocketException ex) when (ex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) - { - return null; - } - - var messageString = Encoding.UTF8.GetString(messageStream.GetBuffer(), 0, (int)messageStream.Length); - if (string.IsNullOrEmpty(messageString)) - { - throw new InvalidOperationException("Received an empty message from the server."); - } - - JsonNode? serverMessageNode = JsonNode.Parse(messageString); - if (serverMessageNode == null) - { - throw new InvalidOperationException("Failed to deserialize server message because it is null."); - } - LiveConverters liveConverters = new LiveConverters(_apiClient); - JsonNode transformedNode; - if (_apiClient.VertexAI) - { - transformedNode = liveConverters.LiveServerMessageFromVertex(serverMessageNode, new JsonObject()); - } - else - { - transformedNode = liveConverters.LiveServerMessageFromMldev(serverMessageNode, new JsonObject()); - } - var serverMessage = JsonSerializer.Deserialize(transformedNode, JsonConfig.JsonSerializerOptions); - if (serverMessage == null) - { - throw new InvalidOperationException("Failed to deserialize server message because it is null."); - } - return serverMessage; - } - - /// - /// Closes the WebSocket connection gracefully. This method is thread-safe and idempotent. - /// - public async Task CloseAsync() - { - // Atomically check and set the disposed flag to ensure this block runs only once. - // Critical to avoid race conditions in multi-threaded scenarios. - if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) != 0) - { - return; - } - - using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - - try - { - if (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting) - { - await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", timeoutCts.Token); - } - else if (_webSocket.State == WebSocketState.CloseReceived) - { - await _webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledging server close", timeoutCts.Token); - } - // For other states (None, CloseSent, Closed, Aborted), no action is needed. - } - catch (Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || - ex is WebSocketException || ex is OperationCanceledException || - ex is IOException) - { - // Suppress exceptions during cleanup as the primary goal is to release resources. - // Optionally, these exceptions can be logged for debugging purposes. - } - finally - { - _webSocket.Dispose(); - } - } - - /// - /// Asynchronously disposes the session by closing the WebSocket connection. - /// - public async ValueTask DisposeAsync() - { - await CloseAsync(); - } - - private async Task send(LiveClientMessage liveClientMessage, CancellationToken cancellationToken = default) - { - JsonNode? liveClientMessageNode = JsonNode.Parse(JsonSerializer.Serialize(liveClientMessage, JsonConfig.JsonSerializerOptions)); - if (liveClientMessageNode == null) - { - throw new InvalidOperationException("Failed to parse liveClientMessage into a JsonNode."); - } - LiveConverters liveConverters = new LiveConverters(_apiClient); - JsonNode body; - if (_apiClient.VertexAI) - { - body = liveConverters.LiveClientMessageToVertex(liveClientMessageNode, new JsonObject()); - } - else - { - body = liveConverters.LiveClientMessageToMldev(liveClientMessageNode, new JsonObject()); - } - string jsonMessage = JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions); - byte[] buffer = Encoding.UTF8.GetBytes(jsonMessage); - - if (_webSocket.State != WebSocketState.Open) - { - throw new InvalidOperationException($"WebSocket is not open. State: {_webSocket.State}"); - } - await _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken); - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Net.WebSockets; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; + +using Google.GenAI.Types; + +namespace Google.GenAI +{ + /// + /// Live class encapsulates the logic for connecting to Google's GenAI Live API. + /// Use to establish a websocket connection session. + /// + public class Live + { + private readonly ApiClient _apiClient; + + public Live(ApiClient apiClient) + { + _apiClient = apiClient; + } + + /// + /// Establishes a websocket connection to the specified model with the given configuration. + /// + /// + /// The name of the model to connect to. For example "gemini-2.0-flash-live-preview-04-09". + /// + /// + /// The parameters for establishing a connection to the model. + /// + /// The cancellation token to use for the connection. + /// + public async Task ConnectAsync(string model, LiveConnectConfig config, CancellationToken cancellationToken = default) + { + var clientWebSocket = new ClientWebSocket(); + bool success = false; + try + { + await SetRequestHeadersAsync(clientWebSocket, cancellationToken); + Uri serverUri = GetServerUri(); + await clientWebSocket.ConnectAsync(serverUri, cancellationToken); + string setupClientMessage = getSetupMessage(model, config); + byte[] buffer = Encoding.UTF8.GetBytes(setupClientMessage); + + await clientWebSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken); + + var session = new AsyncSession(clientWebSocket, _apiClient); + success = true; + return session; + } + finally + { + if (!success) + { + clientWebSocket.Dispose(); + } + } + } + + private async Task SetRequestHeadersAsync(ClientWebSocket clientWebSocket, CancellationToken cancellationToken = default) + { + if (_apiClient.VertexAI) + { + if (_apiClient.Credentials == null) + { + throw new InvalidOperationException("GoogleAuth credentials are required for Vertex AI."); + } + + string accessToken = await _apiClient.Credentials.GetAccessTokenForRequestAsync(cancellationToken: cancellationToken); + if (string.IsNullOrEmpty(accessToken)) + { + throw new InvalidOperationException("Failed to retrieve access token from credentials."); + } + clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {accessToken}"); + } + else + { + if (string.IsNullOrEmpty(_apiClient.ApiKey)) + { + throw new InvalidOperationException("An API key is required for Gemini API connections."); + } + clientWebSocket.Options.SetRequestHeader("x-goog-api-key", _apiClient.ApiKey); + } + + foreach (var header in _apiClient?.HttpOptions?.Headers ?? new Dictionary()) + { + clientWebSocket.Options.SetRequestHeader(header.Key, header.Value); + } + } + + private Uri GetServerUri() + { + string baseUrl = _apiClient.HttpOptions?.BaseUrl; + if (string.IsNullOrEmpty(baseUrl)) + { + throw new InvalidOperationException("BaseUrl is not set in Client."); + } + try + { + bool hasSufficientAuth = (_apiClient.Project != null && _apiClient.Location != null) || _apiClient.ApiKey != null; + if (_apiClient.CustomBaseUrl != null && !_apiClient.CustomBaseUrl.EndsWith(".googleapis.com") && !hasSufficientAuth) + { + var customUri = new Uri(_apiClient.CustomBaseUrl); + return new UriBuilder( customUri ) { Scheme = customUri.Scheme == "http" ? "ws" : "wss" }.Uri; + } + + var baseUri = new Uri(baseUrl); + var uriBuilder = new UriBuilder(baseUri) + { + Scheme = baseUri.Scheme == "http" ? "ws" : "wss" + }; + + string wsBaseUrl = uriBuilder.Uri.ToString().TrimEnd('/'); + + if (_apiClient.VertexAI) + { + string apiVersion = _apiClient.HttpOptions?.ApiVersion ?? "v1beta1"; + return new Uri($"{wsBaseUrl}/ws/google.cloud.aiplatform.{apiVersion}.LlmBidiService/BidiGenerateContent"); + } + else + { + string apiVersion = _apiClient.HttpOptions?.ApiVersion ?? "v1beta"; + return new Uri($"{wsBaseUrl}/ws/google.ai.generativelanguage.{apiVersion}.GenerativeService.BidiGenerateContent"); + } + } + catch (UriFormatException e) + { + throw new InvalidOperationException("Failed to parse URL.", e); + } + } + private string getSetupMessage(string model, LiveConnectConfig config) + { + var transformedModel = Transformers.TModel(this._apiClient, model); + bool shouldPrependVertexProjectPath = _apiClient.VertexAI && string.IsNullOrEmpty(_apiClient.ApiKey) && + !((_apiClient.HttpOptions?.BaseUrlResourceScope == Types.ResourceScope.Collection) && !string.IsNullOrEmpty(_apiClient.HttpOptions?.BaseUrl)); + + if (shouldPrependVertexProjectPath && transformedModel != null && transformedModel.StartsWith("publishers/")) + { + transformedModel = string.Format( + "projects/{0}/locations/{1}/{2}", + _apiClient.Project, + _apiClient.Location, + transformedModel + ); + } + LiveConnectParameters parameters = new LiveConnectParameters + { + Model = transformedModel, + Config = config, + }; + LiveConverters liveConverters = new LiveConverters(_apiClient); + string jsonString = JsonSerializer.Serialize(parameters, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) + { + throw new InvalidOperationException("Failed to parse jsonString into a JsonNode."); + } + JsonNode body; + if (_apiClient.VertexAI) + { + body = liveConverters.LiveConnectParametersToVertex(_apiClient, parameterNode, new JsonObject()); + } + else + { + body = liveConverters.LiveConnectParametersToMldev(_apiClient, parameterNode, new JsonObject()); + } + body?.AsObject().Remove("config"); + return JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions); + } + + } + /// + /// Represents a websocket connection to the Google's GenAI Live API. + /// This class is not meant to be instantiated directly. + /// Instead, use to create an instance. + /// + public class AsyncSession : IAsyncDisposable + { + private readonly WebSocket _webSocket; + private readonly ApiClient _apiClient; + private int _isDisposed = 0; // 0 = false, 1 = true. Used with Interlocked. + + public AsyncSession(WebSocket webSocket, ApiClient apiClient) + { + _webSocket = webSocket; + _apiClient = apiClient; + } + + /// + /// Sends non-realtime, turn-based content to the model. + /// + /// There are two ways to send messages to the live API: + /// SendClientContentAsync and SendRealtimeInputAsync. + /// + /// + /// SendClientContentAsync messages are added to the model context + /// in order. Because SendClientContentAsync guarantees the order + /// of messages between the client and the server, the model cannot respond as + /// quickly as with SendRealtimeInputAsync. This is most noticeable when + /// sending objects that require significant preprocessing time (typically images). + /// + /// + /// SendRealtimeInputAsync sends a list of objects, + /// which offers more options than the objects sent by + /// SendClientContentAsync. + /// + /// + /// The main use cases for SendClientContentAsync over + /// SendRealtimeInputAsync are: + /// + /// + /// Prefilling a conversation context (including sending anything that can't be + /// represented as a realtime message) before starting a realtime conversation. + /// + /// + /// Conducting a non-realtime conversation with the live API. + /// + /// + /// Caution: Interleaving SendClientContentAsync and + /// SendRealtimeInputAsync in the same conversation is not recommended and + /// can lead to unexpected behavior. + /// + /// + /// + /// The client content to send to the model. + /// + /// The cancellation token to use for the send operation. + /// + public async Task SendClientContentAsync(LiveSendClientContentParameters clientContent, CancellationToken cancellationToken = default) + { + LiveClientMessage liveClientMessage = new LiveClientMessage(); + liveClientMessage.ClientContent = new LiveClientContent(); + liveClientMessage.ClientContent.Turns = clientContent.Turns; + liveClientMessage.ClientContent.TurnComplete = clientContent.TurnComplete; + + await send(liveClientMessage, cancellationToken); + } + + /// + /// Sends realtime input to the model. With SendRealtimeInputAsync, + /// Google's GenAI Live API will respond to audio automatically based on voice + /// activity detection (VAD). SendRealtimeInputAsync is optimized for + /// responsiveness at the expense of deterministic ordering of the conversation + /// messages. Response tokens are added to the context as they become available. + /// + /// + /// The realtime input to send to the model. + /// + /// The cancellation token to use for the send operation. + /// + public async Task SendRealtimeInputAsync(LiveSendRealtimeInputParameters realtimeInput, CancellationToken cancellationToken = default) + { + LiveClientMessage liveClientMessage = new LiveClientMessage(); + liveClientMessage.RealtimeInputParameters = realtimeInput; + await send(liveClientMessage, cancellationToken); + } + + public async Task SendToolResponseAsync(LiveSendToolResponseParameters toolResponse, CancellationToken cancellationToken = default) + { + LiveClientMessage liveClientMessage = new LiveClientMessage(); + liveClientMessage.ToolResponse = new LiveClientToolResponse + { + FunctionResponses = toolResponse.FunctionResponses + }; + await send(liveClientMessage, cancellationToken); + } + + /// + /// Receives model responses from the server. + /// + /// + /// A containing the model's response, or null if the + /// connection has been gracefully closed. + /// + /// Thrown if an empty or invalid message is received. + /// Thrown for underlying WebSocket errors that are not a graceful close. + public async Task ReceiveAsync(CancellationToken cancellationToken = default) + { + if (_isDisposed == 1) + { + return null; + } + + switch (_webSocket.State) + { + case WebSocketState.Connecting: + throw new InvalidOperationException("Cannot receive data while the WebSocket is still connecting. Ensure that the ConnectAsync method has completed."); + case WebSocketState.Open: + break; // Proceed with receiving. + default: + return null; + } + + var buffer = new byte[4096]; + using var messageStream = new MemoryStream(); + WebSocketReceiveResult result; + + try + { + do + { + result = await _webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationToken); + if (result.MessageType == WebSocketMessageType.Close) + { + return null; + } + messageStream.Write(buffer, 0, result.Count); + } + while (!result.EndOfMessage); + } + catch (WebSocketException ex) when (ex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) + { + return null; + } + + var messageString = Encoding.UTF8.GetString(messageStream.GetBuffer(), 0, (int)messageStream.Length); + if (string.IsNullOrEmpty(messageString)) + { + throw new InvalidOperationException("Received an empty message from the server."); + } + + JsonNode? serverMessageNode = JsonNode.Parse(messageString); + if (serverMessageNode == null) + { + throw new InvalidOperationException("Failed to deserialize server message because it is null."); + } + LiveConverters liveConverters = new LiveConverters(_apiClient); + JsonNode transformedNode; + if (_apiClient.VertexAI) + { + transformedNode = liveConverters.LiveServerMessageFromVertex(serverMessageNode, new JsonObject()); + } + else + { + transformedNode = liveConverters.LiveServerMessageFromMldev(serverMessageNode, new JsonObject()); + } + var serverMessage = JsonSerializer.Deserialize(transformedNode, JsonConfig.JsonSerializerOptions); + if (serverMessage == null) + { + throw new InvalidOperationException("Failed to deserialize server message because it is null."); + } + return serverMessage; + } + + /// + /// Closes the WebSocket connection gracefully. This method is thread-safe and idempotent. + /// + public async Task CloseAsync() + { + // Atomically check and set the disposed flag to ensure this block runs only once. + // Critical to avoid race conditions in multi-threaded scenarios. + if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) != 0) + { + return; + } + + using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + + try + { + if (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting) + { + await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", timeoutCts.Token); + } + else if (_webSocket.State == WebSocketState.CloseReceived) + { + await _webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledging server close", timeoutCts.Token); + } + // For other states (None, CloseSent, Closed, Aborted), no action is needed. + } + catch (Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || + ex is WebSocketException || ex is OperationCanceledException || + ex is IOException) + { + // Suppress exceptions during cleanup as the primary goal is to release resources. + // Optionally, these exceptions can be logged for debugging purposes. + } + finally + { + _webSocket.Dispose(); + } + } + + /// + /// Asynchronously disposes the session by closing the WebSocket connection. + /// + public async ValueTask DisposeAsync() + { + await CloseAsync(); + } + + private async Task send(LiveClientMessage liveClientMessage, CancellationToken cancellationToken = default) + { + JsonNode? liveClientMessageNode = JsonNode.Parse(JsonSerializer.Serialize(liveClientMessage, JsonConfig.JsonSerializerOptions)); + if (liveClientMessageNode == null) + { + throw new InvalidOperationException("Failed to parse liveClientMessage into a JsonNode."); + } + LiveConverters liveConverters = new LiveConverters(_apiClient); + JsonNode body; + if (_apiClient.VertexAI) + { + body = liveConverters.LiveClientMessageToVertex(liveClientMessageNode, new JsonObject()); + } + else + { + body = liveConverters.LiveClientMessageToMldev(liveClientMessageNode, new JsonObject()); + } + string jsonMessage = JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions); + byte[] buffer = Encoding.UTF8.GetBytes(jsonMessage); + + if (_webSocket.State != WebSocketState.Open) + { + throw new InvalidOperationException($"WebSocket is not open. State: {_webSocket.State}"); + } + await _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken); + } + } +} diff --git a/Google.GenAI/Models.cs b/Google.GenAI/Models.cs index d5a75e2e..730f7002 100644 --- a/Google.GenAI/Models.cs +++ b/Google.GenAI/Models.cs @@ -1,5982 +1,5982 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - - /// - /// Provides methods to interact with the Gen AI models. This class is not intended to be - /// instantiated directly, instead it should be accessed through the class. - /// - - public sealed class Models { - private readonly ApiClient _apiClient; - - internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { - Common.SetValueByPath(toObject, new string[] { "apiKey" }, - Common.GetValueByPath(fromObject, new string[] { "apiKey" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { - throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { - throw new NotSupportedException("authType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { - throw new NotSupportedException( - "googleServiceAccountConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { - throw new NotSupportedException( - "httpBasicAuthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { - throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { - throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { - Common.SetValueByPath(toObject, new string[] { "data" }, - Common.GetValueByPath(fromObject, new string[] { "data" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode CandidateFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { - Common.SetValueByPath(toObject, new string[] { "content" }, - Common.GetValueByPath(fromObject, new string[] { "content" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "citationMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "citationMetadata" }, - CitationMetadataFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "citationMetadata" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tokenCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tokenCount" }, - Common.GetValueByPath(fromObject, new string[] { "tokenCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "finishReason" }) != null) { - Common.SetValueByPath(toObject, new string[] { "finishReason" }, - Common.GetValueByPath(fromObject, new string[] { "finishReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "groundingMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "avgLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "index" }) != null) { - Common.SetValueByPath(toObject, new string[] { "index" }, - Common.GetValueByPath(fromObject, new string[] { "index" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobsResult" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobsResult" }, - Common.GetValueByPath(fromObject, new string[] { "logprobsResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyRatings" }) != null) { - Common.SetValueByPath(toObject, new string[] { "safetyRatings" }, - Common.GetValueByPath(fromObject, new string[] { "safetyRatings" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "urlContextMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" })); - } - - return toObject; - } - - internal JsonNode CitationMetadataFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "citationSources" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "citations" }, - Common.GetValueByPath(fromObject, new string[] { "citationSources" })); - } - - return toObject; - } - - internal JsonNode ComputeTokensParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "contents" }, result); - } - - return toObject; - } - - internal JsonNode ComputeTokensResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tokensInfo" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tokensInfo" }, - Common.GetValueByPath(fromObject, new string[] { "tokensInfo" })); - } - - return toObject; - } - - internal JsonNode ContentEmbeddingFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "values" }) != null) { - Common.SetValueByPath(toObject, new string[] { "values" }, - Common.GetValueByPath(fromObject, new string[] { "values" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "statistics" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "statistics" }, - ContentEmbeddingStatisticsFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "statistics" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode ContentEmbeddingStatisticsFromVertex(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "truncated" }) != null) { - Common.SetValueByPath(toObject, new string[] { "truncated" }, - Common.GetValueByPath(fromObject, new string[] { "truncated" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "token_count" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tokenCount" }, - Common.GetValueByPath(fromObject, new string[] { "token_count" })); - } - - return toObject; - } - - internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode ContentToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode ControlReferenceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "controlType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "controlType" }, - Common.GetValueByPath(fromObject, new string[] { "controlType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableControlImageComputation" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "computeControl" }, - Common.GetValueByPath(fromObject, new string[] { "enableControlImageComputation" })); - } - - return toObject; - } - - internal JsonNode CountTokensConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }))) { - throw new NotSupportedException( - "systemInstruction parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "tools" }))) { - throw new NotSupportedException("tools parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "generationConfig" }))) { - throw new NotSupportedException( - "generationConfig parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode CountTokensConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ToolToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "generationConfig" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "generationConfig" }, - GenerationConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "generationConfig" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode CountTokensParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CountTokensConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode CountTokensParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CountTokensConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode CountTokensResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "totalTokens" }) != null) { - Common.SetValueByPath(toObject, new string[] { "totalTokens" }, - Common.GetValueByPath(fromObject, new string[] { "totalTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContentTokenCount" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "cachedContentTokenCount" }, - Common.GetValueByPath(fromObject, new string[] { "cachedContentTokenCount" })); - } - - return toObject; - } - - internal JsonNode CountTokensResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "totalTokens" }) != null) { - Common.SetValueByPath(toObject, new string[] { "totalTokens" }, - Common.GetValueByPath(fromObject, new string[] { "totalTokens" })); - } - - return toObject; - } - - internal JsonNode DeleteModelParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - return toObject; - } - - internal JsonNode DeleteModelParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - return toObject; - } - - internal JsonNode DeleteModelResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode DeleteModelResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode EditImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, - Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, - Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "guidanceScale" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "guidanceScale" }, - Common.GetValueByPath(fromObject, new string[] { "guidanceScale" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "safetySetting" }, - Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "includeSafetyAttributes" }, - Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "includeRaiReason" }, - Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "language" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "language" }, - Common.GetValueByPath(fromObject, new string[] { "language" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "parameters", "outputOptions", "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "addWatermark" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "addWatermark" }, - Common.GetValueByPath(fromObject, new string[] { "addWatermark" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "editMode" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "editMode" }, - Common.GetValueByPath(fromObject, new string[] { "editMode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "baseSteps" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "parameters", "editConfig", "baseSteps" }, - Common.GetValueByPath(fromObject, new string[] { "baseSteps" })); - } - - return toObject; - } - - internal JsonNode EditImageParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "referenceImages" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "referenceImages" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - ReferenceImageAPIToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "instances[0]", "referenceImages" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = EditImageConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode EditImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); - } - - return toObject; - } - - internal JsonNode EmbedContentConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "requests[]", "taskType" }, - Common.GetValueByPath(fromObject, new string[] { "taskType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "requests[]", "title" }, - Common.GetValueByPath(fromObject, new string[] { "title" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "requests[]", "outputDimensionality" }, - Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mimeType" }))) { - throw new NotSupportedException("mimeType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }))) { - throw new NotSupportedException("autoTruncate parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode EmbedContentConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - JsonNode discriminatorTaskType = - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); - string discriminatorValueTaskType = - discriminatorTaskType == null ? "PREDICT" : discriminatorTaskType.GetValue(); - if (discriminatorValueTaskType == "PREDICT") { - if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[]", "task_type" }, - Common.GetValueByPath(fromObject, new string[] { "taskType" })); - } - } else if (discriminatorValueTaskType == "EMBED_CONTENT") { - if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "taskType" }, - Common.GetValueByPath(fromObject, new string[] { "taskType" })); - } - } - - JsonNode discriminatorTitle = - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); - string discriminatorValueTitle = - discriminatorTitle == null ? "PREDICT" : discriminatorTitle.GetValue(); - if (discriminatorValueTitle == "PREDICT") { - if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[]", "title" }, - Common.GetValueByPath(fromObject, new string[] { "title" })); - } - } else if (discriminatorValueTitle == "EMBED_CONTENT") { - if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "title" }, - Common.GetValueByPath(fromObject, new string[] { "title" })); - } - } - - JsonNode discriminatorOutputDimensionality = - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); - string discriminatorValueOutputDimensionality = - discriminatorOutputDimensionality == null - ? "PREDICT" - : discriminatorOutputDimensionality.GetValue(); - if (discriminatorValueOutputDimensionality == "PREDICT") { - if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "outputDimensionality" }, - Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); - } - } else if (discriminatorValueOutputDimensionality == "EMBED_CONTENT") { - if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "outputDimensionality" }, - Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); - } - } - - JsonNode discriminatorMimeType = - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); - string discriminatorValueMimeType = - discriminatorMimeType == null ? "PREDICT" : discriminatorMimeType.GetValue(); - if (discriminatorValueMimeType == "PREDICT") { - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[]", "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - } - - JsonNode discriminatorAutoTruncate = - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); - string discriminatorValueAutoTruncate = discriminatorAutoTruncate == null - ? "PREDICT" - : discriminatorAutoTruncate.GetValue(); - if (discriminatorValueAutoTruncate == "PREDICT") { - if (Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "autoTruncate" }, - Common.GetValueByPath(fromObject, new string[] { "autoTruncate" })); - } - } else if (discriminatorValueAutoTruncate == "EMBED_CONTENT") { - if (Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "autoTruncate" }, - Common.GetValueByPath(fromObject, new string[] { "autoTruncate" })); - } - } - - return toObject; - } - - internal JsonNode EmbedContentParametersPrivateToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "requests[]", "content" }, - Transformers.TContentsForEmbed( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "contents" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { - _ = ContentToMldev(Common.ParseToJsonNode(Transformers.TContent( - Common.GetValueByPath(fromObject, new string[] { "content" }))), - toObject, rootObject); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = EmbedContentConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - Common.SetValueByPath( - toObject, new string[] { "requests[]", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - - return toObject; - } - - internal JsonNode EmbedContentParametersPrivateToVertex(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - JsonNode discriminatorContents = - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); - string discriminatorValueContents = - discriminatorContents == null ? "PREDICT" : discriminatorContents.GetValue(); - if (discriminatorValueContents == "PREDICT") { - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "instances[]", "content" }, - Transformers.TContentsForEmbed( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "contents" }))); - } - } - - JsonNode discriminatorContent = - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); - string discriminatorValueContent = - discriminatorContent == null ? "PREDICT" : discriminatorContent.GetValue(); - if (discriminatorValueContent == "EMBED_CONTENT") { - if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "content" }, - ContentToVertex(Common.ParseToJsonNode(Transformers.TContent( - Common.GetValueByPath(fromObject, new string[] { "content" }))), - toObject, rootObject)); - } - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = EmbedContentConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode EmbedContentResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "embeddings" }) != null) { - Common.SetValueByPath(toObject, new string[] { "embeddings" }, - Common.GetValueByPath(fromObject, new string[] { "embeddings" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - return toObject; - } - - internal JsonNode EmbedContentResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "predictions[]", "embeddings" }) != - null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath( - fromObject, new string[] { "predictions[]", "embeddings" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - ContentEmbeddingFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "embeddings" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (rootObject != null && - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }) != null && - Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }) - .GetValue() == "EMBED_CONTENT") { - JsonNode? embedding_node = Common.GetValueByPath(fromObject, new string[] { "embedding" }); - if (embedding_node != null) { - JsonNode embedding = JsonNode.Parse(embedding_node.ToJsonString()); - JsonNode usageMetadata = - Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }); - JsonNode truncated = Common.GetValueByPath(fromObject, new string[] { "truncated" }); - JsonObject stats = new JsonObject(); - if (usageMetadata != null && usageMetadata["promptTokenCount"] != null) { - stats.Add("token_count", - JsonNode.Parse(usageMetadata["promptTokenCount"].ToJsonString())); - } - if (truncated != null) { - stats.Add("truncated", JsonNode.Parse(truncated.ToJsonString())); - } - ((JsonObject)embedding).Add("statistics", stats); - JsonArray embeddings = new JsonArray(); - embeddings.Add(embedding); - Common.SetValueByPath(toObject, new string[] { "embeddings" }, embeddings); - } - } - - return toObject; - } - - internal JsonNode EndpointFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "endpoint" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "endpoint" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "deployedModelId" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "deployedModelId" }, - Common.GetValueByPath(fromObject, new string[] { "deployedModelId" })); - } - - return toObject; - } - - internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileUri" }, - Common.GetValueByPath(fromObject, new string[] { "fileUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { - Common.SetValueByPath(toObject, new string[] { "id" }, - Common.GetValueByPath(fromObject, new string[] { "id" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { - Common.SetValueByPath(toObject, new string[] { "args" }, - Common.GetValueByPath(fromObject, new string[] { "args" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { - throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { - throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "allowedFunctionNames" }, - Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { - throw new NotSupportedException( - "streamFunctionCallArguments parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode FunctionDeclarationToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { - Common.SetValueByPath(toObject, new string[] { "parameters" }, - Common.GetValueByPath(fromObject, new string[] { "parameters" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parametersJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath(toObject, new string[] { "response" }, - Common.GetValueByPath(fromObject, new string[] { "response" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "behavior" }))) { - throw new NotSupportedException("behavior parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode GenerateContentConfigToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "candidateCount" }, - Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { - Common.SetValueByPath(toObject, new string[] { "stopSequences" }, - Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobs" }, - Common.GetValueByPath(fromObject, new string[] { "logprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "presencePenalty" }, - Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "frequencyPenalty" }, - Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(toObject, new string[] { "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseMimeType" }, - Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseSchema" }, - Transformers.TSchema(Common.GetValueByPath( - fromObject, new string[] { "responseSchema" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "routingConfig" }))) { - throw new NotSupportedException("routingConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }))) { - throw new NotSupportedException( - "modelSelectionConfig parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetySettings" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "safetySettings" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(SafetySettingToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(parentObject, new string[] { "safetySettings" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - var keyList = - Transformers.TTools(Common.GetValueByPath(fromObject, new string[] { "tools" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ToolToMldev(Common.ParseToJsonNode(Transformers.TTool(record)), toObject, - rootObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, - ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "toolConfig" })), - toObject, rootObject)); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { - throw new NotSupportedException("labels parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContent" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "cachedContent" }, - Transformers.TCachedContentName( - this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "cachedContent" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "speechConfig" }, - Transformers.TSpeechConfig(Common.GetValueByPath( - fromObject, new string[] { "speechConfig" }))); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }))) { - throw new NotSupportedException("audioTimestamp parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageConfig" }, - ImageConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "imageConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "enableEnhancedCivicAnswers" }, - Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" }))) { - throw new NotSupportedException( - "modelArmorConfig parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode GenerateContentConfigToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "candidateCount" }, - Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { - Common.SetValueByPath(toObject, new string[] { "stopSequences" }, - Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobs" }, - Common.GetValueByPath(fromObject, new string[] { "logprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "presencePenalty" }, - Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "frequencyPenalty" }, - Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(toObject, new string[] { "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseMimeType" }, - Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseSchema" }, - Transformers.TSchema(Common.GetValueByPath( - fromObject, new string[] { "responseSchema" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "routingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "modelConfig" }, - Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetySettings" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "safetySettings" }, - Common.GetValueByPath(fromObject, new string[] { "safetySettings" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - var keyList = - Transformers.TTools(Common.GetValueByPath(fromObject, new string[] { "tools" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ToolToVertex(Common.ParseToJsonNode(Transformers.TTool(record)), toObject, - rootObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, - ToolConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "toolConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContent" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "cachedContent" }, - Transformers.TCachedContentName( - this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "cachedContent" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "speechConfig" }, - SpeechConfigToVertex( - Common.ParseToJsonNode(Transformers.TSpeechConfig( - Common.GetValueByPath(fromObject, new string[] { "speechConfig" }))), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { - Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, - Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageConfig" }, - ImageConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "imageConfig" })), - toObject, rootObject)); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }))) { - throw new NotSupportedException( - "enableEnhancedCivicAnswers parameter is not supported in Vertex AI."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "modelArmorConfig" }, - Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" })); - } - - return toObject; - } - - internal JsonNode GenerateContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "generationConfig" }, - GenerateContentConfigToMldev(apiClient, - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "config" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerateContentParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "generationConfig" }, - GenerateContentConfigToVertex(apiClient, - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "config" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerateContentResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidates" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "candidates" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(CandidateFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "candidates" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "modelVersion" }) != null) { - Common.SetValueByPath(toObject, new string[] { "modelVersion" }, - Common.GetValueByPath(fromObject, new string[] { "modelVersion" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "promptFeedback" }) != null) { - Common.SetValueByPath(toObject, new string[] { "promptFeedback" }, - Common.GetValueByPath(fromObject, new string[] { "promptFeedback" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseId" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseId" }, - Common.GetValueByPath(fromObject, new string[] { "responseId" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "usageMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "usageMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "modelStatus" }) != null) { - Common.SetValueByPath(toObject, new string[] { "modelStatus" }, - Common.GetValueByPath(fromObject, new string[] { "modelStatus" })); - } - - return toObject; - } - - internal JsonNode GenerateContentResponseFromVertex(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidates" }) != null) { - Common.SetValueByPath(toObject, new string[] { "candidates" }, - Common.GetValueByPath(fromObject, new string[] { "candidates" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "createTime" }, - Common.GetValueByPath(fromObject, new string[] { "createTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "modelVersion" }) != null) { - Common.SetValueByPath(toObject, new string[] { "modelVersion" }, - Common.GetValueByPath(fromObject, new string[] { "modelVersion" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "promptFeedback" }) != null) { - Common.SetValueByPath(toObject, new string[] { "promptFeedback" }, - Common.GetValueByPath(fromObject, new string[] { "promptFeedback" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseId" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseId" }, - Common.GetValueByPath(fromObject, new string[] { "responseId" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "usageMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "usageMetadata" })); - } - - return toObject; - } - - internal JsonNode GenerateImagesConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }))) { - throw new NotSupportedException("outputGcsUri parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }))) { - throw new NotSupportedException("negativePrompt parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, - Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "guidanceScale" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "guidanceScale" }, - Common.GetValueByPath(fromObject, new string[] { "guidanceScale" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "seed" }))) { - throw new NotSupportedException("seed parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "safetySetting" }, - Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "includeSafetyAttributes" }, - Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "includeRaiReason" }, - Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "language" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "language" }, - Common.GetValueByPath(fromObject, new string[] { "language" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "parameters", "outputOptions", "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "addWatermark" }))) { - throw new NotSupportedException("addWatermark parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { - throw new NotSupportedException("labels parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleImageSize" }, - Common.GetValueByPath(fromObject, new string[] { "imageSize" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }))) { - throw new NotSupportedException("enhancePrompt parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode GenerateImagesConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, - Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, - Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "guidanceScale" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "guidanceScale" }, - Common.GetValueByPath(fromObject, new string[] { "guidanceScale" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "safetySetting" }, - Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "includeSafetyAttributes" }, - Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "includeRaiReason" }, - Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "language" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "language" }, - Common.GetValueByPath(fromObject, new string[] { "language" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "parameters", "outputOptions", "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "addWatermark" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "addWatermark" }, - Common.GetValueByPath(fromObject, new string[] { "addWatermark" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleImageSize" }, - Common.GetValueByPath(fromObject, new string[] { "imageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); - } - - return toObject; - } - - internal JsonNode GenerateImagesParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = GenerateImagesConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode GenerateImagesParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = GenerateImagesConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode GenerateImagesResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(GeneratedImageFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "positivePromptSafetyAttributes" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "positivePromptSafetyAttributes" }, - SafetyAttributesFromMldev( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "positivePromptSafetyAttributes" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerateImagesResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "positivePromptSafetyAttributes" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "positivePromptSafetyAttributes" }, - SafetyAttributesFromVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "positivePromptSafetyAttributes" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, - Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }))) { - throw new NotSupportedException("outputGcsUri parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fps" }))) { - throw new NotSupportedException("fps parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "durationSeconds" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "durationSeconds" }, - Common.GetValueByPath(fromObject, new string[] { "durationSeconds" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "seed" }))) { - throw new NotSupportedException("seed parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "resolution" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "resolution" }, - Common.GetValueByPath(fromObject, new string[] { "resolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "pubsubTopic" }))) { - throw new NotSupportedException("pubsubTopic parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "generateAudio" }))) { - throw new NotSupportedException("generateAudio parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "lastFrame" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "lastFrame" }, - ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "lastFrame" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "referenceImages" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "referenceImages" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(VideoGenerationReferenceImageToMldev(Common.ParseToJsonNode(record), toObject, - rootObject)); - } - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "referenceImages" }, - result); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mask" }))) { - throw new NotSupportedException("mask parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "compressionQuality" }))) { - throw new NotSupportedException( - "compressionQuality parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { - throw new NotSupportedException("labels parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode GenerateVideosConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, - Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, - Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fps" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "fps" }, - Common.GetValueByPath(fromObject, new string[] { "fps" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "durationSeconds" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "durationSeconds" }, - Common.GetValueByPath(fromObject, new string[] { "durationSeconds" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "resolution" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "resolution" }, - Common.GetValueByPath(fromObject, new string[] { "resolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pubsubTopic" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "pubsubTopic" }, - Common.GetValueByPath(fromObject, new string[] { "pubsubTopic" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "generateAudio" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "generateAudio" }, - Common.GetValueByPath(fromObject, new string[] { "generateAudio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "lastFrame" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "lastFrame" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "lastFrame" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "referenceImages" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "referenceImages" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(VideoGenerationReferenceImageToVertex(Common.ParseToJsonNode(record), toObject, - rootObject)); - } - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "referenceImages" }, - result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mask" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "instances[0]", "mask" }, - VideoGenerationMaskToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "mask" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "compressionQuality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "compressionQuality" }, - Common.GetValueByPath(fromObject, new string[] { "compressionQuality" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - return toObject; - } - - internal JsonNode GenerateVideosOperationFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response", "generateVideoResponse" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "response" }, - GenerateVideosResponseFromMldev( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "response", "generateVideoResponse" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosOperationFromVertex(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "response" }, - GenerateVideosResponseFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "response" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "image" }, - ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "video" }, - VideoToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "video" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { - _ = GenerateVideosSourceToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), - toObject, rootObject); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = GenerateVideosConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode GenerateVideosParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "video" }, - VideoToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "video" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { - _ = GenerateVideosSourceToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), - toObject, rootObject); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = GenerateVideosConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode GenerateVideosResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(GeneratedVideoFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredCount" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredReasons" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); - } - - return toObject; - } - - internal JsonNode GenerateVideosResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "videos" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "videos" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - GeneratedVideoFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredCount" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredReasons" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); - } - - return toObject; - } - - internal JsonNode GenerateVideosSourceToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "image" }, - ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "video" }, - VideoToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "video" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosSourceToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "video" }, - VideoToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "video" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GeneratedImageFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "image" }, - ImageFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiFilteredReason" }, - Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "safetyAttributes" }, - SafetyAttributesFromMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GeneratedImageFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "image" }, - ImageFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiFilteredReason" }, - Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "safetyAttributes" }, - SafetyAttributesFromVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(toObject, new string[] { "enhancedPrompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - return toObject; - } - - internal JsonNode GeneratedImageMaskFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mask" }, - ImageFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(toObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - return toObject; - } - - internal JsonNode GeneratedVideoFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { - Common.SetValueByPath(toObject, new string[] { "video" }, - VideoFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "video" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GeneratedVideoFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "video" }, - VideoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode GenerationConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "modelConfig" }, - Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { - Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, - Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "candidateCount" }, - Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "enableAffectiveDialog" }, - Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "frequencyPenalty" }, - Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobs" }, - Common.GetValueByPath(fromObject, new string[] { "logprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "presencePenalty" }, - Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseMimeType" }, - Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "routingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(toObject, new string[] { "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "speechConfig" }, - SpeechConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "speechConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { - Common.SetValueByPath(toObject, new string[] { "stopSequences" }, - Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }))) { - throw new NotSupportedException( - "enableEnhancedCivicAnswers parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode GetModelParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - return toObject; - } - - internal JsonNode GetModelParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - return toObject; - } - - internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "authConfig" }, - AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "authConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { - Common.SetValueByPath(toObject, new string[] { "enableWidget" }, - Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { - Common.SetValueByPath(toObject, new string[] { "searchTypes" }, - Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { - throw new NotSupportedException( - "blockingConfidence parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { - throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "timeRangeFilter" }, - Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); - } - - return toObject; - } - - internal JsonNode ImageConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(toObject, new string[] { "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageSize" }, - Common.GetValueByPath(fromObject, new string[] { "imageSize" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "personGeneration" }))) { - throw new NotSupportedException( - "personGeneration parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "prominentPeople" }))) { - throw new NotSupportedException( - "prominentPeople parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }))) { - throw new NotSupportedException("outputMimeType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }))) { - throw new NotSupportedException( - "outputCompressionQuality parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" }))) { - throw new NotSupportedException( - "imageOutputOptions parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode ImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { - Common.SetValueByPath(toObject, new string[] { "aspectRatio" }, - Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageSize" }, - Common.GetValueByPath(fromObject, new string[] { "imageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prominentPeople" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "prominentPeople" }, - Common.GetValueByPath(fromObject, new string[] { "prominentPeople" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageOutputOptions", "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "imageOutputOptions", "compressionQuality" }, - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "imageOutputOptions" }, - Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" })); - } - - return toObject; - } - - internal JsonNode ImageFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "bytesBase64Encoded" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode ImageFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "gcsUri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { - Common.SetValueByPath(toObject, new string[] { "imageBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "bytesBase64Encoded" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode ImageToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "gcsUri" }))) { - throw new NotSupportedException("gcsUri parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageBytes" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "bytesBase64Encoded" }, - Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "imageBytes" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode ImageToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "gcsUri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imageBytes" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "bytesBase64Encoded" }, - Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "imageBytes" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode ListModelsConfigToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, - Common.GetValueByPath(fromObject, new string[] { "filter" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "queryBase" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "_url", "models_url" }, - Transformers.TModelsUrl( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "queryBase" }))); - } - - return toObject; - } - - internal JsonNode ListModelsConfigToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, - Common.GetValueByPath(fromObject, new string[] { "filter" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "queryBase" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "_url", "models_url" }, - Transformers.TModelsUrl( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "queryBase" }))); - } - - return toObject; - } - - internal JsonNode ListModelsParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListModelsConfigToMldev( - apiClient, - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode ListModelsParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListModelsConfigToVertex( - apiClient, - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode ListModelsResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - JsonArray keyArray = (JsonArray)Transformers.TExtractModels( - Common.GetValueByPath(fromObject, new string[] { "_self" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ModelFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "models" }, result); - } - - return toObject; - } - - internal JsonNode ListModelsResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - JsonArray keyArray = (JsonArray)Transformers.TExtractModels( - Common.GetValueByPath(fromObject, new string[] { "_self" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ModelFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "models" }, result); - } - - return toObject; - } - - internal JsonNode MaskReferenceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "maskMode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "maskMode" }, - Common.GetValueByPath(fromObject, new string[] { "maskMode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "segmentationClasses" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maskClasses" }, - Common.GetValueByPath(fromObject, new string[] { "segmentationClasses" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maskDilation" }) != null) { - Common.SetValueByPath(toObject, new string[] { "dilation" }, - Common.GetValueByPath(fromObject, new string[] { "maskDilation" })); - } - - return toObject; - } - - internal JsonNode ModelFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "version" }) != null) { - Common.SetValueByPath(toObject, new string[] { "version" }, - Common.GetValueByPath(fromObject, new string[] { "version" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tunedModelInfo" }, - Common.GetValueByPath(fromObject, new string[] { "_self" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inputTokenLimit" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "inputTokenLimit" }, - Common.GetValueByPath(fromObject, new string[] { "inputTokenLimit" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputTokenLimit" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "outputTokenLimit" }, - Common.GetValueByPath(fromObject, new string[] { "outputTokenLimit" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "supportedGenerationMethods" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "supportedActions" }, - Common.GetValueByPath(fromObject, new string[] { "supportedGenerationMethods" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxTemperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "maxTemperature" }, - Common.GetValueByPath(fromObject, new string[] { "maxTemperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thinking" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thinking" }, - Common.GetValueByPath(fromObject, new string[] { "thinking" })); - } - - return toObject; - } - - internal JsonNode ModelFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "versionId" }) != null) { - Common.SetValueByPath(toObject, new string[] { "version" }, - Common.GetValueByPath(fromObject, new string[] { "versionId" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "deployedModels" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "deployedModels" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(EndpointFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "endpoints" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(toObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tunedModelInfo" }, - TunedModelInfoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "defaultCheckpointId" }, - Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "checkpoints" }) != null) { - Common.SetValueByPath(toObject, new string[] { "checkpoints" }, - Common.GetValueByPath(fromObject, new string[] { "checkpoints" })); - } - - return toObject; - } - - internal JsonNode MultiSpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - SpeakerVoiceConfigToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); - } - - return toObject; - } - - internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileData" }, - FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "fileData" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCall" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "inlineData" }, - BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "inlineData" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolCall" }, - Common.GetValueByPath(fromObject, new string[] { "toolCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolResponse" }, - Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "partMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); - } - - return toObject; - } - - internal JsonNode PartToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileData" }, - Common.GetValueByPath(fromObject, new string[] { "fileData" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - Common.GetValueByPath(fromObject, new string[] { "functionCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "inlineData" }, - Common.GetValueByPath(fromObject, new string[] { "inlineData" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolCall" }))) { - throw new NotSupportedException("toolCall parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolResponse" }))) { - throw new NotSupportedException("toolResponse parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partMetadata" }))) { - throw new NotSupportedException("partMetadata parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode ProductImageToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "productImage" }) != null) { - Common.SetValueByPath(toObject, new string[] { "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "productImage" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode RecontextImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, - Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "baseSteps" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "baseSteps" }, - Common.GetValueByPath(fromObject, new string[] { "baseSteps" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, - Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "safetySetting" }, - Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "addWatermark" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "addWatermark" }, - Common.GetValueByPath(fromObject, new string[] { "addWatermark" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "parameters", "outputOptions", "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, - Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - return toObject; - } - - internal JsonNode RecontextImageParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { - _ = RecontextImageSourceToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), - toObject, rootObject); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = RecontextImageConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode RecontextImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); - } - - return toObject; - } - - internal JsonNode RecontextImageSourceToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personImage" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "personImage", "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "personImage" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "productImages" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "productImages" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ProductImageToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "productImages" }, - result); - } - - return toObject; - } - - internal JsonNode ReferenceImageAPIToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "referenceImage" }) != null) { - Common.SetValueByPath(toObject, new string[] { "referenceImage" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "referenceImage" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "referenceId" }) != null) { - Common.SetValueByPath(toObject, new string[] { "referenceId" }, - Common.GetValueByPath(fromObject, new string[] { "referenceId" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "referenceType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "referenceType" }, - Common.GetValueByPath(fromObject, new string[] { "referenceType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maskImageConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maskImageConfig" }, - MaskReferenceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "maskImageConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "controlImageConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "controlImageConfig" }, - ControlReferenceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "controlImageConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "styleImageConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "styleImageConfig" }, - Common.GetValueByPath(fromObject, new string[] { "styleImageConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "subjectImageConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "subjectImageConfig" }, - Common.GetValueByPath(fromObject, new string[] { "subjectImageConfig" })); - } - - return toObject; - } - - internal JsonNode ReplicatedVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "voiceSampleAudio" }, - Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" })); - } - - return toObject; - } - - internal JsonNode SafetyAttributesFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "categories" }, - Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "scores" }, - Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contentType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "contentType" }, - Common.GetValueByPath(fromObject, new string[] { "contentType" })); - } - - return toObject; - } - - internal JsonNode SafetyAttributesFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "categories" }, - Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "scores" }, - Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contentType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "contentType" }, - Common.GetValueByPath(fromObject, new string[] { "contentType" })); - } - - return toObject; - } - - internal JsonNode SafetySettingToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "category" }) != null) { - Common.SetValueByPath(toObject, new string[] { "category" }, - Common.GetValueByPath(fromObject, new string[] { "category" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "method" }))) { - throw new NotSupportedException("method parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "threshold" }) != null) { - Common.SetValueByPath(toObject, new string[] { "threshold" }, - Common.GetValueByPath(fromObject, new string[] { "threshold" })); - } - - return toObject; - } - - internal JsonNode ScribbleImageToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(toObject, new string[] { "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode SegmentImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxPredictions" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "maxPredictions" }, - Common.GetValueByPath(fromObject, new string[] { "maxPredictions" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "confidenceThreshold" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "confidenceThreshold" }, - Common.GetValueByPath(fromObject, new string[] { "confidenceThreshold" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maskDilation" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "maskDilation" }, - Common.GetValueByPath(fromObject, new string[] { "maskDilation" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "binaryColorThreshold" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "binaryColorThreshold" }, - Common.GetValueByPath(fromObject, new string[] { "binaryColorThreshold" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - return toObject; - } - - internal JsonNode SegmentImageParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { - _ = SegmentImageSourceToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), - toObject, rootObject); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = SegmentImageConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode SegmentImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - GeneratedImageMaskFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedMasks" }, result); - } - - return toObject; - } - - internal JsonNode SegmentImageSourceToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, - Common.GetValueByPath(fromObject, new string[] { "prompt" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "instances[0]", "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "scribbleImage" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "instances[0]", "scribble" }, - ScribbleImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "scribbleImage" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode SpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { - Common.SetValueByPath(toObject, new string[] { "speaker" }, - Common.GetValueByPath(fromObject, new string[] { "speaker" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, - VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode SpeechConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, - VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "languageCode" }, - Common.GetValueByPath(fromObject, new string[] { "languageCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, - MultiSpeakerVoiceConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "multiSpeakerVoiceConfig" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "retrievalConfig" }, - Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionCallingConfig" }, - FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCallingConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "includeServerSideToolInvocations" }, - Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); - } - - return toObject; - } - - internal JsonNode ToolConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "retrievalConfig" }, - Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionCallingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" })); - } - - if (!Common.IsZero(Common.GetValueByPath( - fromObject, new string[] { "includeServerSideToolInvocations" }))) { - throw new NotSupportedException( - "includeServerSideToolInvocations parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { - throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "computerUse" }, - Common.GetValueByPath(fromObject, new string[] { "computerUse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileSearch" }, - Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearch" }, - GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleSearch" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleMaps" }, - GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleMaps" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { - throw new NotSupportedException( - "enterpriseWebSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionDeclarations" }, - Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearchRetrieval" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { - throw new NotSupportedException( - "parallelAiSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath(toObject, new string[] { "urlContext" }, - Common.GetValueByPath(fromObject, new string[] { "urlContext" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mcpServers" }, - Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); - } - - return toObject; - } - - internal JsonNode ToolToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrieval" }) != null) { - Common.SetValueByPath(toObject, new string[] { "retrieval" }, - Common.GetValueByPath(fromObject, new string[] { "retrieval" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "computerUse" }, - Common.GetValueByPath(fromObject, new string[] { "computerUse" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileSearch" }))) { - throw new NotSupportedException("fileSearch parameter is not supported in Vertex AI."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearch" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleMaps" }, - Common.GetValueByPath(fromObject, new string[] { "googleMaps" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "enterpriseWebSearch" }, - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - FunctionDeclarationToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearchRetrieval" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parallelAiSearch" }, - Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath(toObject, new string[] { "urlContext" }, - Common.GetValueByPath(fromObject, new string[] { "urlContext" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mcpServers" }))) { - throw new NotSupportedException("mcpServers parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode TunedModelInfoFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath( - fromObject, new string[] { "labels", "google-vertex-llm-tuning-base-model-id" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "baseModel" }, - Common.GetValueByPath( - fromObject, new string[] { "labels", "google-vertex-llm-tuning-base-model-id" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "createTime" }, - Common.GetValueByPath(fromObject, new string[] { "createTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "updateTime" }, - Common.GetValueByPath(fromObject, new string[] { "updateTime" })); - } - - return toObject; - } - - internal JsonNode UpdateModelConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "defaultCheckpointId" }, - Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" })); - } - - return toObject; - } - - internal JsonNode UpdateModelConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "defaultCheckpointId" }, - Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" })); - } - - return toObject; - } - - internal JsonNode UpdateModelParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = UpdateModelConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode UpdateModelParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = UpdateModelConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode UpscaleImageAPIConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, - Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "safetySetting" }, - Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "personGeneration" }, - Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "includeRaiReason" }, - Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "parameters", "outputOptions", "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, - Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enhanceInputImage" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "upscaleConfig", "enhanceInputImage" }, - Common.GetValueByPath(fromObject, new string[] { "enhanceInputImage" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "imagePreservationFactor" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "parameters", "upscaleConfig", "imagePreservationFactor" }, - Common.GetValueByPath(fromObject, new string[] { "imagePreservationFactor" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, - Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "parameters", "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - return toObject; - } - - internal JsonNode UpscaleImageAPIParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(toObject, new string[] { "instances[0]", "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "upscaleFactor" }) != null) { - Common.SetValueByPath(toObject, - new string[] { "parameters", "upscaleConfig", "upscaleFactor" }, - Common.GetValueByPath(fromObject, new string[] { "upscaleFactor" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = UpscaleImageAPIConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode UpscaleImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); - } - - return toObject; - } - - internal JsonNode VideoFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uri" }, - Common.GetValueByPath(fromObject, new string[] { "uri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encodedVideo" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "encodedVideo" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encoding" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "encoding" })); - } - - return toObject; - } - - internal JsonNode VideoFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "bytesBase64Encoded" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode VideoGenerationMaskToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_self" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maskMode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "maskMode" }, - Common.GetValueByPath(fromObject, new string[] { "maskMode" })); - } - - return toObject; - } - - internal JsonNode VideoGenerationReferenceImageToMldev(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(toObject, new string[] { "image" }, - ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "referenceType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "referenceType" }, - Common.GetValueByPath(fromObject, new string[] { "referenceType" })); - } - - return toObject; - } - - internal JsonNode VideoGenerationReferenceImageToVertex(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { - Common.SetValueByPath(toObject, new string[] { "image" }, - ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "image" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "referenceType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "referenceType" }, - Common.GetValueByPath(fromObject, new string[] { "referenceType" })); - } - - return toObject; - } - - internal JsonNode VideoToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uri" }, - Common.GetValueByPath(fromObject, new string[] { "uri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoBytes" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "encodedVideo" }, - Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "videoBytes" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "encoding" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode VideoToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "gcsUri" }, - Common.GetValueByPath(fromObject, new string[] { "uri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoBytes" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "bytesBase64Encoded" }, - Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "videoBytes" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode VoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "replicatedVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "replicatedVoiceConfig" }, - ReplicatedVoiceConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "replicatedVoiceConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "prebuiltVoiceConfig" }, - Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" })); - } - - return toObject; - } - - public Models(ApiClient apiClient) { - _apiClient = apiClient; - } - - private async Task PrivateGenerateContentAsync( - string model, List contents, GenerateContentConfig? config, - CancellationToken cancellationToken = default) { - GenerateContentParameters parameter = new GenerateContentParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(contents)) { - parameter.Contents = contents; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GenerateContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GenerateContentParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:generateContent", body["_url"]); - } else { - body = GenerateContentParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:generateContent", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - GenerateContentResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - GenerateContentResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - private async IAsyncEnumerable PrivateGenerateContentStreamAsync( - string model, List contents, GenerateContentConfig? config, - [EnumeratorCancellation] CancellationToken cancellationToken = default) { - GenerateContentParameters parameter = new GenerateContentParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(contents)) { - parameter.Contents = contents; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GenerateContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GenerateContentParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:streamGenerateContent?alt=sse", body["_url"]); - } else { - body = GenerateContentParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:streamGenerateContent?alt=sse", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - await foreach (ApiResponse apiResponse in this._apiClient.RequestStreamAsync( - HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, - cancellationToken)) { -#if NETSTANDARD2_0 - string chunkJson = await apiResponse.GetEntity().ReadAsStringAsync(); -#else - string chunkJson = await apiResponse.GetEntity().ReadAsStringAsync(cancellationToken); -#endif - JsonNode? chunkNode = JsonNode.Parse(chunkJson); - if (chunkNode == null) - continue; - JsonNode responseNode; - if (this._apiClient.VertexAI) { - responseNode = - GenerateContentResponseFromVertex(chunkNode, new JsonObject(), parameterNode); - } else { - responseNode = - GenerateContentResponseFromMldev(chunkNode, new JsonObject(), parameterNode); - } - var chunkResponse = responseNode.Deserialize(); - yield return chunkResponse; - } - } - - /// - /// Calculates embeddings for the given contents. - /// - /// The model to use for embedding. - /// A to calculate embeddings for. - /// A to calculate embeddings for. - /// The to use. - /// An instance that specifies the - /// optional configurations. - /// A to cancel the - /// operation. A that represents the - /// asynchronous operation. The task result contains a - /// instance with embeddings and other metadata. - - private async Task PrivateEmbedContentAsync( - string model, List? contents, Content? content, EmbeddingApiType? embeddingApiType, - EmbedContentConfig? config, CancellationToken cancellationToken = default) { - EmbedContentParametersPrivate parameter = new EmbedContentParametersPrivate(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(contents)) { - parameter.Contents = contents; - } - if (!Common.IsZero(content)) { - parameter.Content = content; - } - if (!Common.IsZero(embeddingApiType)) { - parameter.EmbeddingApiType = embeddingApiType; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse EmbedContentParametersPrivate to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = EmbedContentParametersPrivateToVertex(this._apiClient, parameterNode, - new JsonObject(), parameterNode); - string endpointUrl = - Transformers.TIsVertexEmbedContentModel(parameterNode["model"].ToString()) - ? "{model}:embedContent" - : "{model}:predict"; - path = Common.FormatMap(endpointUrl, body["_url"]); - } else { - body = EmbedContentParametersPrivateToMldev(this._apiClient, parameterNode, - new JsonObject(), parameterNode); - path = Common.FormatMap("{model}:batchEmbedContents", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - EmbedContentResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - EmbedContentResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - private async Task PrivateGenerateImagesAsync( - string model, string prompt, GenerateImagesConfig? config, - CancellationToken cancellationToken = default) { - GenerateImagesParameters parameter = new GenerateImagesParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(prompt)) { - parameter.Prompt = prompt; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GenerateImagesParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GenerateImagesParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predict", body["_url"]); - } else { - body = GenerateImagesParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predict", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - GenerateImagesResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - GenerateImagesResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - private async Task PrivateEditImageAsync( - string model, string prompt, List referenceImages, - EditImageConfig? config, CancellationToken cancellationToken = default) { - EditImageParameters parameter = new EditImageParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(prompt)) { - parameter.Prompt = prompt; - } - if (!Common.IsZero(referenceImages)) { - parameter.ReferenceImages = referenceImages; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse EditImageParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = EditImageParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predict", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - EditImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateUpscaleImageAsync( - string model, Image image, string upscaleFactor, UpscaleImageAPIConfig? config, - CancellationToken cancellationToken = default) { - UpscaleImageAPIParameters parameter = new UpscaleImageAPIParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(image)) { - parameter.Image = image; - } - if (!Common.IsZero(upscaleFactor)) { - parameter.UpscaleFactor = upscaleFactor; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse UpscaleImageAPIParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = UpscaleImageAPIParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predict", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - UpscaleImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task RecontextImageAsync( - string model, RecontextImageSource source, RecontextImageConfig? config = null, - CancellationToken cancellationToken = default) { - RecontextImageParameters parameter = new RecontextImageParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(source)) { - parameter.Source = source; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse RecontextImageParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = RecontextImageParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predict", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - RecontextImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task SegmentImageAsync( - string model, SegmentImageSource source, SegmentImageConfig? config = null, - CancellationToken cancellationToken = default) { - SegmentImageParameters parameter = new SegmentImageParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(source)) { - parameter.Source = source; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse SegmentImageParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = SegmentImageParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predict", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - SegmentImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Retrieves a specific model resource by its name. - /// - - public async Task GetAsync(string model, GetModelConfig? config = null, - CancellationToken cancellationToken = default) { - GetModelParameters parameter = new GetModelParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetModelParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GetModelParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = GetModelParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = ModelFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = ModelFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateListAsync( - ListModelsConfig? config, CancellationToken cancellationToken = default) { - ListModelsParameters parameter = new ListModelsParameters(); - - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse ListModelsParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = ListModelsParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{models_url}", body["_url"]); - } else { - body = ListModelsParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{models_url}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - ListModelsResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - ListModelsResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - /// - /// Updates a specific model resource. - /// - /// The model to update. - /// An instance that specifies parameters - /// to update. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains the updated - /// instance. - - public async Task UpdateAsync(string model, UpdateModelConfig? config = null, - CancellationToken cancellationToken = default) { - UpdateModelParameters parameter = new UpdateModelParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse UpdateModelParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = UpdateModelParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}", body["_url"]); - } else { - body = UpdateModelParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = ModelFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = ModelFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - /// - /// Deletes a specific model resource by its name. - /// - - public async Task DeleteAsync( - string model, DeleteModelConfig? config = null, - CancellationToken cancellationToken = default) { - DeleteModelParameters parameter = new DeleteModelParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse DeleteModelParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = DeleteModelParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = DeleteModelParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - DeleteModelResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - DeleteModelResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Counts the number of tokens in the provided content. - /// - /// The name of the GenAI model to use for token counting. - /// A to count tokens for. - /// A instance that specifies the - /// optional configurations. - /// A to cancel the - /// operation. A that represents the - /// asynchronous operation. The task result contains a - /// instance with the total token count and other metadata. - - public async Task CountTokensAsync( - string model, List contents, CountTokensConfig? config = null, - CancellationToken cancellationToken = default) { - CountTokensParameters parameter = new CountTokensParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(contents)) { - parameter.Contents = contents; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse CountTokensParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = CountTokensParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:countTokens", body["_url"]); - } else { - body = CountTokensParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:countTokens", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - CountTokensResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - CountTokensResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Computes the number of tokens for the given content. - /// - /// The name of the GenAI model to use for token computation. - /// A to compute tokens for. - /// A instance that specifies the - /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains a instance with token - /// information. Thrown when called with a - /// non-Vertex AI client. - - public async Task ComputeTokensAsync( - string model, List contents, ComputeTokensConfig? config = null, - CancellationToken cancellationToken = default) { - ComputeTokensParameters parameter = new ComputeTokensParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(contents)) { - parameter.Contents = contents; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse ComputeTokensParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = ComputeTokensParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:computeTokens", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - ComputeTokensResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - private async Task PrivateGenerateVideosAsync( - string model, string? prompt, Image? image, Video? video, GenerateVideosSource? source, - GenerateVideosConfig? config, CancellationToken cancellationToken = default) { - GenerateVideosParameters parameter = new GenerateVideosParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(prompt)) { - parameter.Prompt = prompt; - } - if (!Common.IsZero(image)) { - parameter.Image = image; - } - if (!Common.IsZero(video)) { - parameter.Video = video; - } - if (!Common.IsZero(source)) { - parameter.Source = source; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GenerateVideosParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GenerateVideosParametersToVertex(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predictLongRunning", body["_url"]); - } else { - body = GenerateVideosParametersToMldev(this._apiClient, parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("{model}:predictLongRunning", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - GenerateVideosOperationFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - GenerateVideosOperationFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Generates content given a GenAI model and a list of content. - /// - /// The name of the GenAI model to use for generation. - /// A to send to the generative - /// model. A instance that - /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains a instance with response contents and - /// other metadata. - public async Task GenerateContentAsync( - string model, List contents, GenerateContentConfig? config = null, - CancellationToken cancellationToken = default) { - return await PrivateGenerateContentAsync(model, contents, config, cancellationToken); - } - - /// - /// Generates content given a GenAI model and a single content item. - /// - /// The name of the GenAI model to use for generation. - /// A instance to send to the generative - /// model. A instance that - /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains a instance with response contents and - /// other metadata. - public async Task GenerateContentAsync( - string model, Content contents, GenerateContentConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await GenerateContentAsync(model, contentList, config, cancellationToken); - } - - /// - /// Generates content given a GenAI model and a text string. - /// - /// The name of the GenAI model to use for generation. - /// A text string to send to the generative model. - /// A instance that specifies the - /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains a instance with response contents and - /// other metadata. - public async Task GenerateContentAsync( - string model, string contents, GenerateContentConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await GenerateContentAsync(model, contentList, config, cancellationToken); - } - - /// - /// Generates content with streaming responses given a GenAI model and a list of content. - /// - /// The name of the GenAI model to use for generation. - /// A to send to the generative - /// model. A instance that - /// specifies the optional configurations. A to cancel the operation. An async enumerable of - /// chunks. - public async IAsyncEnumerable GenerateContentStreamAsync( - string model, List contents, GenerateContentConfig? config = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default) { - await foreach (var response in PrivateGenerateContentStreamAsync(model, contents, config, - cancellationToken)) { - yield return response; - } - } - - /// - /// Generates content with streaming responses given a GenAI model and a single content item. - /// - /// The name of the GenAI model to use for generation. - /// A instance to send to the generative - /// model. A instance that - /// specifies the optional configurations. A to cancel the operation. An async enumerable of - /// chunks. - public async IAsyncEnumerable GenerateContentStreamAsync( - string model, Content contents, GenerateContentConfig? config = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - await foreach (var response in GenerateContentStreamAsync(model, contentList, config, - cancellationToken)) { - yield return response; - } - } - - /// - /// Generates content with streaming responses given a GenAI model and a text string. - /// - /// The name of the GenAI model to use for generation. - /// A text string to send to the generative model. - /// A instance that specifies the - /// optional configurations. A to cancel the operation. An async enumerable of - /// chunks. - public async IAsyncEnumerable GenerateContentStreamAsync( - string model, string contents, GenerateContentConfig? config = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - await foreach (var response in GenerateContentStreamAsync(model, contentList, config, - cancellationToken)) { - yield return response; - } - } - - /// - /// Generates images given a GenAI model and a prompt. - /// - /// The name of the GenAI model to use for generation. - /// A text prompt string to send to the generative model. - /// A instance that specifies the - /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains a instance with response contents and - /// other metadata. - public async Task GenerateImagesAsync( - string model, string prompt, GenerateImagesConfig? config = null, - CancellationToken cancellationToken = default) { - GenerateImagesResponse apiResponse = - await PrivateGenerateImagesAsync(model, prompt, config, cancellationToken); - SafetyAttributes? positivePromptSafetyAttributes = null; - List generatedImages = new List(); - - if (apiResponse.GeneratedImages != null) { - foreach (var generatedImage in apiResponse.GeneratedImages) { - if (generatedImage.SafetyAttributes?.ContentType == "Positive Prompt") { - positivePromptSafetyAttributes = generatedImage.SafetyAttributes; - } else { - generatedImages.Add(generatedImage); - } - } - } - - var response = new GenerateImagesResponse { GeneratedImages = generatedImages }; - - if (positivePromptSafetyAttributes != null) { - response.PositivePromptSafetyAttributes = positivePromptSafetyAttributes; - } - - return response; - } - - public async Task EditImageAsync( - String model, String prompt, List referenceImages, - EditImageConfig? config = null, CancellationToken cancellationToken = default) { - List referenceImagesAPI = - referenceImages.Select(i => ((IReferenceImageInternal)i).ToReferenceImageAPI()).ToList(); - - return await PrivateEditImageAsync(model, prompt, referenceImagesAPI, config, - cancellationToken); - } - - public async Task UpscaleImageAsync( - String model, Image image, String upscaleFactor, UpscaleImageConfig? config = null, - CancellationToken cancellationToken = default) { - UpscaleImageAPIConfig apiConfig = new UpscaleImageAPIConfig { - Mode = "upscale", - NumberOfImages = 1, - }; - - if (config != null) { - apiConfig.OutputGcsUri = config.OutputGcsUri; - apiConfig.OutputMimeType = config.OutputMimeType; - apiConfig.OutputCompressionQuality = config.OutputCompressionQuality; - apiConfig.SafetyFilterLevel = config.SafetyFilterLevel; - apiConfig.PersonGeneration = config.PersonGeneration; - apiConfig.IncludeRaiReason = config.IncludeRaiReason; - apiConfig.EnhanceInputImage = config.EnhanceInputImage; - apiConfig.ImagePreservationFactor = config.ImagePreservationFactor; - apiConfig.Labels = config.Labels; - } - - return await PrivateUpscaleImageAsync(model, image, upscaleFactor, apiConfig, - cancellationToken); - } - - /// - /// Lists models asynchronously. - /// - /// A instance that specifies the - /// optional configuration for the list request. - /// A to cancel the - /// operation. A Pager object that contains one page of models. When iterating - /// over the pager, it automatically fetches the next page if there are more. - public async Task> ListAsync( - ListModelsConfig? config = null, CancellationToken cancellationToken = default) { - config ??= new ListModelsConfig(); - - if (!config.QueryBase.HasValue) { - config.QueryBase = true; - } - - if (this._apiClient.VertexAI) { - if (!config.QueryBase.Value) { - if (!String.IsNullOrEmpty(config.Filter)) { - throw new NotSupportedException( - "Filtering tuned models list for Vertex AI is not currently supported"); - } - } - } - - var initialResponse = await PrivateListAsync(config, cancellationToken); - - return new Pager( - requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), - extractItems: response => response.Models, - extractNextPageToken: response => response.NextPageToken, - extractHttpResponse: response => response.SdkHttpResponse, - updateConfigPageToken: (cfg, token) => { - cfg.PageToken = token; - return cfg; - }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); - } - - /// - /// Counts the number of tokens in the provided content. - /// - /// The name of the GenAI model to use for token counting. - /// A to count tokens for. - /// A instance that specifies the - /// optional configurations. - /// A to cancel the - /// operation. A that represents the - /// asynchronous operation. The task result contains a - /// instance with the total token count and other metadata. - public async Task CountTokensAsync( - String model, Content contents, CountTokensConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await CountTokensAsync(model, contentList, config, cancellationToken); - } - - /// - /// Counts the number of tokens in the provided content. - /// - /// The name of the GenAI model to use for token counting. - /// A text string to send to count tokens for. - /// A instance that specifies the - /// optional configurations. - /// A to cancel the - /// operation. A that represents the - /// asynchronous operation. The task result contains a - /// instance with the total token count and other metadata. - public async Task CountTokensAsync( - String model, String contents, CountTokensConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await CountTokensAsync(model, contentList, config, cancellationToken); - } - - /// - /// Computes the number of tokens for a single content item. - /// - /// The name of the GenAI model to use for token computation. - /// A instance to compute tokens for. - /// A instance that specifies the - /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains a instance with token - /// information. Thrown when called with a - /// non-Vertex AI client. - public async Task ComputeTokensAsync( - String model, Content contents, ComputeTokensConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await ComputeTokensAsync(model, contentList, config, cancellationToken); - } - - /// - /// Computes the number of tokens for a text string. - /// - /// The name of the GenAI model to use for token computation. - /// A string of text to compute tokens for. - /// A instance that specifies the - /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains a instance with token - /// information. Thrown when called with a - /// non-Vertex AI client. - public async Task ComputeTokensAsync( - String model, String contents, ComputeTokensConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await ComputeTokensAsync(model, contentList, config, cancellationToken); - } - - /// - /// Calculates embeddings for the given content. - /// - /// The model to use. - /// The content to embed. - /// Optional configuration for embeddings. - /// A to cancel the - /// operation. A that represents the - /// asynchronous operation. - public async Task EmbedContentAsync( - string model, Content contents, EmbedContentConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await EmbedContentAsync(model, contentList, config, cancellationToken); - } - - /// - /// Calculates embeddings for the given text string. - /// - /// The model to use. - /// The text string to embed. - /// Optional configuration for embeddings. - /// A to cancel the - /// operation. A that represents the - /// asynchronous operation. - public async Task EmbedContentAsync( - string model, String contents, EmbedContentConfig? config = null, - CancellationToken cancellationToken = default) { - List contentList = Transformers.TContents(contents) ?? new List(); - return await EmbedContentAsync(model, contentList, config, cancellationToken); - } - - public async Task GenerateVideosAsync( - String model, GenerateVideosSource source, GenerateVideosConfig? config = null, - CancellationToken cancellationToken = default) { - return await PrivateGenerateVideosAsync(model, null, null, null, source, config, - cancellationToken); - } - - /// - /// Calculates embeddings for the given content. - /// - /// The model to use. - /// The content to embed. - /// Optional configuration for embeddings. - /// A to cancel the - /// operation. A that represents the - /// asynchronous operation. - public async Task EmbedContentAsync( - string model, List contents, EmbedContentConfig? config = null, - CancellationToken cancellationToken = default) { - if (!_apiClient.VertexAI) { - return await PrivateEmbedContentAsync(model, contents, null, null, config, - cancellationToken); - } - if (Transformers.TIsVertexEmbedContentModel(model)) { - if (contents.Count > 1) { - throw new ArgumentException( - "The embedContent API for this model only supports one content at a time."); - } - return await PrivateEmbedContentAsync( - model, contents, contents[0], EmbeddingApiType.EmbedContent, config, cancellationToken); - } else { - return await PrivateEmbedContentAsync(model, contents, null, EmbeddingApiType.Predict, - config, cancellationToken); - } - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + + /// + /// Provides methods to interact with the Gen AI models. This class is not intended to be + /// instantiated directly, instead it should be accessed through the class. + /// + + public sealed class Models { + private readonly ApiClient _apiClient; + + internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { + Common.SetValueByPath(toObject, new string[] { "apiKey" }, + Common.GetValueByPath(fromObject, new string[] { "apiKey" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { + throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { + throw new NotSupportedException("authType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { + throw new NotSupportedException( + "googleServiceAccountConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { + throw new NotSupportedException( + "httpBasicAuthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { + throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { + throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { + Common.SetValueByPath(toObject, new string[] { "data" }, + Common.GetValueByPath(fromObject, new string[] { "data" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode CandidateFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { + Common.SetValueByPath(toObject, new string[] { "content" }, + Common.GetValueByPath(fromObject, new string[] { "content" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "citationMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "citationMetadata" }, + CitationMetadataFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "citationMetadata" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tokenCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tokenCount" }, + Common.GetValueByPath(fromObject, new string[] { "tokenCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "finishReason" }) != null) { + Common.SetValueByPath(toObject, new string[] { "finishReason" }, + Common.GetValueByPath(fromObject, new string[] { "finishReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "groundingMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "groundingMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "avgLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "avgLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "index" }) != null) { + Common.SetValueByPath(toObject, new string[] { "index" }, + Common.GetValueByPath(fromObject, new string[] { "index" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobsResult" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobsResult" }, + Common.GetValueByPath(fromObject, new string[] { "logprobsResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyRatings" }) != null) { + Common.SetValueByPath(toObject, new string[] { "safetyRatings" }, + Common.GetValueByPath(fromObject, new string[] { "safetyRatings" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "urlContextMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "urlContextMetadata" })); + } + + return toObject; + } + + internal JsonNode CitationMetadataFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "citationSources" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "citations" }, + Common.GetValueByPath(fromObject, new string[] { "citationSources" })); + } + + return toObject; + } + + internal JsonNode ComputeTokensParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "contents" }, result); + } + + return toObject; + } + + internal JsonNode ComputeTokensResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tokensInfo" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tokensInfo" }, + Common.GetValueByPath(fromObject, new string[] { "tokensInfo" })); + } + + return toObject; + } + + internal JsonNode ContentEmbeddingFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "values" }) != null) { + Common.SetValueByPath(toObject, new string[] { "values" }, + Common.GetValueByPath(fromObject, new string[] { "values" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "statistics" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "statistics" }, + ContentEmbeddingStatisticsFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "statistics" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode ContentEmbeddingStatisticsFromVertex(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "truncated" }) != null) { + Common.SetValueByPath(toObject, new string[] { "truncated" }, + Common.GetValueByPath(fromObject, new string[] { "truncated" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "token_count" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tokenCount" }, + Common.GetValueByPath(fromObject, new string[] { "token_count" })); + } + + return toObject; + } + + internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode ContentToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode ControlReferenceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "controlType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "controlType" }, + Common.GetValueByPath(fromObject, new string[] { "controlType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableControlImageComputation" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "computeControl" }, + Common.GetValueByPath(fromObject, new string[] { "enableControlImageComputation" })); + } + + return toObject; + } + + internal JsonNode CountTokensConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }))) { + throw new NotSupportedException( + "systemInstruction parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "tools" }))) { + throw new NotSupportedException("tools parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "generationConfig" }))) { + throw new NotSupportedException( + "generationConfig parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode CountTokensConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ToolToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "generationConfig" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "generationConfig" }, + GenerationConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "generationConfig" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode CountTokensParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CountTokensConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode CountTokensParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CountTokensConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode CountTokensResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "totalTokens" }) != null) { + Common.SetValueByPath(toObject, new string[] { "totalTokens" }, + Common.GetValueByPath(fromObject, new string[] { "totalTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContentTokenCount" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "cachedContentTokenCount" }, + Common.GetValueByPath(fromObject, new string[] { "cachedContentTokenCount" })); + } + + return toObject; + } + + internal JsonNode CountTokensResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "totalTokens" }) != null) { + Common.SetValueByPath(toObject, new string[] { "totalTokens" }, + Common.GetValueByPath(fromObject, new string[] { "totalTokens" })); + } + + return toObject; + } + + internal JsonNode DeleteModelParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + return toObject; + } + + internal JsonNode DeleteModelParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + return toObject; + } + + internal JsonNode DeleteModelResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode DeleteModelResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode EditImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, + Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, + Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "guidanceScale" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "guidanceScale" }, + Common.GetValueByPath(fromObject, new string[] { "guidanceScale" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "safetySetting" }, + Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "includeSafetyAttributes" }, + Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "includeRaiReason" }, + Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "language" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "language" }, + Common.GetValueByPath(fromObject, new string[] { "language" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "parameters", "outputOptions", "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "addWatermark" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "addWatermark" }, + Common.GetValueByPath(fromObject, new string[] { "addWatermark" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "editMode" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "editMode" }, + Common.GetValueByPath(fromObject, new string[] { "editMode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "baseSteps" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "parameters", "editConfig", "baseSteps" }, + Common.GetValueByPath(fromObject, new string[] { "baseSteps" })); + } + + return toObject; + } + + internal JsonNode EditImageParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "referenceImages" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "referenceImages" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + ReferenceImageAPIToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "instances[0]", "referenceImages" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = EditImageConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode EditImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); + } + + return toObject; + } + + internal JsonNode EmbedContentConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "requests[]", "taskType" }, + Common.GetValueByPath(fromObject, new string[] { "taskType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "requests[]", "title" }, + Common.GetValueByPath(fromObject, new string[] { "title" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "requests[]", "outputDimensionality" }, + Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mimeType" }))) { + throw new NotSupportedException("mimeType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }))) { + throw new NotSupportedException("autoTruncate parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode EmbedContentConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + JsonNode discriminatorTaskType = + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); + string discriminatorValueTaskType = + discriminatorTaskType == null ? "PREDICT" : discriminatorTaskType.GetValue(); + if (discriminatorValueTaskType == "PREDICT") { + if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[]", "task_type" }, + Common.GetValueByPath(fromObject, new string[] { "taskType" })); + } + } else if (discriminatorValueTaskType == "EMBED_CONTENT") { + if (Common.GetValueByPath(fromObject, new string[] { "taskType" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "taskType" }, + Common.GetValueByPath(fromObject, new string[] { "taskType" })); + } + } + + JsonNode discriminatorTitle = + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); + string discriminatorValueTitle = + discriminatorTitle == null ? "PREDICT" : discriminatorTitle.GetValue(); + if (discriminatorValueTitle == "PREDICT") { + if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[]", "title" }, + Common.GetValueByPath(fromObject, new string[] { "title" })); + } + } else if (discriminatorValueTitle == "EMBED_CONTENT") { + if (Common.GetValueByPath(fromObject, new string[] { "title" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "title" }, + Common.GetValueByPath(fromObject, new string[] { "title" })); + } + } + + JsonNode discriminatorOutputDimensionality = + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); + string discriminatorValueOutputDimensionality = + discriminatorOutputDimensionality == null + ? "PREDICT" + : discriminatorOutputDimensionality.GetValue(); + if (discriminatorValueOutputDimensionality == "PREDICT") { + if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "outputDimensionality" }, + Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); + } + } else if (discriminatorValueOutputDimensionality == "EMBED_CONTENT") { + if (Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "outputDimensionality" }, + Common.GetValueByPath(fromObject, new string[] { "outputDimensionality" })); + } + } + + JsonNode discriminatorMimeType = + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); + string discriminatorValueMimeType = + discriminatorMimeType == null ? "PREDICT" : discriminatorMimeType.GetValue(); + if (discriminatorValueMimeType == "PREDICT") { + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[]", "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + } + + JsonNode discriminatorAutoTruncate = + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); + string discriminatorValueAutoTruncate = discriminatorAutoTruncate == null + ? "PREDICT" + : discriminatorAutoTruncate.GetValue(); + if (discriminatorValueAutoTruncate == "PREDICT") { + if (Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "autoTruncate" }, + Common.GetValueByPath(fromObject, new string[] { "autoTruncate" })); + } + } else if (discriminatorValueAutoTruncate == "EMBED_CONTENT") { + if (Common.GetValueByPath(fromObject, new string[] { "autoTruncate" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "autoTruncate" }, + Common.GetValueByPath(fromObject, new string[] { "autoTruncate" })); + } + } + + return toObject; + } + + internal JsonNode EmbedContentParametersPrivateToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "requests[]", "content" }, + Transformers.TContentsForEmbed( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "contents" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { + _ = ContentToMldev(Common.ParseToJsonNode(Transformers.TContent( + Common.GetValueByPath(fromObject, new string[] { "content" }))), + toObject, rootObject); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = EmbedContentConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + Common.SetValueByPath( + toObject, new string[] { "requests[]", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + + return toObject; + } + + internal JsonNode EmbedContentParametersPrivateToVertex(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + JsonNode discriminatorContents = + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); + string discriminatorValueContents = + discriminatorContents == null ? "PREDICT" : discriminatorContents.GetValue(); + if (discriminatorValueContents == "PREDICT") { + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "instances[]", "content" }, + Transformers.TContentsForEmbed( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "contents" }))); + } + } + + JsonNode discriminatorContent = + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }); + string discriminatorValueContent = + discriminatorContent == null ? "PREDICT" : discriminatorContent.GetValue(); + if (discriminatorValueContent == "EMBED_CONTENT") { + if (Common.GetValueByPath(fromObject, new string[] { "content" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "content" }, + ContentToVertex(Common.ParseToJsonNode(Transformers.TContent( + Common.GetValueByPath(fromObject, new string[] { "content" }))), + toObject, rootObject)); + } + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = EmbedContentConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode EmbedContentResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "embeddings" }) != null) { + Common.SetValueByPath(toObject, new string[] { "embeddings" }, + Common.GetValueByPath(fromObject, new string[] { "embeddings" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + return toObject; + } + + internal JsonNode EmbedContentResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "predictions[]", "embeddings" }) != + null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath( + fromObject, new string[] { "predictions[]", "embeddings" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + ContentEmbeddingFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "embeddings" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (rootObject != null && + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }) != null && + Common.GetValueByPath(rootObject, new string[] { "embeddingApiType" }) + .GetValue() == "EMBED_CONTENT") { + JsonNode? embedding_node = Common.GetValueByPath(fromObject, new string[] { "embedding" }); + if (embedding_node != null) { + JsonNode embedding = JsonNode.Parse(embedding_node.ToJsonString()); + JsonNode usageMetadata = + Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }); + JsonNode truncated = Common.GetValueByPath(fromObject, new string[] { "truncated" }); + JsonObject stats = new JsonObject(); + if (usageMetadata != null && usageMetadata["promptTokenCount"] != null) { + stats.Add("token_count", + JsonNode.Parse(usageMetadata["promptTokenCount"].ToJsonString())); + } + if (truncated != null) { + stats.Add("truncated", JsonNode.Parse(truncated.ToJsonString())); + } + ((JsonObject)embedding).Add("statistics", stats); + JsonArray embeddings = new JsonArray(); + embeddings.Add(embedding); + Common.SetValueByPath(toObject, new string[] { "embeddings" }, embeddings); + } + } + + return toObject; + } + + internal JsonNode EndpointFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "endpoint" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "endpoint" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "deployedModelId" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "deployedModelId" }, + Common.GetValueByPath(fromObject, new string[] { "deployedModelId" })); + } + + return toObject; + } + + internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileUri" }, + Common.GetValueByPath(fromObject, new string[] { "fileUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { + Common.SetValueByPath(toObject, new string[] { "id" }, + Common.GetValueByPath(fromObject, new string[] { "id" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { + Common.SetValueByPath(toObject, new string[] { "args" }, + Common.GetValueByPath(fromObject, new string[] { "args" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { + throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { + throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "allowedFunctionNames" }, + Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { + throw new NotSupportedException( + "streamFunctionCallArguments parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode FunctionDeclarationToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { + Common.SetValueByPath(toObject, new string[] { "parameters" }, + Common.GetValueByPath(fromObject, new string[] { "parameters" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parametersJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath(toObject, new string[] { "response" }, + Common.GetValueByPath(fromObject, new string[] { "response" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "behavior" }))) { + throw new NotSupportedException("behavior parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode GenerateContentConfigToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "candidateCount" }, + Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { + Common.SetValueByPath(toObject, new string[] { "stopSequences" }, + Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobs" }, + Common.GetValueByPath(fromObject, new string[] { "logprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "presencePenalty" }, + Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "frequencyPenalty" }, + Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(toObject, new string[] { "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseMimeType" }, + Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseSchema" }, + Transformers.TSchema(Common.GetValueByPath( + fromObject, new string[] { "responseSchema" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "routingConfig" }))) { + throw new NotSupportedException("routingConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }))) { + throw new NotSupportedException( + "modelSelectionConfig parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetySettings" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "safetySettings" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(SafetySettingToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(parentObject, new string[] { "safetySettings" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + var keyList = + Transformers.TTools(Common.GetValueByPath(fromObject, new string[] { "tools" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ToolToMldev(Common.ParseToJsonNode(Transformers.TTool(record)), toObject, + rootObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, + ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "toolConfig" })), + toObject, rootObject)); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { + throw new NotSupportedException("labels parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContent" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "cachedContent" }, + Transformers.TCachedContentName( + this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "cachedContent" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "speechConfig" }, + Transformers.TSpeechConfig(Common.GetValueByPath( + fromObject, new string[] { "speechConfig" }))); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }))) { + throw new NotSupportedException("audioTimestamp parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageConfig" }, + ImageConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "imageConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "enableEnhancedCivicAnswers" }, + Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" }))) { + throw new NotSupportedException( + "modelArmorConfig parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode GenerateContentConfigToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "candidateCount" }, + Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { + Common.SetValueByPath(toObject, new string[] { "stopSequences" }, + Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobs" }, + Common.GetValueByPath(fromObject, new string[] { "logprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "presencePenalty" }, + Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "frequencyPenalty" }, + Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(toObject, new string[] { "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseMimeType" }, + Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseSchema" }, + Transformers.TSchema(Common.GetValueByPath( + fromObject, new string[] { "responseSchema" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "routingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "modelConfig" }, + Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetySettings" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "safetySettings" }, + Common.GetValueByPath(fromObject, new string[] { "safetySettings" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + var keyList = + Transformers.TTools(Common.GetValueByPath(fromObject, new string[] { "tools" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ToolToVertex(Common.ParseToJsonNode(Transformers.TTool(record)), toObject, + rootObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, + ToolConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "toolConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContent" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "cachedContent" }, + Transformers.TCachedContentName( + this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "cachedContent" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "speechConfig" }, + SpeechConfigToVertex( + Common.ParseToJsonNode(Transformers.TSpeechConfig( + Common.GetValueByPath(fromObject, new string[] { "speechConfig" }))), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { + Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, + Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageConfig" }, + ImageConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "imageConfig" })), + toObject, rootObject)); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }))) { + throw new NotSupportedException( + "enableEnhancedCivicAnswers parameter is not supported in Vertex AI."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "modelArmorConfig" }, + Common.GetValueByPath(fromObject, new string[] { "modelArmorConfig" })); + } + + return toObject; + } + + internal JsonNode GenerateContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "generationConfig" }, + GenerateContentConfigToMldev(apiClient, + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "config" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerateContentParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "generationConfig" }, + GenerateContentConfigToVertex(apiClient, + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "config" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerateContentResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidates" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "candidates" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(CandidateFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "candidates" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "modelVersion" }) != null) { + Common.SetValueByPath(toObject, new string[] { "modelVersion" }, + Common.GetValueByPath(fromObject, new string[] { "modelVersion" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "promptFeedback" }) != null) { + Common.SetValueByPath(toObject, new string[] { "promptFeedback" }, + Common.GetValueByPath(fromObject, new string[] { "promptFeedback" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseId" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseId" }, + Common.GetValueByPath(fromObject, new string[] { "responseId" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "usageMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "usageMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "modelStatus" }) != null) { + Common.SetValueByPath(toObject, new string[] { "modelStatus" }, + Common.GetValueByPath(fromObject, new string[] { "modelStatus" })); + } + + return toObject; + } + + internal JsonNode GenerateContentResponseFromVertex(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidates" }) != null) { + Common.SetValueByPath(toObject, new string[] { "candidates" }, + Common.GetValueByPath(fromObject, new string[] { "candidates" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "createTime" }, + Common.GetValueByPath(fromObject, new string[] { "createTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "modelVersion" }) != null) { + Common.SetValueByPath(toObject, new string[] { "modelVersion" }, + Common.GetValueByPath(fromObject, new string[] { "modelVersion" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "promptFeedback" }) != null) { + Common.SetValueByPath(toObject, new string[] { "promptFeedback" }, + Common.GetValueByPath(fromObject, new string[] { "promptFeedback" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseId" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseId" }, + Common.GetValueByPath(fromObject, new string[] { "responseId" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "usageMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "usageMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "usageMetadata" })); + } + + return toObject; + } + + internal JsonNode GenerateImagesConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }))) { + throw new NotSupportedException("outputGcsUri parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }))) { + throw new NotSupportedException("negativePrompt parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, + Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "guidanceScale" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "guidanceScale" }, + Common.GetValueByPath(fromObject, new string[] { "guidanceScale" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "seed" }))) { + throw new NotSupportedException("seed parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "safetySetting" }, + Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "includeSafetyAttributes" }, + Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "includeRaiReason" }, + Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "language" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "language" }, + Common.GetValueByPath(fromObject, new string[] { "language" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "parameters", "outputOptions", "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "addWatermark" }))) { + throw new NotSupportedException("addWatermark parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { + throw new NotSupportedException("labels parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleImageSize" }, + Common.GetValueByPath(fromObject, new string[] { "imageSize" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }))) { + throw new NotSupportedException("enhancePrompt parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode GenerateImagesConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, + Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, + Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "guidanceScale" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "guidanceScale" }, + Common.GetValueByPath(fromObject, new string[] { "guidanceScale" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "safetySetting" }, + Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "includeSafetyAttributes" }, + Common.GetValueByPath(fromObject, new string[] { "includeSafetyAttributes" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "includeRaiReason" }, + Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "language" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "language" }, + Common.GetValueByPath(fromObject, new string[] { "language" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "parameters", "outputOptions", "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "addWatermark" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "addWatermark" }, + Common.GetValueByPath(fromObject, new string[] { "addWatermark" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleImageSize" }, + Common.GetValueByPath(fromObject, new string[] { "imageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); + } + + return toObject; + } + + internal JsonNode GenerateImagesParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = GenerateImagesConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode GenerateImagesParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = GenerateImagesConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode GenerateImagesResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(GeneratedImageFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "positivePromptSafetyAttributes" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "positivePromptSafetyAttributes" }, + SafetyAttributesFromMldev( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "positivePromptSafetyAttributes" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerateImagesResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "positivePromptSafetyAttributes" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "positivePromptSafetyAttributes" }, + SafetyAttributesFromVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "positivePromptSafetyAttributes" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, + Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }))) { + throw new NotSupportedException("outputGcsUri parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fps" }))) { + throw new NotSupportedException("fps parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "durationSeconds" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "durationSeconds" }, + Common.GetValueByPath(fromObject, new string[] { "durationSeconds" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "seed" }))) { + throw new NotSupportedException("seed parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "resolution" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "resolution" }, + Common.GetValueByPath(fromObject, new string[] { "resolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "pubsubTopic" }))) { + throw new NotSupportedException("pubsubTopic parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "generateAudio" }))) { + throw new NotSupportedException("generateAudio parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "lastFrame" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "lastFrame" }, + ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "lastFrame" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "referenceImages" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "referenceImages" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(VideoGenerationReferenceImageToMldev(Common.ParseToJsonNode(record), toObject, + rootObject)); + } + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "referenceImages" }, + result); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mask" }))) { + throw new NotSupportedException("mask parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "compressionQuality" }))) { + throw new NotSupportedException( + "compressionQuality parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { + throw new NotSupportedException("labels parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode GenerateVideosConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, + Common.GetValueByPath(fromObject, new string[] { "numberOfVideos" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, + Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fps" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "fps" }, + Common.GetValueByPath(fromObject, new string[] { "fps" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "durationSeconds" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "durationSeconds" }, + Common.GetValueByPath(fromObject, new string[] { "durationSeconds" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "resolution" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "resolution" }, + Common.GetValueByPath(fromObject, new string[] { "resolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pubsubTopic" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "pubsubTopic" }, + Common.GetValueByPath(fromObject, new string[] { "pubsubTopic" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "negativePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "negativePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "negativePrompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "generateAudio" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "generateAudio" }, + Common.GetValueByPath(fromObject, new string[] { "generateAudio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "lastFrame" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "lastFrame" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "lastFrame" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "referenceImages" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "referenceImages" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(VideoGenerationReferenceImageToVertex(Common.ParseToJsonNode(record), toObject, + rootObject)); + } + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "referenceImages" }, + result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mask" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "instances[0]", "mask" }, + VideoGenerationMaskToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "mask" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "compressionQuality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "compressionQuality" }, + Common.GetValueByPath(fromObject, new string[] { "compressionQuality" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + return toObject; + } + + internal JsonNode GenerateVideosOperationFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response", "generateVideoResponse" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "response" }, + GenerateVideosResponseFromMldev( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "response", "generateVideoResponse" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosOperationFromVertex(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "response" }, + GenerateVideosResponseFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "response" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "image" }, + ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "video" }, + VideoToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "video" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { + _ = GenerateVideosSourceToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), + toObject, rootObject); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = GenerateVideosConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode GenerateVideosParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "video" }, + VideoToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "video" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { + _ = GenerateVideosSourceToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), + toObject, rootObject); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = GenerateVideosConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode GenerateVideosResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(GeneratedVideoFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredCount" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredReasons" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); + } + + return toObject; + } + + internal JsonNode GenerateVideosResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "videos" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "videos" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + GeneratedVideoFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredCount" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredReasons" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); + } + + return toObject; + } + + internal JsonNode GenerateVideosSourceToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "image" }, + ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "video" }, + VideoToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "video" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosSourceToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "video" }, + VideoToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "video" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GeneratedImageFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "image" }, + ImageFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiFilteredReason" }, + Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "safetyAttributes" }, + SafetyAttributesFromMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GeneratedImageFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "image" }, + ImageFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiFilteredReason" }, + Common.GetValueByPath(fromObject, new string[] { "raiFilteredReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "safetyAttributes" }, + SafetyAttributesFromVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(toObject, new string[] { "enhancedPrompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + return toObject; + } + + internal JsonNode GeneratedImageMaskFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mask" }, + ImageFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(toObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + return toObject; + } + + internal JsonNode GeneratedVideoFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { + Common.SetValueByPath(toObject, new string[] { "video" }, + VideoFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "video" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GeneratedVideoFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "video" }, + VideoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode GenerationConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "modelConfig" }, + Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { + Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, + Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "candidateCount" }, + Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "enableAffectiveDialog" }, + Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "frequencyPenalty" }, + Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobs" }, + Common.GetValueByPath(fromObject, new string[] { "logprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "presencePenalty" }, + Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseMimeType" }, + Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "routingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(toObject, new string[] { "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "speechConfig" }, + SpeechConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "speechConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { + Common.SetValueByPath(toObject, new string[] { "stopSequences" }, + Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }))) { + throw new NotSupportedException( + "enableEnhancedCivicAnswers parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode GetModelParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + return toObject; + } + + internal JsonNode GetModelParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + return toObject; + } + + internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "authConfig" }, + AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "authConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { + Common.SetValueByPath(toObject, new string[] { "enableWidget" }, + Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { + Common.SetValueByPath(toObject, new string[] { "searchTypes" }, + Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { + throw new NotSupportedException( + "blockingConfidence parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { + throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "timeRangeFilter" }, + Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); + } + + return toObject; + } + + internal JsonNode ImageConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(toObject, new string[] { "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageSize" }, + Common.GetValueByPath(fromObject, new string[] { "imageSize" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "personGeneration" }))) { + throw new NotSupportedException( + "personGeneration parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "prominentPeople" }))) { + throw new NotSupportedException( + "prominentPeople parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }))) { + throw new NotSupportedException("outputMimeType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }))) { + throw new NotSupportedException( + "outputCompressionQuality parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" }))) { + throw new NotSupportedException( + "imageOutputOptions parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode ImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "aspectRatio" }) != null) { + Common.SetValueByPath(toObject, new string[] { "aspectRatio" }, + Common.GetValueByPath(fromObject, new string[] { "aspectRatio" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageSize" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageSize" }, + Common.GetValueByPath(fromObject, new string[] { "imageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prominentPeople" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "prominentPeople" }, + Common.GetValueByPath(fromObject, new string[] { "prominentPeople" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageOutputOptions", "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "imageOutputOptions", "compressionQuality" }, + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "imageOutputOptions" }, + Common.GetValueByPath(fromObject, new string[] { "imageOutputOptions" })); + } + + return toObject; + } + + internal JsonNode ImageFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "bytesBase64Encoded" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode ImageFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "gcsUri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { + Common.SetValueByPath(toObject, new string[] { "imageBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "bytesBase64Encoded" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode ImageToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "gcsUri" }))) { + throw new NotSupportedException("gcsUri parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageBytes" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "bytesBase64Encoded" }, + Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "imageBytes" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode ImageToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "gcsUri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imageBytes" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "bytesBase64Encoded" }, + Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "imageBytes" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode ListModelsConfigToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, + Common.GetValueByPath(fromObject, new string[] { "filter" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "queryBase" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "_url", "models_url" }, + Transformers.TModelsUrl( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "queryBase" }))); + } + + return toObject; + } + + internal JsonNode ListModelsConfigToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, + Common.GetValueByPath(fromObject, new string[] { "filter" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "queryBase" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "_url", "models_url" }, + Transformers.TModelsUrl( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "queryBase" }))); + } + + return toObject; + } + + internal JsonNode ListModelsParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListModelsConfigToMldev( + apiClient, + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode ListModelsParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListModelsConfigToVertex( + apiClient, + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode ListModelsResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + JsonArray keyArray = (JsonArray)Transformers.TExtractModels( + Common.GetValueByPath(fromObject, new string[] { "_self" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ModelFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "models" }, result); + } + + return toObject; + } + + internal JsonNode ListModelsResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + JsonArray keyArray = (JsonArray)Transformers.TExtractModels( + Common.GetValueByPath(fromObject, new string[] { "_self" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ModelFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "models" }, result); + } + + return toObject; + } + + internal JsonNode MaskReferenceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "maskMode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "maskMode" }, + Common.GetValueByPath(fromObject, new string[] { "maskMode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "segmentationClasses" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maskClasses" }, + Common.GetValueByPath(fromObject, new string[] { "segmentationClasses" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maskDilation" }) != null) { + Common.SetValueByPath(toObject, new string[] { "dilation" }, + Common.GetValueByPath(fromObject, new string[] { "maskDilation" })); + } + + return toObject; + } + + internal JsonNode ModelFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "version" }) != null) { + Common.SetValueByPath(toObject, new string[] { "version" }, + Common.GetValueByPath(fromObject, new string[] { "version" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tunedModelInfo" }, + Common.GetValueByPath(fromObject, new string[] { "_self" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inputTokenLimit" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "inputTokenLimit" }, + Common.GetValueByPath(fromObject, new string[] { "inputTokenLimit" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputTokenLimit" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "outputTokenLimit" }, + Common.GetValueByPath(fromObject, new string[] { "outputTokenLimit" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "supportedGenerationMethods" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "supportedActions" }, + Common.GetValueByPath(fromObject, new string[] { "supportedGenerationMethods" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxTemperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "maxTemperature" }, + Common.GetValueByPath(fromObject, new string[] { "maxTemperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thinking" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thinking" }, + Common.GetValueByPath(fromObject, new string[] { "thinking" })); + } + + return toObject; + } + + internal JsonNode ModelFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "versionId" }) != null) { + Common.SetValueByPath(toObject, new string[] { "version" }, + Common.GetValueByPath(fromObject, new string[] { "versionId" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "deployedModels" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "deployedModels" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(EndpointFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "endpoints" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(toObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tunedModelInfo" }, + TunedModelInfoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "defaultCheckpointId" }, + Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "checkpoints" }) != null) { + Common.SetValueByPath(toObject, new string[] { "checkpoints" }, + Common.GetValueByPath(fromObject, new string[] { "checkpoints" })); + } + + return toObject; + } + + internal JsonNode MultiSpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + SpeakerVoiceConfigToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); + } + + return toObject; + } + + internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileData" }, + FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "fileData" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCall" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "inlineData" }, + BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "inlineData" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolCall" }, + Common.GetValueByPath(fromObject, new string[] { "toolCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolResponse" }, + Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "partMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); + } + + return toObject; + } + + internal JsonNode PartToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileData" }, + Common.GetValueByPath(fromObject, new string[] { "fileData" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + Common.GetValueByPath(fromObject, new string[] { "functionCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "inlineData" }, + Common.GetValueByPath(fromObject, new string[] { "inlineData" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolCall" }))) { + throw new NotSupportedException("toolCall parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolResponse" }))) { + throw new NotSupportedException("toolResponse parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partMetadata" }))) { + throw new NotSupportedException("partMetadata parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode ProductImageToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "productImage" }) != null) { + Common.SetValueByPath(toObject, new string[] { "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "productImage" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode RecontextImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, + Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "baseSteps" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "baseSteps" }, + Common.GetValueByPath(fromObject, new string[] { "baseSteps" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, + Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "safetySetting" }, + Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "addWatermark" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "addWatermark" }, + Common.GetValueByPath(fromObject, new string[] { "addWatermark" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "parameters", "outputOptions", "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "enhancePrompt" }, + Common.GetValueByPath(fromObject, new string[] { "enhancePrompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + return toObject; + } + + internal JsonNode RecontextImageParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { + _ = RecontextImageSourceToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), + toObject, rootObject); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = RecontextImageConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode RecontextImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); + } + + return toObject; + } + + internal JsonNode RecontextImageSourceToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personImage" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "personImage", "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "personImage" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "productImages" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "productImages" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ProductImageToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "productImages" }, + result); + } + + return toObject; + } + + internal JsonNode ReferenceImageAPIToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "referenceImage" }) != null) { + Common.SetValueByPath(toObject, new string[] { "referenceImage" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "referenceImage" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "referenceId" }) != null) { + Common.SetValueByPath(toObject, new string[] { "referenceId" }, + Common.GetValueByPath(fromObject, new string[] { "referenceId" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "referenceType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "referenceType" }, + Common.GetValueByPath(fromObject, new string[] { "referenceType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maskImageConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maskImageConfig" }, + MaskReferenceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "maskImageConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "controlImageConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "controlImageConfig" }, + ControlReferenceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "controlImageConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "styleImageConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "styleImageConfig" }, + Common.GetValueByPath(fromObject, new string[] { "styleImageConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "subjectImageConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "subjectImageConfig" }, + Common.GetValueByPath(fromObject, new string[] { "subjectImageConfig" })); + } + + return toObject; + } + + internal JsonNode ReplicatedVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "voiceSampleAudio" }, + Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" })); + } + + return toObject; + } + + internal JsonNode SafetyAttributesFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "categories" }, + Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "scores" }, + Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contentType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "contentType" }, + Common.GetValueByPath(fromObject, new string[] { "contentType" })); + } + + return toObject; + } + + internal JsonNode SafetyAttributesFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "categories" }, + Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "categories" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "scores" }, + Common.GetValueByPath(fromObject, new string[] { "safetyAttributes", "scores" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contentType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "contentType" }, + Common.GetValueByPath(fromObject, new string[] { "contentType" })); + } + + return toObject; + } + + internal JsonNode SafetySettingToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "category" }) != null) { + Common.SetValueByPath(toObject, new string[] { "category" }, + Common.GetValueByPath(fromObject, new string[] { "category" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "method" }))) { + throw new NotSupportedException("method parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "threshold" }) != null) { + Common.SetValueByPath(toObject, new string[] { "threshold" }, + Common.GetValueByPath(fromObject, new string[] { "threshold" })); + } + + return toObject; + } + + internal JsonNode ScribbleImageToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(toObject, new string[] { "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode SegmentImageConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxPredictions" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "maxPredictions" }, + Common.GetValueByPath(fromObject, new string[] { "maxPredictions" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "confidenceThreshold" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "confidenceThreshold" }, + Common.GetValueByPath(fromObject, new string[] { "confidenceThreshold" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maskDilation" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "maskDilation" }, + Common.GetValueByPath(fromObject, new string[] { "maskDilation" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "binaryColorThreshold" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "binaryColorThreshold" }, + Common.GetValueByPath(fromObject, new string[] { "binaryColorThreshold" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + return toObject; + } + + internal JsonNode SegmentImageParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "source" }) != null) { + _ = SegmentImageSourceToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "source" })), + toObject, rootObject); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = SegmentImageConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode SegmentImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + GeneratedImageMaskFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedMasks" }, result); + } + + return toObject; + } + + internal JsonNode SegmentImageSourceToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "prompt" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "prompt" }, + Common.GetValueByPath(fromObject, new string[] { "prompt" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "instances[0]", "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "scribbleImage" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "instances[0]", "scribble" }, + ScribbleImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "scribbleImage" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode SpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { + Common.SetValueByPath(toObject, new string[] { "speaker" }, + Common.GetValueByPath(fromObject, new string[] { "speaker" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, + VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode SpeechConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, + VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "languageCode" }, + Common.GetValueByPath(fromObject, new string[] { "languageCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, + MultiSpeakerVoiceConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "multiSpeakerVoiceConfig" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "retrievalConfig" }, + Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionCallingConfig" }, + FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCallingConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "includeServerSideToolInvocations" }, + Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); + } + + return toObject; + } + + internal JsonNode ToolConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "retrievalConfig" }, + Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionCallingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" })); + } + + if (!Common.IsZero(Common.GetValueByPath( + fromObject, new string[] { "includeServerSideToolInvocations" }))) { + throw new NotSupportedException( + "includeServerSideToolInvocations parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { + throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "computerUse" }, + Common.GetValueByPath(fromObject, new string[] { "computerUse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileSearch" }, + Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearch" }, + GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleSearch" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleMaps" }, + GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleMaps" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { + throw new NotSupportedException( + "enterpriseWebSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionDeclarations" }, + Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearchRetrieval" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { + throw new NotSupportedException( + "parallelAiSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath(toObject, new string[] { "urlContext" }, + Common.GetValueByPath(fromObject, new string[] { "urlContext" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mcpServers" }, + Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); + } + + return toObject; + } + + internal JsonNode ToolToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrieval" }) != null) { + Common.SetValueByPath(toObject, new string[] { "retrieval" }, + Common.GetValueByPath(fromObject, new string[] { "retrieval" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "computerUse" }, + Common.GetValueByPath(fromObject, new string[] { "computerUse" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileSearch" }))) { + throw new NotSupportedException("fileSearch parameter is not supported in Vertex AI."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearch" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleMaps" }, + Common.GetValueByPath(fromObject, new string[] { "googleMaps" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "enterpriseWebSearch" }, + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + FunctionDeclarationToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearchRetrieval" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parallelAiSearch" }, + Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath(toObject, new string[] { "urlContext" }, + Common.GetValueByPath(fromObject, new string[] { "urlContext" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mcpServers" }))) { + throw new NotSupportedException("mcpServers parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode TunedModelInfoFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath( + fromObject, new string[] { "labels", "google-vertex-llm-tuning-base-model-id" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "baseModel" }, + Common.GetValueByPath( + fromObject, new string[] { "labels", "google-vertex-llm-tuning-base-model-id" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "createTime" }, + Common.GetValueByPath(fromObject, new string[] { "createTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "updateTime" }, + Common.GetValueByPath(fromObject, new string[] { "updateTime" })); + } + + return toObject; + } + + internal JsonNode UpdateModelConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "defaultCheckpointId" }, + Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" })); + } + + return toObject; + } + + internal JsonNode UpdateModelConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "defaultCheckpointId" }, + Common.GetValueByPath(fromObject, new string[] { "defaultCheckpointId" })); + } + + return toObject; + } + + internal JsonNode UpdateModelParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = UpdateModelConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode UpdateModelParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = UpdateModelConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode UpscaleImageAPIConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "storageUri" }, + Common.GetValueByPath(fromObject, new string[] { "outputGcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "safetySetting" }, + Common.GetValueByPath(fromObject, new string[] { "safetyFilterLevel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "personGeneration" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "personGeneration" }, + Common.GetValueByPath(fromObject, new string[] { "personGeneration" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "includeRaiReason" }, + Common.GetValueByPath(fromObject, new string[] { "includeRaiReason" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputMimeType" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "parameters", "outputOptions", "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "outputMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "outputOptions", "compressionQuality" }, + Common.GetValueByPath(fromObject, new string[] { "outputCompressionQuality" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enhanceInputImage" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "upscaleConfig", "enhanceInputImage" }, + Common.GetValueByPath(fromObject, new string[] { "enhanceInputImage" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "imagePreservationFactor" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "parameters", "upscaleConfig", "imagePreservationFactor" }, + Common.GetValueByPath(fromObject, new string[] { "imagePreservationFactor" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "numberOfImages" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "sampleCount" }, + Common.GetValueByPath(fromObject, new string[] { "numberOfImages" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "parameters", "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + return toObject; + } + + internal JsonNode UpscaleImageAPIParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(toObject, new string[] { "instances[0]", "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "upscaleFactor" }) != null) { + Common.SetValueByPath(toObject, + new string[] { "parameters", "upscaleConfig", "upscaleFactor" }, + Common.GetValueByPath(fromObject, new string[] { "upscaleFactor" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = UpscaleImageAPIConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode UpscaleImageResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "predictions" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "predictions" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + GeneratedImageFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedImages" }, result); + } + + return toObject; + } + + internal JsonNode VideoFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uri" }, + Common.GetValueByPath(fromObject, new string[] { "uri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encodedVideo" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "encodedVideo" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encoding" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "encoding" })); + } + + return toObject; + } + + internal JsonNode VideoFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "bytesBase64Encoded" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode VideoGenerationMaskToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_self" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maskMode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "maskMode" }, + Common.GetValueByPath(fromObject, new string[] { "maskMode" })); + } + + return toObject; + } + + internal JsonNode VideoGenerationReferenceImageToMldev(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(toObject, new string[] { "image" }, + ImageToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "referenceType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "referenceType" }, + Common.GetValueByPath(fromObject, new string[] { "referenceType" })); + } + + return toObject; + } + + internal JsonNode VideoGenerationReferenceImageToVertex(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "image" }) != null) { + Common.SetValueByPath(toObject, new string[] { "image" }, + ImageToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "image" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "referenceType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "referenceType" }, + Common.GetValueByPath(fromObject, new string[] { "referenceType" })); + } + + return toObject; + } + + internal JsonNode VideoToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uri" }, + Common.GetValueByPath(fromObject, new string[] { "uri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoBytes" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "encodedVideo" }, + Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "videoBytes" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "encoding" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode VideoToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "gcsUri" }, + Common.GetValueByPath(fromObject, new string[] { "uri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoBytes" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "bytesBase64Encoded" }, + Transformers.TBytes(Common.GetValueByPath(fromObject, new string[] { "videoBytes" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode VoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "replicatedVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "replicatedVoiceConfig" }, + ReplicatedVoiceConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "replicatedVoiceConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "prebuiltVoiceConfig" }, + Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" })); + } + + return toObject; + } + + public Models(ApiClient apiClient) { + _apiClient = apiClient; + } + + private async Task PrivateGenerateContentAsync( + string model, List contents, GenerateContentConfig? config, + CancellationToken cancellationToken = default) { + GenerateContentParameters parameter = new GenerateContentParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(contents)) { + parameter.Contents = contents; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GenerateContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GenerateContentParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:generateContent", body["_url"]); + } else { + body = GenerateContentParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:generateContent", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + GenerateContentResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + GenerateContentResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + private async IAsyncEnumerable PrivateGenerateContentStreamAsync( + string model, List contents, GenerateContentConfig? config, + [EnumeratorCancellation] CancellationToken cancellationToken = default) { + GenerateContentParameters parameter = new GenerateContentParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(contents)) { + parameter.Contents = contents; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GenerateContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GenerateContentParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:streamGenerateContent?alt=sse", body["_url"]); + } else { + body = GenerateContentParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:streamGenerateContent?alt=sse", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + await foreach (ApiResponse apiResponse in this._apiClient.RequestStreamAsync( + HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + cancellationToken)) { +#if NETSTANDARD2_0 + string chunkJson = await apiResponse.GetEntity().ReadAsStringAsync(); +#else + string chunkJson = await apiResponse.GetEntity().ReadAsStringAsync(cancellationToken); +#endif + JsonNode? chunkNode = JsonNode.Parse(chunkJson); + if (chunkNode == null) + continue; + JsonNode responseNode; + if (this._apiClient.VertexAI) { + responseNode = + GenerateContentResponseFromVertex(chunkNode, new JsonObject(), parameterNode); + } else { + responseNode = + GenerateContentResponseFromMldev(chunkNode, new JsonObject(), parameterNode); + } + var chunkResponse = responseNode.Deserialize(); + yield return chunkResponse; + } + } + + /// + /// Calculates embeddings for the given contents. + /// + /// The model to use for embedding. + /// A to calculate embeddings for. + /// A to calculate embeddings for. + /// The to use. + /// An instance that specifies the + /// optional configurations. + /// A to cancel the + /// operation. A that represents the + /// asynchronous operation. The task result contains a + /// instance with embeddings and other metadata. + + private async Task PrivateEmbedContentAsync( + string model, List? contents, Content? content, EmbeddingApiType? embeddingApiType, + EmbedContentConfig? config, CancellationToken cancellationToken = default) { + EmbedContentParametersPrivate parameter = new EmbedContentParametersPrivate(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(contents)) { + parameter.Contents = contents; + } + if (!Common.IsZero(content)) { + parameter.Content = content; + } + if (!Common.IsZero(embeddingApiType)) { + parameter.EmbeddingApiType = embeddingApiType; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse EmbedContentParametersPrivate to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = EmbedContentParametersPrivateToVertex(this._apiClient, parameterNode, + new JsonObject(), parameterNode); + string endpointUrl = + Transformers.TIsVertexEmbedContentModel(parameterNode["model"].ToString()) + ? "{model}:embedContent" + : "{model}:predict"; + path = Common.FormatMap(endpointUrl, body["_url"]); + } else { + body = EmbedContentParametersPrivateToMldev(this._apiClient, parameterNode, + new JsonObject(), parameterNode); + path = Common.FormatMap("{model}:batchEmbedContents", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + EmbedContentResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + EmbedContentResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + private async Task PrivateGenerateImagesAsync( + string model, string prompt, GenerateImagesConfig? config, + CancellationToken cancellationToken = default) { + GenerateImagesParameters parameter = new GenerateImagesParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(prompt)) { + parameter.Prompt = prompt; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GenerateImagesParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GenerateImagesParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predict", body["_url"]); + } else { + body = GenerateImagesParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predict", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + GenerateImagesResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + GenerateImagesResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + private async Task PrivateEditImageAsync( + string model, string prompt, List referenceImages, + EditImageConfig? config, CancellationToken cancellationToken = default) { + EditImageParameters parameter = new EditImageParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(prompt)) { + parameter.Prompt = prompt; + } + if (!Common.IsZero(referenceImages)) { + parameter.ReferenceImages = referenceImages; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse EditImageParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = EditImageParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predict", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + EditImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateUpscaleImageAsync( + string model, Image image, string upscaleFactor, UpscaleImageAPIConfig? config, + CancellationToken cancellationToken = default) { + UpscaleImageAPIParameters parameter = new UpscaleImageAPIParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(image)) { + parameter.Image = image; + } + if (!Common.IsZero(upscaleFactor)) { + parameter.UpscaleFactor = upscaleFactor; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse UpscaleImageAPIParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = UpscaleImageAPIParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predict", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + UpscaleImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task RecontextImageAsync( + string model, RecontextImageSource source, RecontextImageConfig? config = null, + CancellationToken cancellationToken = default) { + RecontextImageParameters parameter = new RecontextImageParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(source)) { + parameter.Source = source; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse RecontextImageParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = RecontextImageParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predict", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + RecontextImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task SegmentImageAsync( + string model, SegmentImageSource source, SegmentImageConfig? config = null, + CancellationToken cancellationToken = default) { + SegmentImageParameters parameter = new SegmentImageParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(source)) { + parameter.Source = source; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse SegmentImageParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = SegmentImageParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predict", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + SegmentImageResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Retrieves a specific model resource by its name. + /// + + public async Task GetAsync(string model, GetModelConfig? config = null, + CancellationToken cancellationToken = default) { + GetModelParameters parameter = new GetModelParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetModelParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GetModelParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = GetModelParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = ModelFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = ModelFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateListAsync( + ListModelsConfig? config, CancellationToken cancellationToken = default) { + ListModelsParameters parameter = new ListModelsParameters(); + + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse ListModelsParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = ListModelsParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{models_url}", body["_url"]); + } else { + body = ListModelsParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{models_url}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + ListModelsResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + ListModelsResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + /// + /// Updates a specific model resource. + /// + /// The model to update. + /// An instance that specifies parameters + /// to update. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains the updated + /// instance. + + public async Task UpdateAsync(string model, UpdateModelConfig? config = null, + CancellationToken cancellationToken = default) { + UpdateModelParameters parameter = new UpdateModelParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse UpdateModelParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = UpdateModelParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}", body["_url"]); + } else { + body = UpdateModelParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = ModelFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = ModelFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + /// + /// Deletes a specific model resource by its name. + /// + + public async Task DeleteAsync( + string model, DeleteModelConfig? config = null, + CancellationToken cancellationToken = default) { + DeleteModelParameters parameter = new DeleteModelParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse DeleteModelParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = DeleteModelParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = DeleteModelParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + DeleteModelResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + DeleteModelResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Counts the number of tokens in the provided content. + /// + /// The name of the GenAI model to use for token counting. + /// A to count tokens for. + /// A instance that specifies the + /// optional configurations. + /// A to cancel the + /// operation. A that represents the + /// asynchronous operation. The task result contains a + /// instance with the total token count and other metadata. + + public async Task CountTokensAsync( + string model, List contents, CountTokensConfig? config = null, + CancellationToken cancellationToken = default) { + CountTokensParameters parameter = new CountTokensParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(contents)) { + parameter.Contents = contents; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse CountTokensParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = CountTokensParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:countTokens", body["_url"]); + } else { + body = CountTokensParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:countTokens", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + CountTokensResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + CountTokensResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Computes the number of tokens for the given content. + /// + /// The name of the GenAI model to use for token computation. + /// A to compute tokens for. + /// A instance that specifies the + /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains a instance with token + /// information. Thrown when called with a + /// non-Vertex AI client. + + public async Task ComputeTokensAsync( + string model, List contents, ComputeTokensConfig? config = null, + CancellationToken cancellationToken = default) { + ComputeTokensParameters parameter = new ComputeTokensParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(contents)) { + parameter.Contents = contents; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse ComputeTokensParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = ComputeTokensParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:computeTokens", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + ComputeTokensResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + private async Task PrivateGenerateVideosAsync( + string model, string? prompt, Image? image, Video? video, GenerateVideosSource? source, + GenerateVideosConfig? config, CancellationToken cancellationToken = default) { + GenerateVideosParameters parameter = new GenerateVideosParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(prompt)) { + parameter.Prompt = prompt; + } + if (!Common.IsZero(image)) { + parameter.Image = image; + } + if (!Common.IsZero(video)) { + parameter.Video = video; + } + if (!Common.IsZero(source)) { + parameter.Source = source; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GenerateVideosParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GenerateVideosParametersToVertex(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predictLongRunning", body["_url"]); + } else { + body = GenerateVideosParametersToMldev(this._apiClient, parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("{model}:predictLongRunning", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + GenerateVideosOperationFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + GenerateVideosOperationFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Generates content given a GenAI model and a list of content. + /// + /// The name of the GenAI model to use for generation. + /// A to send to the generative + /// model. A instance that + /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains a instance with response contents and + /// other metadata. + public async Task GenerateContentAsync( + string model, List contents, GenerateContentConfig? config = null, + CancellationToken cancellationToken = default) { + return await PrivateGenerateContentAsync(model, contents, config, cancellationToken); + } + + /// + /// Generates content given a GenAI model and a single content item. + /// + /// The name of the GenAI model to use for generation. + /// A instance to send to the generative + /// model. A instance that + /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains a instance with response contents and + /// other metadata. + public async Task GenerateContentAsync( + string model, Content contents, GenerateContentConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await GenerateContentAsync(model, contentList, config, cancellationToken); + } + + /// + /// Generates content given a GenAI model and a text string. + /// + /// The name of the GenAI model to use for generation. + /// A text string to send to the generative model. + /// A instance that specifies the + /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains a instance with response contents and + /// other metadata. + public async Task GenerateContentAsync( + string model, string contents, GenerateContentConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await GenerateContentAsync(model, contentList, config, cancellationToken); + } + + /// + /// Generates content with streaming responses given a GenAI model and a list of content. + /// + /// The name of the GenAI model to use for generation. + /// A to send to the generative + /// model. A instance that + /// specifies the optional configurations. A to cancel the operation. An async enumerable of + /// chunks. + public async IAsyncEnumerable GenerateContentStreamAsync( + string model, List contents, GenerateContentConfig? config = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) { + await foreach (var response in PrivateGenerateContentStreamAsync(model, contents, config, + cancellationToken)) { + yield return response; + } + } + + /// + /// Generates content with streaming responses given a GenAI model and a single content item. + /// + /// The name of the GenAI model to use for generation. + /// A instance to send to the generative + /// model. A instance that + /// specifies the optional configurations. A to cancel the operation. An async enumerable of + /// chunks. + public async IAsyncEnumerable GenerateContentStreamAsync( + string model, Content contents, GenerateContentConfig? config = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + await foreach (var response in GenerateContentStreamAsync(model, contentList, config, + cancellationToken)) { + yield return response; + } + } + + /// + /// Generates content with streaming responses given a GenAI model and a text string. + /// + /// The name of the GenAI model to use for generation. + /// A text string to send to the generative model. + /// A instance that specifies the + /// optional configurations. A to cancel the operation. An async enumerable of + /// chunks. + public async IAsyncEnumerable GenerateContentStreamAsync( + string model, string contents, GenerateContentConfig? config = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + await foreach (var response in GenerateContentStreamAsync(model, contentList, config, + cancellationToken)) { + yield return response; + } + } + + /// + /// Generates images given a GenAI model and a prompt. + /// + /// The name of the GenAI model to use for generation. + /// A text prompt string to send to the generative model. + /// A instance that specifies the + /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains a instance with response contents and + /// other metadata. + public async Task GenerateImagesAsync( + string model, string prompt, GenerateImagesConfig? config = null, + CancellationToken cancellationToken = default) { + GenerateImagesResponse apiResponse = + await PrivateGenerateImagesAsync(model, prompt, config, cancellationToken); + SafetyAttributes? positivePromptSafetyAttributes = null; + List generatedImages = new List(); + + if (apiResponse.GeneratedImages != null) { + foreach (var generatedImage in apiResponse.GeneratedImages) { + if (generatedImage.SafetyAttributes?.ContentType == "Positive Prompt") { + positivePromptSafetyAttributes = generatedImage.SafetyAttributes; + } else { + generatedImages.Add(generatedImage); + } + } + } + + var response = new GenerateImagesResponse { GeneratedImages = generatedImages }; + + if (positivePromptSafetyAttributes != null) { + response.PositivePromptSafetyAttributes = positivePromptSafetyAttributes; + } + + return response; + } + + public async Task EditImageAsync( + String model, String prompt, List referenceImages, + EditImageConfig? config = null, CancellationToken cancellationToken = default) { + List referenceImagesAPI = + referenceImages.Select(i => ((IReferenceImageInternal)i).ToReferenceImageAPI()).ToList(); + + return await PrivateEditImageAsync(model, prompt, referenceImagesAPI, config, + cancellationToken); + } + + public async Task UpscaleImageAsync( + String model, Image image, String upscaleFactor, UpscaleImageConfig? config = null, + CancellationToken cancellationToken = default) { + UpscaleImageAPIConfig apiConfig = new UpscaleImageAPIConfig { + Mode = "upscale", + NumberOfImages = 1, + }; + + if (config != null) { + apiConfig.OutputGcsUri = config.OutputGcsUri; + apiConfig.OutputMimeType = config.OutputMimeType; + apiConfig.OutputCompressionQuality = config.OutputCompressionQuality; + apiConfig.SafetyFilterLevel = config.SafetyFilterLevel; + apiConfig.PersonGeneration = config.PersonGeneration; + apiConfig.IncludeRaiReason = config.IncludeRaiReason; + apiConfig.EnhanceInputImage = config.EnhanceInputImage; + apiConfig.ImagePreservationFactor = config.ImagePreservationFactor; + apiConfig.Labels = config.Labels; + } + + return await PrivateUpscaleImageAsync(model, image, upscaleFactor, apiConfig, + cancellationToken); + } + + /// + /// Lists models asynchronously. + /// + /// A instance that specifies the + /// optional configuration for the list request. + /// A to cancel the + /// operation. A Pager object that contains one page of models. When iterating + /// over the pager, it automatically fetches the next page if there are more. + public async Task> ListAsync( + ListModelsConfig? config = null, CancellationToken cancellationToken = default) { + config ??= new ListModelsConfig(); + + if (!config.QueryBase.HasValue) { + config.QueryBase = true; + } + + if (this._apiClient.VertexAI) { + if (!config.QueryBase.Value) { + if (!String.IsNullOrEmpty(config.Filter)) { + throw new NotSupportedException( + "Filtering tuned models list for Vertex AI is not currently supported"); + } + } + } + + var initialResponse = await PrivateListAsync(config, cancellationToken); + + return new Pager( + requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), + extractItems: response => response.Models, + extractNextPageToken: response => response.NextPageToken, + extractHttpResponse: response => response.SdkHttpResponse, + updateConfigPageToken: (cfg, token) => { + cfg.PageToken = token; + return cfg; + }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); + } + + /// + /// Counts the number of tokens in the provided content. + /// + /// The name of the GenAI model to use for token counting. + /// A to count tokens for. + /// A instance that specifies the + /// optional configurations. + /// A to cancel the + /// operation. A that represents the + /// asynchronous operation. The task result contains a + /// instance with the total token count and other metadata. + public async Task CountTokensAsync( + String model, Content contents, CountTokensConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await CountTokensAsync(model, contentList, config, cancellationToken); + } + + /// + /// Counts the number of tokens in the provided content. + /// + /// The name of the GenAI model to use for token counting. + /// A text string to send to count tokens for. + /// A instance that specifies the + /// optional configurations. + /// A to cancel the + /// operation. A that represents the + /// asynchronous operation. The task result contains a + /// instance with the total token count and other metadata. + public async Task CountTokensAsync( + String model, String contents, CountTokensConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await CountTokensAsync(model, contentList, config, cancellationToken); + } + + /// + /// Computes the number of tokens for a single content item. + /// + /// The name of the GenAI model to use for token computation. + /// A instance to compute tokens for. + /// A instance that specifies the + /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains a instance with token + /// information. Thrown when called with a + /// non-Vertex AI client. + public async Task ComputeTokensAsync( + String model, Content contents, ComputeTokensConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await ComputeTokensAsync(model, contentList, config, cancellationToken); + } + + /// + /// Computes the number of tokens for a text string. + /// + /// The name of the GenAI model to use for token computation. + /// A string of text to compute tokens for. + /// A instance that specifies the + /// optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains a instance with token + /// information. Thrown when called with a + /// non-Vertex AI client. + public async Task ComputeTokensAsync( + String model, String contents, ComputeTokensConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await ComputeTokensAsync(model, contentList, config, cancellationToken); + } + + /// + /// Calculates embeddings for the given content. + /// + /// The model to use. + /// The content to embed. + /// Optional configuration for embeddings. + /// A to cancel the + /// operation. A that represents the + /// asynchronous operation. + public async Task EmbedContentAsync( + string model, Content contents, EmbedContentConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await EmbedContentAsync(model, contentList, config, cancellationToken); + } + + /// + /// Calculates embeddings for the given text string. + /// + /// The model to use. + /// The text string to embed. + /// Optional configuration for embeddings. + /// A to cancel the + /// operation. A that represents the + /// asynchronous operation. + public async Task EmbedContentAsync( + string model, String contents, EmbedContentConfig? config = null, + CancellationToken cancellationToken = default) { + List contentList = Transformers.TContents(contents) ?? new List(); + return await EmbedContentAsync(model, contentList, config, cancellationToken); + } + + public async Task GenerateVideosAsync( + String model, GenerateVideosSource source, GenerateVideosConfig? config = null, + CancellationToken cancellationToken = default) { + return await PrivateGenerateVideosAsync(model, null, null, null, source, config, + cancellationToken); + } + + /// + /// Calculates embeddings for the given content. + /// + /// The model to use. + /// The content to embed. + /// Optional configuration for embeddings. + /// A to cancel the + /// operation. A that represents the + /// asynchronous operation. + public async Task EmbedContentAsync( + string model, List contents, EmbedContentConfig? config = null, + CancellationToken cancellationToken = default) { + if (!_apiClient.VertexAI) { + return await PrivateEmbedContentAsync(model, contents, null, null, config, + cancellationToken); + } + if (Transformers.TIsVertexEmbedContentModel(model)) { + if (contents.Count > 1) { + throw new ArgumentException( + "The embedContent API for this model only supports one content at a time."); + } + return await PrivateEmbedContentAsync( + model, contents, contents[0], EmbeddingApiType.EmbedContent, config, cancellationToken); + } else { + return await PrivateEmbedContentAsync(model, contents, null, EmbeddingApiType.Predict, + config, cancellationToken); + } + } + } +} diff --git a/Google.GenAI/Operations.cs b/Google.GenAI/Operations.cs index ef278f28..1df64148 100644 --- a/Google.GenAI/Operations.cs +++ b/Google.GenAI/Operations.cs @@ -1,426 +1,426 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - - public sealed class Operations { - private readonly ApiClient _apiClient; - - internal JsonNode FetchPredictOperationParametersToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "operationName" }, - Common.GetValueByPath(fromObject, new string[] { "operationName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "resourceName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "resourceName" }, - Common.GetValueByPath(fromObject, new string[] { "resourceName" })); - } - - return toObject; - } - - internal JsonNode GenerateVideosOperationFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response", "generateVideoResponse" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "response" }, - GenerateVideosResponseFromMldev( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "response", "generateVideoResponse" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosOperationFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "response" }, - GenerateVideosResponseFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "response" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosResponseFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(GeneratedVideoFromMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredCount" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredReasons" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); - } - - return toObject; - } - - internal JsonNode GenerateVideosResponseFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "videos" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "videos" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(GeneratedVideoFromVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredCount" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredReasons" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); - } - - return toObject; - } - - internal JsonNode GeneratedVideoFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { - Common.SetValueByPath(toObject, new string[] { "video" }, - VideoFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "video" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GeneratedVideoFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "video" }, - VideoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GetOperationParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, - Common.GetValueByPath(fromObject, new string[] { "operationName" })); - } - - return toObject; - } - - internal JsonNode GetOperationParametersToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, - Common.GetValueByPath(fromObject, new string[] { "operationName" })); - } - - return toObject; - } - - internal JsonNode VideoFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uri" }, - Common.GetValueByPath(fromObject, new string[] { "uri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encodedVideo" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "encodedVideo" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encoding" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "encoding" })); - } - - return toObject; - } - - internal JsonNode VideoFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "bytesBase64Encoded" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - public Operations(ApiClient apiClient) { - _apiClient = apiClient; - } - - internal async Task PrivateGetVideosOperationAsync( - string operationName, GetOperationConfig? config, - CancellationToken cancellationToken = default) { - GetOperationParameters parameter = new GetOperationParameters(); - - if (!Common.IsZero(operationName)) { - parameter.OperationName = operationName; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetOperationParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GetOperationParametersToVertex(parameterNode, new JsonObject()); - path = Common.FormatMap("{operationName}", body["_url"]); - } else { - body = GetOperationParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("{operationName}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - return responseNode; - } - - internal async Task PrivateFetchPredictVideosOperationAsync( - string operationName, string resourceName, FetchPredictOperationConfig? config, - CancellationToken cancellationToken = default) { - FetchPredictOperationParameters parameter = new FetchPredictOperationParameters(); - - if (!Common.IsZero(operationName)) { - parameter.OperationName = operationName; - } - if (!Common.IsZero(resourceName)) { - parameter.ResourceName = resourceName; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse FetchPredictOperationParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = FetchPredictOperationParametersToVertex(parameterNode, new JsonObject()); - path = Common.FormatMap("{resourceName}:fetchPredictOperation", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - return responseNode; - } - - /// - /// Gets the status of a long-running operation. - /// - /// The type of the operation. - /// An operation instance to get the status for. - /// A instance that specifies the optional - /// configurations. A - /// that can be used to cancel the operation. A - /// that represents the asynchronous operation. The task result contains the updated - /// instance with the latest status or result. - public async Task GetAsync( - TOperation operation, GetOperationConfig? config, - CancellationToken cancellationToken = default) - where TOperation : Operation { - if (string.IsNullOrEmpty(operation.Name)) { - throw new ArgumentException("Operation name is required.", nameof(operation)); - } - - string operationName = operation.Name; - - if (this._apiClient.VertexAI) { - string resourceName = - operationName.Split(new[] { "/operations/" }, StringSplitOptions.None)[0]; - FetchPredictOperationConfig fetchConfig = new FetchPredictOperationConfig {}; - if (config != null) { - fetchConfig.HttpOptions = config.HttpOptions; - } - JsonNode response = await this.PrivateFetchPredictVideosOperationAsync( - operationName, resourceName, fetchConfig, cancellationToken); - - return operation.FromApiResponse(response, true); - } else { - JsonNode response = - await this.PrivateGetVideosOperationAsync(operationName, config, cancellationToken); - return operation.FromApiResponse(response, false); - } - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + + public sealed class Operations { + private readonly ApiClient _apiClient; + + internal JsonNode FetchPredictOperationParametersToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "operationName" }, + Common.GetValueByPath(fromObject, new string[] { "operationName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "resourceName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "resourceName" }, + Common.GetValueByPath(fromObject, new string[] { "resourceName" })); + } + + return toObject; + } + + internal JsonNode GenerateVideosOperationFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response", "generateVideoResponse" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "response" }, + GenerateVideosResponseFromMldev( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "response", "generateVideoResponse" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosOperationFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "response" }, + GenerateVideosResponseFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "response" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosResponseFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(GeneratedVideoFromMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredCount" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredReasons" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); + } + + return toObject; + } + + internal JsonNode GenerateVideosResponseFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "videos" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "videos" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(GeneratedVideoFromVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredCount" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredReasons" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); + } + + return toObject; + } + + internal JsonNode GeneratedVideoFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { + Common.SetValueByPath(toObject, new string[] { "video" }, + VideoFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "video" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GeneratedVideoFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "video" }, + VideoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GetOperationParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, + Common.GetValueByPath(fromObject, new string[] { "operationName" })); + } + + return toObject; + } + + internal JsonNode GetOperationParametersToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, + Common.GetValueByPath(fromObject, new string[] { "operationName" })); + } + + return toObject; + } + + internal JsonNode VideoFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uri" }, + Common.GetValueByPath(fromObject, new string[] { "uri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encodedVideo" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "encodedVideo" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encoding" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "encoding" })); + } + + return toObject; + } + + internal JsonNode VideoFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "bytesBase64Encoded" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + public Operations(ApiClient apiClient) { + _apiClient = apiClient; + } + + internal async Task PrivateGetVideosOperationAsync( + string operationName, GetOperationConfig? config, + CancellationToken cancellationToken = default) { + GetOperationParameters parameter = new GetOperationParameters(); + + if (!Common.IsZero(operationName)) { + parameter.OperationName = operationName; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetOperationParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GetOperationParametersToVertex(parameterNode, new JsonObject()); + path = Common.FormatMap("{operationName}", body["_url"]); + } else { + body = GetOperationParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("{operationName}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + return responseNode; + } + + internal async Task PrivateFetchPredictVideosOperationAsync( + string operationName, string resourceName, FetchPredictOperationConfig? config, + CancellationToken cancellationToken = default) { + FetchPredictOperationParameters parameter = new FetchPredictOperationParameters(); + + if (!Common.IsZero(operationName)) { + parameter.OperationName = operationName; + } + if (!Common.IsZero(resourceName)) { + parameter.ResourceName = resourceName; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse FetchPredictOperationParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = FetchPredictOperationParametersToVertex(parameterNode, new JsonObject()); + path = Common.FormatMap("{resourceName}:fetchPredictOperation", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + return responseNode; + } + + /// + /// Gets the status of a long-running operation. + /// + /// The type of the operation. + /// An operation instance to get the status for. + /// A instance that specifies the optional + /// configurations. A + /// that can be used to cancel the operation. A + /// that represents the asynchronous operation. The task result contains the updated + /// instance with the latest status or result. + public async Task GetAsync( + TOperation operation, GetOperationConfig? config, + CancellationToken cancellationToken = default) + where TOperation : Operation { + if (string.IsNullOrEmpty(operation.Name)) { + throw new ArgumentException("Operation name is required.", nameof(operation)); + } + + string operationName = operation.Name; + + if (this._apiClient.VertexAI) { + string resourceName = + operationName.Split(new[] { "/operations/" }, StringSplitOptions.None)[0]; + FetchPredictOperationConfig fetchConfig = new FetchPredictOperationConfig {}; + if (config != null) { + fetchConfig.HttpOptions = config.HttpOptions; + } + JsonNode response = await this.PrivateFetchPredictVideosOperationAsync( + operationName, resourceName, fetchConfig, cancellationToken); + + return operation.FromApiResponse(response, true); + } else { + JsonNode response = + await this.PrivateGetVideosOperationAsync(operationName, config, cancellationToken); + return operation.FromApiResponse(response, false); + } + } + } +} diff --git a/Google.GenAI/TokensConverters.cs b/Google.GenAI/TokensConverters.cs index 19391438..538b7bbf 100644 --- a/Google.GenAI/TokensConverters.cs +++ b/Google.GenAI/TokensConverters.cs @@ -1,813 +1,813 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - class TokensConverters { - private readonly ApiClient _apiClient; - - public TokensConverters(ApiClient apiClient) { - _apiClient = apiClient; - } - - internal JsonNode PrebuiltVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "voiceName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "voiceName" }, - Common.GetValueByPath(fromObject, new string[] { "voiceName" })); - } - - return toObject; - } - - internal JsonNode VoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "prebuiltVoiceConfig" }, - PrebuiltVoiceConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "prebuiltVoiceConfig" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode SpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { - Common.SetValueByPath(toObject, new string[] { "speaker" }, - Common.GetValueByPath(fromObject, new string[] { "speaker" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "voiceConfig" }, - VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode MultiSpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(SpeakerVoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(record)), - toObject)); - } - Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); - } - - return toObject; - } - - internal JsonNode SpeechConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "voiceConfig" }, - VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, - MultiSpeakerVoiceConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "multiSpeakerVoiceConfig" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "languageCode" }, - Common.GetValueByPath(fromObject, new string[] { "languageCode" })); - } - - return toObject; - } - - internal JsonNode VideoMetadataToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "fps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fps" }, - Common.GetValueByPath(fromObject, new string[] { "fps" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endOffset" }) != null) { - Common.SetValueByPath(toObject, new string[] { "endOffset" }, - Common.GetValueByPath(fromObject, new string[] { "endOffset" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "startOffset" }) != null) { - Common.SetValueByPath(toObject, new string[] { "startOffset" }, - Common.GetValueByPath(fromObject, new string[] { "startOffset" })); - } - - return toObject; - } - - internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { - Common.SetValueByPath(toObject, new string[] { "data" }, - Common.GetValueByPath(fromObject, new string[] { "data" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileUri" }, - Common.GetValueByPath(fromObject, new string[] { "fileUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "videoMetadata" }, - VideoMetadataToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "videoMetadata" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "inlineData" }, - BlobToMldev(JsonNode.Parse(JsonSerializer.Serialize( - Common.GetValueByPath(fromObject, new string[] { "inlineData" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "fileData" }, - FileDataToMldev(JsonNode.Parse(JsonSerializer.Serialize( - Common.GetValueByPath(fromObject, new string[] { "fileData" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - Common.GetValueByPath(fromObject, new string[] { "functionCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - return toObject; - } - - internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToMldev(JsonNode.Parse(JsonSerializer.Serialize(record)), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode FunctionDeclarationToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "behavior" }) != null) { - Common.SetValueByPath(toObject, new string[] { "behavior" }, - Common.GetValueByPath(fromObject, new string[] { "behavior" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { - Common.SetValueByPath(toObject, new string[] { "parameters" }, - Common.GetValueByPath(fromObject, new string[] { "parameters" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parametersJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath(toObject, new string[] { "response" }, - Common.GetValueByPath(fromObject, new string[] { "response" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - return toObject; - } - - internal JsonNode IntervalToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "startTime" }, - Common.GetValueByPath(fromObject, new string[] { "startTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "endTime" }, - Common.GetValueByPath(fromObject, new string[] { "endTime" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "timeRangeFilter" }, - IntervalToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "timeRangeFilter" }))), - toObject)); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { - throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode DynamicRetrievalConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "dynamicThreshold" }, - Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchRetrievalToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "dynamicRetrievalConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "dynamicRetrievalConfig" }, - DynamicRetrievalConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "dynamicRetrievalConfig" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode UrlContextToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode ToolComputerUseToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "environment" }) != null) { - Common.SetValueByPath(toObject, new string[] { "environment" }, - Common.GetValueByPath(fromObject, new string[] { "environment" })); - } - - return toObject; - } - - internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(FunctionDeclarationToMldev(JsonNode.Parse(JsonSerializer.Serialize(record)), - toObject)); - } - Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { - throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearch" }, - GoogleSearchToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "googleSearch" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearchRetrieval" }, - GoogleSearchRetrievalToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "googleSearchRetrieval" }))), - toObject)); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { - throw new NotSupportedException( - "enterpriseWebSearch parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "googleMaps" }))) { - throw new NotSupportedException("googleMaps parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "urlContext" }, - UrlContextToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "urlContext" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "computerUse" }, - ToolComputerUseToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "computerUse" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - return toObject; - } - - internal JsonNode SessionResumptionConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "handle" }) != null) { - Common.SetValueByPath(toObject, new string[] { "handle" }, - Common.GetValueByPath(fromObject, new string[] { "handle" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "transparent" }))) { - throw new NotSupportedException("transparent parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode AudioTranscriptionConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode AutomaticActivityDetectionToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "disabled" }) != null) { - Common.SetValueByPath(toObject, new string[] { "disabled" }, - Common.GetValueByPath(fromObject, new string[] { "disabled" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "startOfSpeechSensitivity" }, - Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "endOfSpeechSensitivity" }, - Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "prefixPaddingMs" }, - Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "silenceDurationMs" }, - Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" })); - } - - return toObject; - } - - internal JsonNode RealtimeInputConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "automaticActivityDetection" }) != - null) { - Common.SetValueByPath(toObject, new string[] { "automaticActivityDetection" }, - AutomaticActivityDetectionToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "automaticActivityDetection" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "activityHandling" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "activityHandling" }, - Common.GetValueByPath(fromObject, new string[] { "activityHandling" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "turnCoverage" }) != null) { - Common.SetValueByPath(toObject, new string[] { "turnCoverage" }, - Common.GetValueByPath(fromObject, new string[] { "turnCoverage" })); - } - - return toObject; - } - - internal JsonNode SlidingWindowToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "targetTokens" }) != null) { - Common.SetValueByPath(toObject, new string[] { "targetTokens" }, - Common.GetValueByPath(fromObject, new string[] { "targetTokens" })); - } - - return toObject; - } - - internal JsonNode ContextWindowCompressionConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "triggerTokens" }) != null) { - Common.SetValueByPath(toObject, new string[] { "triggerTokens" }, - Common.GetValueByPath(fromObject, new string[] { "triggerTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "slidingWindow" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "slidingWindow" }, - SlidingWindowToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "slidingWindow" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode ProactivityConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" }) != null) { - Common.SetValueByPath(toObject, new string[] { "proactiveAudio" }, - Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" })); - } - - return toObject; - } - - internal JsonNode LiveConnectConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "generationConfig" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig" }, - Common.GetValueByPath(fromObject, new string[] { "generationConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "setup", "generationConfig", "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "speechConfig" }, - SpeechConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Transformers.TLiveSpeechConfig( - Common.GetValueByPath(fromObject, new string[] { "speechConfig" })))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "enableAffectiveDialog" }, - Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "systemInstruction" }, - ContentToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Transformers.TContent( - Common.GetValueByPath(fromObject, new string[] { "systemInstruction" })))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ToolToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Transformers.TTool(record))), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "setup", "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "sessionResumption" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "sessionResumption" }, - SessionResumptionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "sessionResumption" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inputAudioTranscription" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "inputAudioTranscription" }, - AudioTranscriptionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "inputAudioTranscription" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputAudioTranscription" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "outputAudioTranscription" }, - AudioTranscriptionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "outputAudioTranscription" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "realtimeInputConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "realtimeInputConfig" }, - RealtimeInputConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "realtimeInputConfig" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contextWindowCompression" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "contextWindowCompression" }, - ContextWindowCompressionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "contextWindowCompression" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "proactivity" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "proactivity" }, - ProactivityConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "proactivity" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode LiveConnectConstraintsToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "setup", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "config" }, - LiveConnectConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "config" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode CreateAuthTokenConfigToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "newSessionExpireTime" }, - Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "uses" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "uses" }, - Common.GetValueByPath(fromObject, new string[] { "uses" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "liveConnectConstraints" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "bidiGenerateContentSetup" }, - LiveConnectConstraintsToMldev( - apiClient, - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "liveConnectConstraints" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "fieldMask" }, - Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" })); - } - - return toObject; - } - - internal JsonNode CreateAuthTokenParametersToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode CreateAuthTokenParametersToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode AuthTokenFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode AuthTokenFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + class TokensConverters { + private readonly ApiClient _apiClient; + + public TokensConverters(ApiClient apiClient) { + _apiClient = apiClient; + } + + internal JsonNode PrebuiltVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "voiceName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "voiceName" }, + Common.GetValueByPath(fromObject, new string[] { "voiceName" })); + } + + return toObject; + } + + internal JsonNode VoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "prebuiltVoiceConfig" }, + PrebuiltVoiceConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "prebuiltVoiceConfig" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode SpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { + Common.SetValueByPath(toObject, new string[] { "speaker" }, + Common.GetValueByPath(fromObject, new string[] { "speaker" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "voiceConfig" }, + VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode MultiSpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(SpeakerVoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(record, JsonConfig.InternalSerializerOptions)), + toObject)); + } + Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); + } + + return toObject; + } + + internal JsonNode SpeechConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "voiceConfig" }, + VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, + MultiSpeakerVoiceConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "multiSpeakerVoiceConfig" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "languageCode" }, + Common.GetValueByPath(fromObject, new string[] { "languageCode" })); + } + + return toObject; + } + + internal JsonNode VideoMetadataToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "fps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fps" }, + Common.GetValueByPath(fromObject, new string[] { "fps" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endOffset" }) != null) { + Common.SetValueByPath(toObject, new string[] { "endOffset" }, + Common.GetValueByPath(fromObject, new string[] { "endOffset" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "startOffset" }) != null) { + Common.SetValueByPath(toObject, new string[] { "startOffset" }, + Common.GetValueByPath(fromObject, new string[] { "startOffset" })); + } + + return toObject; + } + + internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { + Common.SetValueByPath(toObject, new string[] { "data" }, + Common.GetValueByPath(fromObject, new string[] { "data" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileUri" }, + Common.GetValueByPath(fromObject, new string[] { "fileUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "videoMetadata" }, + VideoMetadataToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "videoMetadata" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "inlineData" }, + BlobToMldev(JsonNode.Parse(JsonSerializer.Serialize( + Common.GetValueByPath(fromObject, new string[] { "inlineData" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "fileData" }, + FileDataToMldev(JsonNode.Parse(JsonSerializer.Serialize( + Common.GetValueByPath(fromObject, new string[] { "fileData" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + Common.GetValueByPath(fromObject, new string[] { "functionCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + return toObject; + } + + internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToMldev(JsonNode.Parse(JsonSerializer.Serialize(record, JsonConfig.InternalSerializerOptions)), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode FunctionDeclarationToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "behavior" }) != null) { + Common.SetValueByPath(toObject, new string[] { "behavior" }, + Common.GetValueByPath(fromObject, new string[] { "behavior" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { + Common.SetValueByPath(toObject, new string[] { "parameters" }, + Common.GetValueByPath(fromObject, new string[] { "parameters" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parametersJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath(toObject, new string[] { "response" }, + Common.GetValueByPath(fromObject, new string[] { "response" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + return toObject; + } + + internal JsonNode IntervalToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "startTime" }, + Common.GetValueByPath(fromObject, new string[] { "startTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "endTime" }, + Common.GetValueByPath(fromObject, new string[] { "endTime" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "timeRangeFilter" }, + IntervalToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "timeRangeFilter" }))), + toObject)); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { + throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode DynamicRetrievalConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "dynamicThreshold" }, + Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchRetrievalToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "dynamicRetrievalConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "dynamicRetrievalConfig" }, + DynamicRetrievalConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "dynamicRetrievalConfig" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode UrlContextToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode ToolComputerUseToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "environment" }) != null) { + Common.SetValueByPath(toObject, new string[] { "environment" }, + Common.GetValueByPath(fromObject, new string[] { "environment" })); + } + + return toObject; + } + + internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(FunctionDeclarationToMldev(JsonNode.Parse(JsonSerializer.Serialize(record, JsonConfig.InternalSerializerOptions)), + toObject)); + } + Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { + throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearch" }, + GoogleSearchToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "googleSearch" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearchRetrieval" }, + GoogleSearchRetrievalToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "googleSearchRetrieval" }))), + toObject)); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { + throw new NotSupportedException( + "enterpriseWebSearch parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "googleMaps" }))) { + throw new NotSupportedException("googleMaps parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "urlContext" }, + UrlContextToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "urlContext" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "computerUse" }, + ToolComputerUseToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "computerUse" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + return toObject; + } + + internal JsonNode SessionResumptionConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "handle" }) != null) { + Common.SetValueByPath(toObject, new string[] { "handle" }, + Common.GetValueByPath(fromObject, new string[] { "handle" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "transparent" }))) { + throw new NotSupportedException("transparent parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode AudioTranscriptionConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode AutomaticActivityDetectionToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "disabled" }) != null) { + Common.SetValueByPath(toObject, new string[] { "disabled" }, + Common.GetValueByPath(fromObject, new string[] { "disabled" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "startOfSpeechSensitivity" }, + Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "endOfSpeechSensitivity" }, + Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "prefixPaddingMs" }, + Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "silenceDurationMs" }, + Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" })); + } + + return toObject; + } + + internal JsonNode RealtimeInputConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "automaticActivityDetection" }) != + null) { + Common.SetValueByPath(toObject, new string[] { "automaticActivityDetection" }, + AutomaticActivityDetectionToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "automaticActivityDetection" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "activityHandling" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "activityHandling" }, + Common.GetValueByPath(fromObject, new string[] { "activityHandling" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "turnCoverage" }) != null) { + Common.SetValueByPath(toObject, new string[] { "turnCoverage" }, + Common.GetValueByPath(fromObject, new string[] { "turnCoverage" })); + } + + return toObject; + } + + internal JsonNode SlidingWindowToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "targetTokens" }) != null) { + Common.SetValueByPath(toObject, new string[] { "targetTokens" }, + Common.GetValueByPath(fromObject, new string[] { "targetTokens" })); + } + + return toObject; + } + + internal JsonNode ContextWindowCompressionConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "triggerTokens" }) != null) { + Common.SetValueByPath(toObject, new string[] { "triggerTokens" }, + Common.GetValueByPath(fromObject, new string[] { "triggerTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "slidingWindow" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "slidingWindow" }, + SlidingWindowToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "slidingWindow" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode ProactivityConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" }) != null) { + Common.SetValueByPath(toObject, new string[] { "proactiveAudio" }, + Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" })); + } + + return toObject; + } + + internal JsonNode LiveConnectConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "generationConfig" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig" }, + Common.GetValueByPath(fromObject, new string[] { "generationConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "setup", "generationConfig", "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "speechConfig" }, + SpeechConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Transformers.TLiveSpeechConfig( + Common.GetValueByPath(fromObject, new string[] { "speechConfig" })))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "enableAffectiveDialog" }, + Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "systemInstruction" }, + ContentToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Transformers.TContent( + Common.GetValueByPath(fromObject, new string[] { "systemInstruction" })))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ToolToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Transformers.TTool(record), JsonConfig.InternalSerializerOptions)), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "setup", "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "sessionResumption" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "sessionResumption" }, + SessionResumptionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "sessionResumption" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inputAudioTranscription" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "inputAudioTranscription" }, + AudioTranscriptionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "inputAudioTranscription" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputAudioTranscription" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "outputAudioTranscription" }, + AudioTranscriptionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "outputAudioTranscription" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "realtimeInputConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "realtimeInputConfig" }, + RealtimeInputConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "realtimeInputConfig" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contextWindowCompression" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "contextWindowCompression" }, + ContextWindowCompressionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "contextWindowCompression" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "proactivity" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "proactivity" }, + ProactivityConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "proactivity" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode LiveConnectConstraintsToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "setup", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "config" }, + LiveConnectConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "config" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode CreateAuthTokenConfigToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "newSessionExpireTime" }, + Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "uses" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "uses" }, + Common.GetValueByPath(fromObject, new string[] { "uses" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "liveConnectConstraints" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "bidiGenerateContentSetup" }, + LiveConnectConstraintsToMldev( + apiClient, + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "liveConnectConstraints" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "fieldMask" }, + Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" })); + } + + return toObject; + } + + internal JsonNode CreateAuthTokenParametersToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode CreateAuthTokenParametersToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode AuthTokenFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode AuthTokenFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + } +} diff --git a/Google.GenAI/Transformers.cs b/Google.GenAI/Transformers.cs index 1197eac5..7f9dd8c3 100644 --- a/Google.GenAI/Transformers.cs +++ b/Google.GenAI/Transformers.cs @@ -1,732 +1,732 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; - -using Google.GenAI.Types; - - -namespace Google.GenAI -{ - /// - /// Transformers for GenAI SDK. - /// - internal static class Transformers - { - /// - /// Transforms a model name to the correct format for the API. - /// - /// The API client to use for transformation. - /// The model name to transform, can only be a string. - /// The transformed model name. - /// If the object is not a supported type. - internal static string? TModel(ApiClient apiClient, object origin) - { - string? model; - if (origin == null) - { - return null; - } - else if (origin is string strModel) - { - model = strModel; - } - else if (origin is JsonNode jsonNode) - { - model = jsonNode.ToString(); - model = model.Replace("\"", ""); - } - else - { - throw new ArgumentException($"Unsupported model type: {origin.GetType()}"); - } - - if (model.Length == 0) - { - throw new ArgumentException("model is required."); - } - if (model.Contains("..") || model.Contains("?") || model.Contains("&")) - { - throw new ArgumentException("invalid model parameter."); - } - if (apiClient.VertexAI) - { - if (model.StartsWith("publishers/") - || model.StartsWith("projects/") - || model.StartsWith("models/")) - { - return model; - } - else if (model.Contains("/")) - { -#if NETSTANDARD2_0 - string[] parts = model.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries); -#else - string[] parts = model.Split('/', 2, StringSplitOptions.RemoveEmptyEntries); -#endif - return string.Format("publishers/{0}/models/{1}", parts[0], parts[1]); - } - else - { - return "publishers/google/models/" + model; - } - } - else - { - if (model.StartsWith("models/") || model.StartsWith("tunedModels/")) - { - return model; - } - else - { - return "models/" + model; - } - } - } - - /// - /// Determines the appropriate models URL based on the API client type and whether base models are - /// requested. - /// - /// The API client to use for transformation. - /// True if base models are requested, false otherwise. - /// The transformed model name - internal static string TModelsUrl(ApiClient apiClient, object? baseModels) - { - bool queryBase = true; - if (baseModels is JsonValue val) - { - queryBase = val.GetValue(); - } - if (queryBase) - { - return apiClient.VertexAI ? "publishers/google/models" : "models"; - } - else - { - return apiClient.VertexAI ? "models" : "tunedModels"; - } - } - - /// - /// Transforms an object to a list of Content for the API. - /// - /// The object to transform, can be a string, Content, or List<Content> - /// The transformed list of Content - /// If the object is not a supported type - internal static List? TContents(object? contents) - { - if (contents == null) - { - return null; - } - if (contents is string contentString) - { - Content content = new Content(); - content.Role = "user"; - Part part = new Part(); - part.Text = contentString; - content.Parts = new List { part }; - return new List { content }; - } - else if (contents is Content singleContent) - { - return new List { singleContent }; - } - else if (contents is List contentList) - { - return contentList; - } - else if (contents is JsonObject jsonObject) - { - return JsonSerializer.Deserialize>(jsonObject.ToString()); - } - else if (contents is JsonNode jsonNode) - { - return JsonSerializer.Deserialize>(jsonNode.ToString()); - } - - throw new ArgumentException($"Unsupported contents type: {contents.GetType()}"); - } - - /// - /// Transforms an object to a Content for the API. - /// - /// The object to transform, can be a string or Content - /// The transformed Content - /// If the object is not a supported type - internal static Content? TContent(object content) - { - if (content == null) - { - return null; - } - else if (content is string contentString) - { - Content contentObject = new Content(); - contentObject.Role = "user"; - Part part = new Part(); - part.Text = contentString; - contentObject.Parts = new List { part }; - return contentObject; - } - else if (content is Content singleContent) - { - return singleContent; - } - else if (content is JsonObject jsonObject) - { - return JsonSerializer.Deserialize(jsonObject.ToString()); - } - - throw new ArgumentException($"Unsupported content type: {content.GetType()}"); - } - - /// Transforms an object to a Schema for the API. - /// If the object is not a supported type. - internal static Schema? TSchema(object origin) - { - if (origin == null) - { - return null; - } - else if (origin is Schema schema) - { - return schema; - } - else if (origin is JsonObject jsonObject) - { - return JsonSerializer.Deserialize(jsonObject.ToString()); - } - throw new ArgumentException($"Unsupported schema type: {origin.GetType()}"); - } - - internal static SpeechConfig? TSpeechConfig(object speechConfig) - { - if (speechConfig == null) - { - return null; - } - else if (speechConfig is string speechConfigString) - { - return null; - } - else if (speechConfig is SpeechConfig config) - { - return config; - } - else if (speechConfig is JsonObject jsonObject) - { - return JsonSerializer.Deserialize(jsonObject.ToString()); - } - - throw new ArgumentException($"Unsupported speechConfig type:{speechConfig.GetType()}"); - } - - /// Transforms an object to a list of Tools for the API. - /// If the object is not a supported type. - internal static List? TTools(object origin) - { - if (origin == null) - { - return null; - } - else if (origin is List tools) - { - List transformedTools = new List(); - foreach (Tool tool in tools) - { - transformedTools.Add(TTool(tool)!); - } - return transformedTools; - } - else if (origin is JsonArray jsonArray) - { - List toolList = new List(); - foreach(JsonNode? toolNode in jsonArray) - { - if(toolNode != null) - { - toolList.Add(TTool(toolNode)!); - } - } - return toolList; - } - else if (origin is JsonNode jsonNode) - { - return JsonSerializer.Deserialize>(jsonNode.ToJsonString()); - } - - throw new ArgumentException($"Unsupported tools type: {origin.GetType()}"); - } - - /// Transforms an object to a Tool for the API. - /// If the object is not a supported type. - internal static Tool? TTool(object origin) - { - if (origin == null) - { - return null; - } - else if (origin is Tool tool) - { - return tool; - } - else if (origin is JsonNode jsonNode) - { - return JsonSerializer.Deserialize(jsonNode.ToJsonString()); - } - throw new ArgumentException($"Unsupported tool type: {origin.GetType()}"); - } - - /// Dummy Blobs transformer. - internal static JsonArray TBlobs(object origin) - { - JsonNode inputNode; - - if (origin is not JsonNode) - { - inputNode = JsonNode.Parse(JsonSerializer.Serialize(origin, JsonConfig.JsonSerializerOptions))!; - } - else - { - inputNode = (JsonNode)origin; - } - - if (inputNode is JsonArray existingArray) - { - return existingArray; - } - - JsonArray arrayNode = new JsonArray(); - arrayNode.Add(JsonNode.Parse(JsonSerializer.Serialize(TBlob(origin), JsonConfig.JsonSerializerOptions))); - return arrayNode; - } - - internal static Blob TBlob(object blob) - { - if (blob is JsonObject jsonObject) - { - blob = JsonSerializer.Deserialize(jsonObject.ToString()); - } - - if (blob is Blob b) - { - return b; - } - else - { - throw new ArgumentException($"Unsupported blob type: {blob.GetType()}"); - } - } - - /// - /// Transforms a blob to an image blob, validating its mime type. - /// - /// The object to transform, can be a Blob or a dictionary. - /// The transformed Blob if it is an image. - /// If the blob is not an image. - internal static Blob TImageBlob(object blob) - { - Blob transformedBlob = TBlob(blob); - if (!string.IsNullOrEmpty(transformedBlob.MimeType) - && transformedBlob.MimeType.StartsWith("image/")) - { - return transformedBlob; - } - throw new ArgumentException( - $"Unsupported mime type for image blob: {transformedBlob.MimeType ?? "null"}"); - } - - /// - /// Transforms a blob to an audio blob, validating its mime type. - /// - /// The object to transform, can be a Blob or a dictionary. - /// The transformed Blob if it is an audio. - /// If the blob is not an audio. - internal static Blob TAudioBlob(object blob) - { - Blob transformedBlob = TBlob(blob); - if (!string.IsNullOrEmpty(transformedBlob.MimeType) - && transformedBlob.MimeType.StartsWith("audio/")) - { - return transformedBlob; - } - throw new ArgumentException( - $"Unsupported mime type for audio blob: {transformedBlob.MimeType ?? "null"}"); - } - - /// Dummy bytes transformer. - internal static object TBytes(object origin) - { - // TODO(b/389133914): Remove dummy bytes converter. - return origin; - } - - /// Transforms a list models response object to a list of models. - internal static JsonArray TExtractModels(JsonNode models) - { - if (models == null) - { - return new JsonArray(); - } - if (models is JsonObject modelsObj) - { - if (modelsObj.ContainsKey("models")) - { - return (JsonArray)modelsObj["models"]!; - } - if (modelsObj.ContainsKey("tunedModels")) - { - return (JsonArray)modelsObj["tunedModels"]!; - } - if (modelsObj.ContainsKey("publisherModels")) - { - return (JsonArray)modelsObj["publisherModels"]!; - } - } - return new JsonArray(); - } - - /// Transforms an object to a cached content name for the API. - internal static string? TCachedContentName(ApiClient apiClient, object origin) - { - if (origin == null) - { - return null; - } - else if (origin is string strOrigin) - { - return GetResourceName(apiClient, strOrigin, "cachedContents"); - } - else if (origin is JsonNode jsonNode) - { - string cachedContentName = jsonNode.ToString(); - cachedContentName = cachedContentName.Replace("\"", ""); - return GetResourceName(apiClient, cachedContentName, "cachedContents"); - } - - throw new ArgumentException( - $"Unsupported cached content name type: {origin.GetType()}"); - } - - /// Transforms an object to a list of Content for the embedding API. - internal static List? TContentsForEmbed(ApiClient apiClient, object origin) - { - if (origin == null) - { - return null; - } - - List? contents; - if (origin is List contentList) - { - contents = contentList; - } - else if (origin is JsonNode jsonNode) - { - contents = JsonSerializer.Deserialize>(jsonNode.ToJsonString()); - } - else - { - throw new ArgumentException($"Unsupported contents type: {origin.GetType()}"); - } - - List result = new List(); - if (contents != null) - { - foreach (Content content in contents) - { - if (!apiClient.VertexAI) - { - result.Add(content); - } - else - { - if (content.Parts != null) - { - foreach (Part part in content.Parts) - { - if (part.Text != null) - { - result.Add(part.Text); - } - } - } - } - } - } - return result; - } - - /// - /// Transforms a model name to the correct format for the Caches API. - /// - /// The API client to use for transformation - /// The model name to transform, can be a string or JsonNode - /// The transformed model name, or null if the input is null - /// If the object is not a supported type - internal static string? TCachesModel(ApiClient apiClient, object origin) - { - string? model = TModel(apiClient, origin); - if (model == null) - { - return null; - } - - if (apiClient.VertexAI) - { - if (model.StartsWith("publishers/")) - { - // Vertex caches only support model names starting with projects. - return string.Format( - "projects/{0}/locations/{1}/{2}", apiClient.Project, apiClient.Location, model); - } - else if (model.StartsWith("models/")) - { - return string.Format( - "projects/{0}/locations/{1}/publishers/google/{2}", - apiClient.Project, apiClient.Location, model); - } - } - return model; - } - - internal static string? TFileName(object? origin) - { - string? name = null; - - if (origin is string strName) - { - name = strName; - } - else if (origin == null) - { - return null; - } - else if (origin is JsonNode jsonNode) - { - name = jsonNode.ToString(); - name = name.Replace("\"", ""); - } - else - { - throw new ArgumentException($"Unsupported file name type: {origin.GetType()}"); - } - - if (name.StartsWith("https://")) - { -#if NET - string suffix = name.Split("files/")[1]; -#else - string suffix = name.Split(new[] { "files/" }, StringSplitOptions.None)[1]; -#endif - Match match = Regex.Match(suffix, "[a-z0-9]+"); - if (match.Success) - { - name = match.Value; - } - else - { - throw new ArgumentException($"Could not extract file name from {name}"); - } - } - else if (name.StartsWith("files/")) - { -#if NET - name = name.Split("files/")[1]; -#else - name = name.Split(new[] { "files/" }, StringSplitOptions.None)[1]; -#endif - } - - return name; - } - - internal static bool TIsVertexEmbedContentModel(string model) - { - // Gemini Embeddings except gemini-embedding-001. - return (model.Contains("gemini") && model != "gemini-embedding-001") - // Open-source MaaS embedding models. - || model.Contains("maas"); - } - - /// Formats a resource name given the resource name and resource prefix. - internal static string GetResourceName( - ApiClient apiClient, string resourceName, string resourcePrefix) - { - if (apiClient.VertexAI) - { - if (resourceName.StartsWith("projects/")) - { - return resourceName; - } - else if (resourceName.StartsWith("locations/")) - { - return string.Format("projects/{0}/{1}", apiClient.Project, resourceName); - } - else if (resourceName.StartsWith(resourcePrefix + "/")) - { - return string.Format( - "projects/{0}/locations/{1}/{2}", apiClient.Project, apiClient.Location, resourceName); - } - else - { - return string.Format( - "projects/{0}/locations/{1}/{2}/{3}", - apiClient.Project, apiClient.Location, resourcePrefix, resourceName); - } - } - else - { - if (resourceName.StartsWith(resourcePrefix + "/")) - { - return resourceName; - } - else - { - return string.Format("{0}/{1}", resourcePrefix, resourceName); - } - } - } - - internal static JsonNode TTuningJobStatus(JsonNode origin) - { - string? status = origin.GetValue(); - switch (status) - { - case "ACTIVE": - return JsonValue.Create("JOB_STATE_SUCCEEDED")!; - case "CREATING": - return JsonValue.Create("JOB_STATE_RUNNING")!; - case "FAILED": - return JsonValue.Create("JOB_STATE_FAILED")!; - case "STATE_UNSPECIFIED": - return JsonValue.Create("JOB_STATE_UNSPECIFIED")!; - default: - return origin; - } - } - - internal static JsonNode TBatchJobName(ApiClient apiClient, JsonNode origin) - { - string nameStr = origin.ToString().Replace("\"", ""); - if (!apiClient.VertexAI) - { - Regex mldevRegex = new Regex(@"batches/[^/]+$"); - if (mldevRegex.IsMatch(nameStr)) - { - return JsonValue.Create(nameStr.Split('/').Last()); - } - else - { - throw new ArgumentException($"Invalid batch job name: {nameStr}."); - } - } - - Regex vertexRegex = new Regex(@"^projects/[^/]+/locations/[^/]+/batchPredictionJobs/[^/]+$"); - nameStr = GetResourceName(apiClient, nameStr, "batchPredictionJobs"); - if (vertexRegex.IsMatch(nameStr)) - { - return JsonValue.Create(nameStr.Split('/').Last()); - } - else if (nameStr.All(char.IsDigit)) - { - return JsonValue.Create(nameStr); - } - else - { - throw new ArgumentException($"Invalid batch job name: {nameStr}."); - } - } - - internal static JsonNode TBatchJobSource(JsonNode origin) - { - return origin; - } - - internal static JsonNode TBatchJobDestination(JsonNode origin) - { - return origin; - } - - internal static JsonNode TJobState(JsonNode origin) - { - string? stateStr = origin.GetValue(); - switch (stateStr) - { - case "BATCH_STATE_UNSPECIFIED": - return JsonValue.Create("JOB_STATE_UNSPECIFIED"); - case "BATCH_STATE_PENDING": - return JsonValue.Create("JOB_STATE_PENDING"); - case "BATCH_STATE_RUNNING": - return JsonValue.Create("JOB_STATE_RUNNING"); - case "BATCH_STATE_SUCCEEDED": - return JsonValue.Create("JOB_STATE_SUCCEEDED"); - case "BATCH_STATE_FAILED": - return JsonValue.Create("JOB_STATE_FAILED"); - case "BATCH_STATE_CANCELLED": - return JsonValue.Create("JOB_STATE_CANCELLED"); - case "BATCH_STATE_EXPIRED": - return JsonValue.Create("JOB_STATE_EXPIRED"); - default: - return origin; - } - } - - internal static JsonNode TRecvBatchJobDestination(JsonNode origin) - { - return origin; - } - - /// - /// Transforms a SpeechConfig object for the live API, validating it. - /// - /// The object to transform, can be a SpeechConfig or a JsonNode. - /// The transformed SpeechConfig. - /// If the object is not a supported type. - /// If multiSpeakerVoiceConfig is present (as it's not supported in the live API). - internal static SpeechConfig? TLiveSpeechConfig(object origin) - { - SpeechConfig? speechConfig; - if (origin == null) - { - return null; - } - else if (origin is SpeechConfig config) - { - speechConfig = config; - } - else if (origin is JsonNode jsonNode) - { - speechConfig = JsonSerializer.Deserialize(jsonNode.ToJsonString()); - } - else - { - throw new ArgumentException($"Unsupported speechConfig type: {origin.GetType()}"); - } - - if (speechConfig?.MultiSpeakerVoiceConfig != null) - { - throw new NotSupportedException("multiSpeakerVoiceConfig parameter is not supported in the live API."); - } - - return speechConfig; - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; + +using Google.GenAI.Types; + + +namespace Google.GenAI +{ + /// + /// Transformers for GenAI SDK. + /// + internal static class Transformers + { + /// + /// Transforms a model name to the correct format for the API. + /// + /// The API client to use for transformation. + /// The model name to transform, can only be a string. + /// The transformed model name. + /// If the object is not a supported type. + internal static string? TModel(ApiClient apiClient, object origin) + { + string? model; + if (origin == null) + { + return null; + } + else if (origin is string strModel) + { + model = strModel; + } + else if (origin is JsonNode jsonNode) + { + model = jsonNode.ToString(); + model = model.Replace("\"", ""); + } + else + { + throw new ArgumentException($"Unsupported model type: {origin.GetType()}"); + } + + if (model.Length == 0) + { + throw new ArgumentException("model is required."); + } + if (model.Contains("..") || model.Contains("?") || model.Contains("&")) + { + throw new ArgumentException("invalid model parameter."); + } + if (apiClient.VertexAI) + { + if (model.StartsWith("publishers/") + || model.StartsWith("projects/") + || model.StartsWith("models/")) + { + return model; + } + else if (model.Contains("/")) + { +#if NETSTANDARD2_0 + string[] parts = model.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries); +#else + string[] parts = model.Split('/', 2, StringSplitOptions.RemoveEmptyEntries); +#endif + return string.Format("publishers/{0}/models/{1}", parts[0], parts[1]); + } + else + { + return "publishers/google/models/" + model; + } + } + else + { + if (model.StartsWith("models/") || model.StartsWith("tunedModels/")) + { + return model; + } + else + { + return "models/" + model; + } + } + } + + /// + /// Determines the appropriate models URL based on the API client type and whether base models are + /// requested. + /// + /// The API client to use for transformation. + /// True if base models are requested, false otherwise. + /// The transformed model name + internal static string TModelsUrl(ApiClient apiClient, object? baseModels) + { + bool queryBase = true; + if (baseModels is JsonValue val) + { + queryBase = val.GetValue(); + } + if (queryBase) + { + return apiClient.VertexAI ? "publishers/google/models" : "models"; + } + else + { + return apiClient.VertexAI ? "models" : "tunedModels"; + } + } + + /// + /// Transforms an object to a list of Content for the API. + /// + /// The object to transform, can be a string, Content, or List<Content> + /// The transformed list of Content + /// If the object is not a supported type + internal static List? TContents(object? contents) + { + if (contents == null) + { + return null; + } + if (contents is string contentString) + { + Content content = new Content(); + content.Role = "user"; + Part part = new Part(); + part.Text = contentString; + content.Parts = new List { part }; + return new List { content }; + } + else if (contents is Content singleContent) + { + return new List { singleContent }; + } + else if (contents is List contentList) + { + return contentList; + } + else if (contents is JsonObject jsonObject) + { + return JsonSerializer.Deserialize>(jsonObject.ToString(), JsonConfig.JsonSerializerOptions); + } + else if (contents is JsonNode jsonNode) + { + return JsonSerializer.Deserialize>(jsonNode.ToString(), JsonConfig.JsonSerializerOptions); + } + + throw new ArgumentException($"Unsupported contents type: {contents.GetType()}"); + } + + /// + /// Transforms an object to a Content for the API. + /// + /// The object to transform, can be a string or Content + /// The transformed Content + /// If the object is not a supported type + internal static Content? TContent(object content) + { + if (content == null) + { + return null; + } + else if (content is string contentString) + { + Content contentObject = new Content(); + contentObject.Role = "user"; + Part part = new Part(); + part.Text = contentString; + contentObject.Parts = new List { part }; + return contentObject; + } + else if (content is Content singleContent) + { + return singleContent; + } + else if (content is JsonObject jsonObject) + { + return JsonSerializer.Deserialize(jsonObject.ToString(), JsonConfig.JsonSerializerOptions); + } + + throw new ArgumentException($"Unsupported content type: {content.GetType()}"); + } + + /// Transforms an object to a Schema for the API. + /// If the object is not a supported type. + internal static Schema? TSchema(object origin) + { + if (origin == null) + { + return null; + } + else if (origin is Schema schema) + { + return schema; + } + else if (origin is JsonObject jsonObject) + { + return JsonSerializer.Deserialize(jsonObject.ToString(), JsonConfig.JsonSerializerOptions); + } + throw new ArgumentException($"Unsupported schema type: {origin.GetType()}"); + } + + internal static SpeechConfig? TSpeechConfig(object speechConfig) + { + if (speechConfig == null) + { + return null; + } + else if (speechConfig is string speechConfigString) + { + return null; + } + else if (speechConfig is SpeechConfig config) + { + return config; + } + else if (speechConfig is JsonObject jsonObject) + { + return JsonSerializer.Deserialize(jsonObject.ToString(), JsonConfig.JsonSerializerOptions); + } + + throw new ArgumentException($"Unsupported speechConfig type:{speechConfig.GetType()}"); + } + + /// Transforms an object to a list of Tools for the API. + /// If the object is not a supported type. + internal static List? TTools(object origin) + { + if (origin == null) + { + return null; + } + else if (origin is List tools) + { + List transformedTools = new List(); + foreach (Tool tool in tools) + { + transformedTools.Add(TTool(tool)!); + } + return transformedTools; + } + else if (origin is JsonArray jsonArray) + { + List toolList = new List(); + foreach(JsonNode? toolNode in jsonArray) + { + if(toolNode != null) + { + toolList.Add(TTool(toolNode)!); + } + } + return toolList; + } + else if (origin is JsonNode jsonNode) + { + return JsonSerializer.Deserialize>(jsonNode.ToJsonString(), JsonConfig.JsonSerializerOptions); + } + + throw new ArgumentException($"Unsupported tools type: {origin.GetType()}"); + } + + /// Transforms an object to a Tool for the API. + /// If the object is not a supported type. + internal static Tool? TTool(object origin) + { + if (origin == null) + { + return null; + } + else if (origin is Tool tool) + { + return tool; + } + else if (origin is JsonNode jsonNode) + { + return JsonSerializer.Deserialize(jsonNode.ToJsonString(), JsonConfig.JsonSerializerOptions); + } + throw new ArgumentException($"Unsupported tool type: {origin.GetType()}"); + } + + /// Dummy Blobs transformer. + internal static JsonArray TBlobs(object origin) + { + JsonNode inputNode; + + if (origin is not JsonNode) + { + inputNode = JsonNode.Parse(JsonSerializer.Serialize(origin, JsonConfig.JsonSerializerOptions))!; + } + else + { + inputNode = (JsonNode)origin; + } + + if (inputNode is JsonArray existingArray) + { + return existingArray; + } + + JsonArray arrayNode = new JsonArray(); + arrayNode.Add(JsonNode.Parse(JsonSerializer.Serialize(TBlob(origin), JsonConfig.InternalSerializerOptions))); + return arrayNode; + } + + internal static Blob TBlob(object blob) + { + if (blob is JsonObject jsonObject) + { + blob = JsonSerializer.Deserialize(jsonObject.ToString(), JsonConfig.JsonSerializerOptions); + } + + if (blob is Blob b) + { + return b; + } + else + { + throw new ArgumentException($"Unsupported blob type: {blob.GetType()}"); + } + } + + /// + /// Transforms a blob to an image blob, validating its mime type. + /// + /// The object to transform, can be a Blob or a dictionary. + /// The transformed Blob if it is an image. + /// If the blob is not an image. + internal static Blob TImageBlob(object blob) + { + Blob transformedBlob = TBlob(blob); + if (!string.IsNullOrEmpty(transformedBlob.MimeType) + && transformedBlob.MimeType.StartsWith("image/")) + { + return transformedBlob; + } + throw new ArgumentException( + $"Unsupported mime type for image blob: {transformedBlob.MimeType ?? "null"}"); + } + + /// + /// Transforms a blob to an audio blob, validating its mime type. + /// + /// The object to transform, can be a Blob or a dictionary. + /// The transformed Blob if it is an audio. + /// If the blob is not an audio. + internal static Blob TAudioBlob(object blob) + { + Blob transformedBlob = TBlob(blob); + if (!string.IsNullOrEmpty(transformedBlob.MimeType) + && transformedBlob.MimeType.StartsWith("audio/")) + { + return transformedBlob; + } + throw new ArgumentException( + $"Unsupported mime type for audio blob: {transformedBlob.MimeType ?? "null"}"); + } + + /// Dummy bytes transformer. + internal static object TBytes(object origin) + { + // TODO(b/389133914): Remove dummy bytes converter. + return origin; + } + + /// Transforms a list models response object to a list of models. + internal static JsonArray TExtractModels(JsonNode models) + { + if (models == null) + { + return new JsonArray(); + } + if (models is JsonObject modelsObj) + { + if (modelsObj.ContainsKey("models")) + { + return (JsonArray)modelsObj["models"]!; + } + if (modelsObj.ContainsKey("tunedModels")) + { + return (JsonArray)modelsObj["tunedModels"]!; + } + if (modelsObj.ContainsKey("publisherModels")) + { + return (JsonArray)modelsObj["publisherModels"]!; + } + } + return new JsonArray(); + } + + /// Transforms an object to a cached content name for the API. + internal static string? TCachedContentName(ApiClient apiClient, object origin) + { + if (origin == null) + { + return null; + } + else if (origin is string strOrigin) + { + return GetResourceName(apiClient, strOrigin, "cachedContents"); + } + else if (origin is JsonNode jsonNode) + { + string cachedContentName = jsonNode.ToString(); + cachedContentName = cachedContentName.Replace("\"", ""); + return GetResourceName(apiClient, cachedContentName, "cachedContents"); + } + + throw new ArgumentException( + $"Unsupported cached content name type: {origin.GetType()}"); + } + + /// Transforms an object to a list of Content for the embedding API. + internal static List? TContentsForEmbed(ApiClient apiClient, object origin) + { + if (origin == null) + { + return null; + } + + List? contents; + if (origin is List contentList) + { + contents = contentList; + } + else if (origin is JsonNode jsonNode) + { + contents = JsonSerializer.Deserialize>(jsonNode.ToJsonString(), JsonConfig.JsonSerializerOptions); + } + else + { + throw new ArgumentException($"Unsupported contents type: {origin.GetType()}"); + } + + List result = new List(); + if (contents != null) + { + foreach (Content content in contents) + { + if (!apiClient.VertexAI) + { + result.Add(content); + } + else + { + if (content.Parts != null) + { + foreach (Part part in content.Parts) + { + if (part.Text != null) + { + result.Add(part.Text); + } + } + } + } + } + } + return result; + } + + /// + /// Transforms a model name to the correct format for the Caches API. + /// + /// The API client to use for transformation + /// The model name to transform, can be a string or JsonNode + /// The transformed model name, or null if the input is null + /// If the object is not a supported type + internal static string? TCachesModel(ApiClient apiClient, object origin) + { + string? model = TModel(apiClient, origin); + if (model == null) + { + return null; + } + + if (apiClient.VertexAI) + { + if (model.StartsWith("publishers/")) + { + // Vertex caches only support model names starting with projects. + return string.Format( + "projects/{0}/locations/{1}/{2}", apiClient.Project, apiClient.Location, model); + } + else if (model.StartsWith("models/")) + { + return string.Format( + "projects/{0}/locations/{1}/publishers/google/{2}", + apiClient.Project, apiClient.Location, model); + } + } + return model; + } + + internal static string? TFileName(object? origin) + { + string? name = null; + + if (origin is string strName) + { + name = strName; + } + else if (origin == null) + { + return null; + } + else if (origin is JsonNode jsonNode) + { + name = jsonNode.ToString(); + name = name.Replace("\"", ""); + } + else + { + throw new ArgumentException($"Unsupported file name type: {origin.GetType()}"); + } + + if (name.StartsWith("https://")) + { +#if NET + string suffix = name.Split("files/")[1]; +#else + string suffix = name.Split(new[] { "files/" }, StringSplitOptions.None)[1]; +#endif + Match match = Regex.Match(suffix, "[a-z0-9]+"); + if (match.Success) + { + name = match.Value; + } + else + { + throw new ArgumentException($"Could not extract file name from {name}"); + } + } + else if (name.StartsWith("files/")) + { +#if NET + name = name.Split("files/")[1]; +#else + name = name.Split(new[] { "files/" }, StringSplitOptions.None)[1]; +#endif + } + + return name; + } + + internal static bool TIsVertexEmbedContentModel(string model) + { + // Gemini Embeddings except gemini-embedding-001. + return (model.Contains("gemini") && model != "gemini-embedding-001") + // Open-source MaaS embedding models. + || model.Contains("maas"); + } + + /// Formats a resource name given the resource name and resource prefix. + internal static string GetResourceName( + ApiClient apiClient, string resourceName, string resourcePrefix) + { + if (apiClient.VertexAI) + { + if (resourceName.StartsWith("projects/")) + { + return resourceName; + } + else if (resourceName.StartsWith("locations/")) + { + return string.Format("projects/{0}/{1}", apiClient.Project, resourceName); + } + else if (resourceName.StartsWith(resourcePrefix + "/")) + { + return string.Format( + "projects/{0}/locations/{1}/{2}", apiClient.Project, apiClient.Location, resourceName); + } + else + { + return string.Format( + "projects/{0}/locations/{1}/{2}/{3}", + apiClient.Project, apiClient.Location, resourcePrefix, resourceName); + } + } + else + { + if (resourceName.StartsWith(resourcePrefix + "/")) + { + return resourceName; + } + else + { + return string.Format("{0}/{1}", resourcePrefix, resourceName); + } + } + } + + internal static JsonNode TTuningJobStatus(JsonNode origin) + { + string? status = origin.GetValue(); + switch (status) + { + case "ACTIVE": + return JsonValue.Create("JOB_STATE_SUCCEEDED")!; + case "CREATING": + return JsonValue.Create("JOB_STATE_RUNNING")!; + case "FAILED": + return JsonValue.Create("JOB_STATE_FAILED")!; + case "STATE_UNSPECIFIED": + return JsonValue.Create("JOB_STATE_UNSPECIFIED")!; + default: + return origin; + } + } + + internal static JsonNode TBatchJobName(ApiClient apiClient, JsonNode origin) + { + string nameStr = origin.ToString().Replace("\"", ""); + if (!apiClient.VertexAI) + { + Regex mldevRegex = new Regex(@"batches/[^/]+$"); + if (mldevRegex.IsMatch(nameStr)) + { + return JsonValue.Create(nameStr.Split('/').Last()); + } + else + { + throw new ArgumentException($"Invalid batch job name: {nameStr}."); + } + } + + Regex vertexRegex = new Regex(@"^projects/[^/]+/locations/[^/]+/batchPredictionJobs/[^/]+$"); + nameStr = GetResourceName(apiClient, nameStr, "batchPredictionJobs"); + if (vertexRegex.IsMatch(nameStr)) + { + return JsonValue.Create(nameStr.Split('/').Last()); + } + else if (nameStr.All(char.IsDigit)) + { + return JsonValue.Create(nameStr); + } + else + { + throw new ArgumentException($"Invalid batch job name: {nameStr}."); + } + } + + internal static JsonNode TBatchJobSource(JsonNode origin) + { + return origin; + } + + internal static JsonNode TBatchJobDestination(JsonNode origin) + { + return origin; + } + + internal static JsonNode TJobState(JsonNode origin) + { + string? stateStr = origin.GetValue(); + switch (stateStr) + { + case "BATCH_STATE_UNSPECIFIED": + return JsonValue.Create("JOB_STATE_UNSPECIFIED"); + case "BATCH_STATE_PENDING": + return JsonValue.Create("JOB_STATE_PENDING"); + case "BATCH_STATE_RUNNING": + return JsonValue.Create("JOB_STATE_RUNNING"); + case "BATCH_STATE_SUCCEEDED": + return JsonValue.Create("JOB_STATE_SUCCEEDED"); + case "BATCH_STATE_FAILED": + return JsonValue.Create("JOB_STATE_FAILED"); + case "BATCH_STATE_CANCELLED": + return JsonValue.Create("JOB_STATE_CANCELLED"); + case "BATCH_STATE_EXPIRED": + return JsonValue.Create("JOB_STATE_EXPIRED"); + default: + return origin; + } + } + + internal static JsonNode TRecvBatchJobDestination(JsonNode origin) + { + return origin; + } + + /// + /// Transforms a SpeechConfig object for the live API, validating it. + /// + /// The object to transform, can be a SpeechConfig or a JsonNode. + /// The transformed SpeechConfig. + /// If the object is not a supported type. + /// If multiSpeakerVoiceConfig is present (as it's not supported in the live API). + internal static SpeechConfig? TLiveSpeechConfig(object origin) + { + SpeechConfig? speechConfig; + if (origin == null) + { + return null; + } + else if (origin is SpeechConfig config) + { + speechConfig = config; + } + else if (origin is JsonNode jsonNode) + { + speechConfig = JsonSerializer.Deserialize(jsonNode.ToJsonString(), JsonConfig.JsonSerializerOptions); + } + else + { + throw new ArgumentException($"Unsupported speechConfig type: {origin.GetType()}"); + } + + if (speechConfig?.MultiSpeakerVoiceConfig != null) + { + throw new NotSupportedException("multiSpeakerVoiceConfig parameter is not supported in the live API."); + } + + return speechConfig; + } + } +} diff --git a/Google.GenAI/Tunings.cs b/Google.GenAI/Tunings.cs index 6b7de32b..5d63d019 100644 --- a/Google.GenAI/Tunings.cs +++ b/Google.GenAI/Tunings.cs @@ -1,1840 +1,1840 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - - public sealed class Tunings { - private readonly ApiClient _apiClient; - - internal JsonNode CancelTuningJobParametersToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - return toObject; - } - - internal JsonNode CancelTuningJobParametersToVertex(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - return toObject; - } - - internal JsonNode CancelTuningJobResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode CancelTuningJobResponseFromVertex(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode CreateTuningJobConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "validationDataset" }))) { - throw new NotSupportedException( - "validationDataset parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "description" }))) { - throw new NotSupportedException("description parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "tuningTask", "hyperparameters", "epochCount" }, - Common.GetValueByPath(fromObject, new string[] { "epochCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "tuningTask", "hyperparameters", "learningRateMultiplier" }, - Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }))) { - throw new NotSupportedException( - "exportLastCheckpointOnly parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "preTunedModelCheckpointId" }))) { - throw new NotSupportedException( - "preTunedModelCheckpointId parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "adapterSize" }))) { - throw new NotSupportedException("adapterSize parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "tuningMode" }))) { - throw new NotSupportedException("tuningMode parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "customBaseModel" }))) { - throw new NotSupportedException( - "customBaseModel parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "batchSize" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "tuningTask", "hyperparameters", "batchSize" }, - Common.GetValueByPath(fromObject, new string[] { "batchSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "learningRate" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "tuningTask", "hyperparameters", "learningRate" }, - Common.GetValueByPath(fromObject, new string[] { "learningRate" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { - throw new NotSupportedException("labels parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "beta" }))) { - throw new NotSupportedException("beta parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "baseTeacherModel" }))) { - throw new NotSupportedException( - "baseTeacherModel parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "tunedTeacherModelSource" }))) { - throw new NotSupportedException( - "tunedTeacherModelSource parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "sftLossWeightMultiplier" }))) { - throw new NotSupportedException( - "sftLossWeightMultiplier parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputUri" }))) { - throw new NotSupportedException("outputUri parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" }))) { - throw new NotSupportedException("encryptionSpec parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode CreateTuningJobConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - JsonNode discriminatorValidationDataset = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueValidationDataset = - discriminatorValidationDataset == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorValidationDataset.GetValue(); - if (discriminatorValueValidationDataset == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "validationDataset" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "supervisedTuningSpec" }, - TuningValidationDatasetToVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "validationDataset" })), - toObject, rootObject)); - } - } else if (discriminatorValueValidationDataset == "PREFERENCE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "validationDataset" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "preferenceOptimizationSpec" }, - TuningValidationDatasetToVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "validationDataset" })), - toObject, rootObject)); - } - } else if (discriminatorValueValidationDataset == "DISTILLATION") { - if (Common.GetValueByPath(fromObject, new string[] { "validationDataset" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "distillationSpec" }, - TuningValidationDatasetToVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "validationDataset" })), - toObject, rootObject)); - } - } - if (Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "tunedModelDisplayName" }, - Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - JsonNode discriminatorEpochCount = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueEpochCount = discriminatorEpochCount == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorEpochCount.GetValue(); - if (discriminatorValueEpochCount == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "supervisedTuningSpec", "hyperParameters", "epochCount" }, - Common.GetValueByPath(fromObject, new string[] { "epochCount" })); - } - } else if (discriminatorValueEpochCount == "PREFERENCE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "preferenceOptimizationSpec", "hyperParameters", "epochCount" }, - Common.GetValueByPath(fromObject, new string[] { "epochCount" })); - } - } else if (discriminatorValueEpochCount == "DISTILLATION") { - if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "distillationSpec", "hyperParameters", "epochCount" }, - Common.GetValueByPath(fromObject, new string[] { "epochCount" })); - } - } - - JsonNode discriminatorLearningRateMultiplier = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueLearningRateMultiplier = - discriminatorLearningRateMultiplier == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorLearningRateMultiplier.GetValue(); - if (discriminatorValueLearningRateMultiplier == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "supervisedTuningSpec", "hyperParameters", "learningRateMultiplier" }, - Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); - } - } else if (discriminatorValueLearningRateMultiplier == "PREFERENCE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "preferenceOptimizationSpec", "hyperParameters", - "learningRateMultiplier" }, - Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); - } - } else if (discriminatorValueLearningRateMultiplier == "DISTILLATION") { - if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "distillationSpec", "hyperParameters", "learningRateMultiplier" }, - Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); - } - } - - JsonNode discriminatorExportLastCheckpointOnly = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueExportLastCheckpointOnly = - discriminatorExportLastCheckpointOnly == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorExportLastCheckpointOnly.GetValue(); - if (discriminatorValueExportLastCheckpointOnly == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }) != - null) { - Common.SetValueByPath( - parentObject, new string[] { "supervisedTuningSpec", "exportLastCheckpointOnly" }, - Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" })); - } - } else if (discriminatorValueExportLastCheckpointOnly == "PREFERENCE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }) != - null) { - Common.SetValueByPath( - parentObject, - new string[] { "preferenceOptimizationSpec", "exportLastCheckpointOnly" }, - Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" })); - } - } else if (discriminatorValueExportLastCheckpointOnly == "DISTILLATION") { - if (Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }) != - null) { - Common.SetValueByPath( - parentObject, new string[] { "distillationSpec", "exportLastCheckpointOnly" }, - Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" })); - } - } - - JsonNode discriminatorAdapterSize = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueAdapterSize = discriminatorAdapterSize == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorAdapterSize.GetValue(); - if (discriminatorValueAdapterSize == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "adapterSize" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "supervisedTuningSpec", "hyperParameters", "adapterSize" }, - Common.GetValueByPath(fromObject, new string[] { "adapterSize" })); - } - } else if (discriminatorValueAdapterSize == "PREFERENCE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "adapterSize" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "preferenceOptimizationSpec", "hyperParameters", "adapterSize" }, - Common.GetValueByPath(fromObject, new string[] { "adapterSize" })); - } - } else if (discriminatorValueAdapterSize == "DISTILLATION") { - if (Common.GetValueByPath(fromObject, new string[] { "adapterSize" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "distillationSpec", "hyperParameters", "adapterSize" }, - Common.GetValueByPath(fromObject, new string[] { "adapterSize" })); - } - } - - JsonNode discriminatorTuningMode = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueTuningMode = discriminatorTuningMode == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorTuningMode.GetValue(); - if (discriminatorValueTuningMode == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "tuningMode" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "supervisedTuningSpec", "tuningMode" }, - Common.GetValueByPath(fromObject, new string[] { "tuningMode" })); - } - } - if (Common.GetValueByPath(fromObject, new string[] { "customBaseModel" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "customBaseModel" }, - Common.GetValueByPath(fromObject, new string[] { "customBaseModel" })); - } - - JsonNode discriminatorBatchSize = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueBatchSize = discriminatorBatchSize == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorBatchSize.GetValue(); - if (discriminatorValueBatchSize == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "batchSize" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "supervisedTuningSpec", "hyperParameters", "batchSize" }, - Common.GetValueByPath(fromObject, new string[] { "batchSize" })); - } - } - - JsonNode discriminatorLearningRate = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueLearningRate = discriminatorLearningRate == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorLearningRate.GetValue(); - if (discriminatorValueLearningRate == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "learningRate" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "supervisedTuningSpec", "hyperParameters", "learningRate" }, - Common.GetValueByPath(fromObject, new string[] { "learningRate" })); - } - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "beta" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "preferenceOptimizationSpec", "hyperParameters", "beta" }, - Common.GetValueByPath(fromObject, new string[] { "beta" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "baseTeacherModel" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "distillationSpec", "baseTeacherModel" }, - Common.GetValueByPath(fromObject, new string[] { "baseTeacherModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tunedTeacherModelSource" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "distillationSpec", "tunedTeacherModelSource" }, - Common.GetValueByPath(fromObject, new string[] { "tunedTeacherModelSource" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "sftLossWeightMultiplier" }) != null) { - Common.SetValueByPath( - parentObject, - new string[] { "distillationSpec", "hyperParameters", "sftLossWeightMultiplier" }, - Common.GetValueByPath(fromObject, new string[] { "sftLossWeightMultiplier" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputUri" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "outputUri" }, - Common.GetValueByPath(fromObject, new string[] { "outputUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "encryptionSpec" }, - Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" })); - } - - return toObject; - } - - internal JsonNode CreateTuningJobParametersPrivateToMldev(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "baseModel" }, - Common.GetValueByPath(fromObject, new string[] { "baseModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "preTunedModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "preTunedModel" }, - Common.GetValueByPath(fromObject, new string[] { "preTunedModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "trainingDataset" }) != null) { - _ = TuningDatasetToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "trainingDataset" })), - toObject, rootObject); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateTuningJobConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode CreateTuningJobParametersPrivateToVertex(JsonNode fromObject, - JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "baseModel" }, - Common.GetValueByPath(fromObject, new string[] { "baseModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "preTunedModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "preTunedModel" }, - Common.GetValueByPath(fromObject, new string[] { "preTunedModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "trainingDataset" }) != null) { - _ = TuningDatasetToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "trainingDataset" })), - toObject, rootObject); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateTuningJobConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode EvaluationConfigFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode EvaluationConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode GenerationConfigFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "modelConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "modelSelectionConfig" }, - Common.GetValueByPath(fromObject, new string[] { "modelConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { - Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, - Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "candidateCount" }, - Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "enableAffectiveDialog" }, - Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "frequencyPenalty" }, - Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobs" }, - Common.GetValueByPath(fromObject, new string[] { "logprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "presencePenalty" }, - Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseMimeType" }, - Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "routingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(toObject, new string[] { "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "speechConfig" }, - Common.GetValueByPath(fromObject, new string[] { "speechConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { - Common.SetValueByPath(toObject, new string[] { "stopSequences" }, - Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - return toObject; - } - - internal JsonNode GenerationConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "modelConfig" }, - Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { - Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, - Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "candidateCount" }, - Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "enableAffectiveDialog" }, - Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "frequencyPenalty" }, - Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { - Common.SetValueByPath(toObject, new string[] { "logprobs" }, - Common.GetValueByPath(fromObject, new string[] { "logprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "presencePenalty" }, - Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseLogprobs" }, - Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseMimeType" }, - Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { - Common.SetValueByPath(toObject, new string[] { "responseSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "routingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(toObject, new string[] { "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "speechConfig" }, - SpeechConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "speechConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { - Common.SetValueByPath(toObject, new string[] { "stopSequences" }, - Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(toObject, new string[] { "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(toObject, new string[] { "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }))) { - throw new NotSupportedException( - "enableEnhancedCivicAnswers parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode GetTuningJobParametersToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - return toObject; - } - - internal JsonNode GetTuningJobParametersToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - return toObject; - } - - internal JsonNode ListTuningJobsConfigToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, - Common.GetValueByPath(fromObject, new string[] { "filter" })); - } - - return toObject; - } - - internal JsonNode ListTuningJobsConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, - Common.GetValueByPath(fromObject, new string[] { "filter" })); - } - - return toObject; - } - - internal JsonNode ListTuningJobsParametersToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListTuningJobsConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode ListTuningJobsParametersToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListTuningJobsConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject, rootObject); - } - - return toObject; - } - - internal JsonNode ListTuningJobsResponseFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tunedModels" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tunedModels" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(TuningJobFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "tuningJobs" }, result); - } - - return toObject; - } - - internal JsonNode ListTuningJobsResponseFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tuningJobs" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tuningJobs" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(TuningJobFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "tuningJobs" }, result); - } - - return toObject; - } - - internal JsonNode MultiSpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add( - SpeakerVoiceConfigToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); - } - Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); - } - - return toObject; - } - - internal JsonNode ReplicatedVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "voiceSampleAudio" }, - Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" })); - } - - return toObject; - } - - internal JsonNode SpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { - Common.SetValueByPath(toObject, new string[] { "speaker" }, - Common.GetValueByPath(fromObject, new string[] { "speaker" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, - VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode SpeechConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, - VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "languageCode" }, - Common.GetValueByPath(fromObject, new string[] { "languageCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, - MultiSpeakerVoiceConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "multiSpeakerVoiceConfig" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode TunedModelFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "model" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "endpoint" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - return toObject; - } - - internal JsonNode TuningDatasetToMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "gcsUri" }))) { - throw new NotSupportedException("gcsUri parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }))) { - throw new NotSupportedException( - "vertexDatasetResource parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "examples" }) != null) { - Common.SetValueByPath(toObject, new string[] { "examples", "examples" }, - Common.GetValueByPath(fromObject, new string[] { "examples" })); - } - - return toObject; - } - - internal JsonNode TuningDatasetToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - JsonNode discriminatorGcsUri = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueGcsUri = discriminatorGcsUri == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorGcsUri.GetValue(); - if (discriminatorValueGcsUri == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "supervisedTuningSpec", "trainingDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - } else if (discriminatorValueGcsUri == "PREFERENCE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "preferenceOptimizationSpec", "trainingDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - } else if (discriminatorValueGcsUri == "DISTILLATION") { - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "distillationSpec", "promptDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - } - - JsonNode discriminatorVertexDatasetResource = - Common.GetValueByPath(rootObject, new string[] { "config", "method" }); - string discriminatorValueVertexDatasetResource = - discriminatorVertexDatasetResource == null - ? "SUPERVISED_FINE_TUNING" - : discriminatorVertexDatasetResource.GetValue(); - if (discriminatorValueVertexDatasetResource == "SUPERVISED_FINE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "supervisedTuningSpec", "trainingDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); - } - } else if (discriminatorValueVertexDatasetResource == "PREFERENCE_TUNING") { - if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "preferenceOptimizationSpec", "trainingDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); - } - } else if (discriminatorValueVertexDatasetResource == "DISTILLATION") { - if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "distillationSpec", "promptDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); - } - } - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "examples" }))) { - throw new NotSupportedException("examples parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode TuningJobFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "state" }) != null) { - Common.SetValueByPath(toObject, new string[] { "state" }, - Transformers.TTuningJobStatus( - Common.GetValueByPath(fromObject, new string[] { "state" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "createTime" }, - Common.GetValueByPath(fromObject, new string[] { "createTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tuningTask", "startTime" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "startTime" }, - Common.GetValueByPath(fromObject, new string[] { "tuningTask", "startTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tuningTask", "completeTime" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "endTime" }, - Common.GetValueByPath(fromObject, new string[] { "tuningTask", "completeTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "updateTime" }, - Common.GetValueByPath(fromObject, new string[] { "updateTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "baseModel" }, - Common.GetValueByPath(fromObject, new string[] { "baseModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tunedModel" }, - TunedModelFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject, rootObject)); - } - - return toObject; - } - - internal JsonNode TuningJobFromVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "state" }) != null) { - Common.SetValueByPath(toObject, new string[] { "state" }, - Transformers.TTuningJobStatus( - Common.GetValueByPath(fromObject, new string[] { "state" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "createTime" }, - Common.GetValueByPath(fromObject, new string[] { "createTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "startTime" }, - Common.GetValueByPath(fromObject, new string[] { "startTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "endTime" }, - Common.GetValueByPath(fromObject, new string[] { "endTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "updateTime" }, - Common.GetValueByPath(fromObject, new string[] { "updateTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "baseModel" }, - Common.GetValueByPath(fromObject, new string[] { "baseModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tunedModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tunedModel" }, - Common.GetValueByPath(fromObject, new string[] { "tunedModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "preTunedModel" }) != null) { - Common.SetValueByPath(toObject, new string[] { "preTunedModel" }, - Common.GetValueByPath(fromObject, new string[] { "preTunedModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "supervisedTuningSpec" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "supervisedTuningSpec" }, - Common.GetValueByPath(fromObject, new string[] { "supervisedTuningSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "preferenceOptimizationSpec" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "preferenceOptimizationSpec" }, - Common.GetValueByPath(fromObject, new string[] { "preferenceOptimizationSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "distillationSpec" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "distillationSpec" }, - Common.GetValueByPath(fromObject, new string[] { "distillationSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tuningDataStats" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "tuningDataStats" }, - Common.GetValueByPath(fromObject, new string[] { "tuningDataStats" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" }) != null) { - Common.SetValueByPath(toObject, new string[] { "encryptionSpec" }, - Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "partnerModelTuningSpec" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "partnerModelTuningSpec" }, - Common.GetValueByPath(fromObject, new string[] { "partnerModelTuningSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "customBaseModel" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "customBaseModel" }, - Common.GetValueByPath(fromObject, new string[] { "customBaseModel" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "evaluateDatasetRuns" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "evaluateDatasetRuns" }, - Common.GetValueByPath(fromObject, new string[] { "evaluateDatasetRuns" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "experiment" }) != null) { - Common.SetValueByPath(toObject, new string[] { "experiment" }, - Common.GetValueByPath(fromObject, new string[] { "experiment" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fullFineTuningSpec" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "fullFineTuningSpec" }, - Common.GetValueByPath(fromObject, new string[] { "fullFineTuningSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { - Common.SetValueByPath(toObject, new string[] { "labels" }, - Common.GetValueByPath(fromObject, new string[] { "labels" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "outputUri" }, - Common.GetValueByPath(fromObject, new string[] { "outputUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pipelineJob" }) != null) { - Common.SetValueByPath(toObject, new string[] { "pipelineJob" }, - Common.GetValueByPath(fromObject, new string[] { "pipelineJob" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "serviceAccount" }) != null) { - Common.SetValueByPath(toObject, new string[] { "serviceAccount" }, - Common.GetValueByPath(fromObject, new string[] { "serviceAccount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "tunedModelDisplayName" }, - Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tuningJobState" }) != null) { - Common.SetValueByPath(toObject, new string[] { "tuningJobState" }, - Common.GetValueByPath(fromObject, new string[] { "tuningJobState" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "veoTuningSpec" }) != null) { - Common.SetValueByPath(toObject, new string[] { "veoTuningSpec" }, - Common.GetValueByPath(fromObject, new string[] { "veoTuningSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "distillationSamplingSpec" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "distillationSamplingSpec" }, - Common.GetValueByPath(fromObject, new string[] { "distillationSamplingSpec" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tuningJobMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "tuningJobMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "tuningJobMetadata" })); - } - - return toObject; - } - - internal JsonNode TuningOperationFromMldev(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - return toObject; - } - - internal JsonNode TuningValidationDatasetToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "validationDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "validationDatasetUri" }, - Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); - } - - return toObject; - } - - internal JsonNode VoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, - JsonNode rootObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "replicatedVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "replicatedVoiceConfig" }, - ReplicatedVoiceConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "replicatedVoiceConfig" })), - toObject, rootObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "prebuiltVoiceConfig" }, - Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" })); - } - - return toObject; - } - - public Tunings(ApiClient apiClient) { - _apiClient = apiClient; - } - - private async Task PrivateGetAsync(string name, GetTuningJobConfig? config, - CancellationToken cancellationToken = default) { - GetTuningJobParameters parameter = new GetTuningJobParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetTuningJobParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GetTuningJobParametersToVertex(parameterNode, new JsonObject(), parameterNode); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = GetTuningJobParametersToMldev(parameterNode, new JsonObject(), parameterNode); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = TuningJobFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = TuningJobFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateListAsync( - ListTuningJobsConfig? config, CancellationToken cancellationToken = default) { - ListTuningJobsParameters parameter = new ListTuningJobsParameters(); - - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse ListTuningJobsParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = ListTuningJobsParametersToVertex(parameterNode, new JsonObject(), parameterNode); - path = Common.FormatMap("tuningJobs", body["_url"]); - } else { - body = ListTuningJobsParametersToMldev(parameterNode, new JsonObject(), parameterNode); - path = Common.FormatMap("tunedModels", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - ListTuningJobsResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - ListTuningJobsResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Cancels a tuning job resource. - /// - /// The resource name of the tuning job. For Vertex, this is the full - /// resource name or `tuningJobs/{id}`. A for configuring the cancel request. A to cancel the operation. - - public async Task CancelAsync( - string name, CancelTuningJobConfig? config = null, - CancellationToken cancellationToken = default) { - CancelTuningJobParameters parameter = new CancelTuningJobParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse CancelTuningJobParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = CancelTuningJobParametersToVertex(parameterNode, new JsonObject(), parameterNode); - path = Common.FormatMap("{name}:cancel", body["_url"]); - } else { - body = CancelTuningJobParametersToMldev(parameterNode, new JsonObject(), parameterNode); - path = Common.FormatMap("{name}:cancel", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = - CancelTuningJobResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - responseNode = - CancelTuningJobResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - private async Task PrivateTuneAsync(string? baseModel, PreTunedModel? preTunedModel, - TuningDataset trainingDataset, - CreateTuningJobConfig? config, - CancellationToken cancellationToken = default) { - CreateTuningJobParametersPrivate parameter = new CreateTuningJobParametersPrivate(); - - if (!Common.IsZero(baseModel)) { - parameter.BaseModel = baseModel; - } - if (!Common.IsZero(preTunedModel)) { - parameter.PreTunedModel = preTunedModel; - } - if (!Common.IsZero(trainingDataset)) { - parameter.TrainingDataset = trainingDataset; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse CreateTuningJobParametersPrivate to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = CreateTuningJobParametersPrivateToVertex(parameterNode, new JsonObject(), - parameterNode); - path = Common.FormatMap("tuningJobs", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = TuningJobFromVertex(httpContentNode, new JsonObject(), parameterNode); - } - - if (!this._apiClient.VertexAI) { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateTuneMldevAsync( - string? baseModel, PreTunedModel? preTunedModel, TuningDataset trainingDataset, - CreateTuningJobConfig? config, CancellationToken cancellationToken = default) { - CreateTuningJobParametersPrivate parameter = new CreateTuningJobParametersPrivate(); - - if (!Common.IsZero(baseModel)) { - parameter.BaseModel = baseModel; - } - if (!Common.IsZero(preTunedModel)) { - parameter.PreTunedModel = preTunedModel; - } - if (!Common.IsZero(trainingDataset)) { - parameter.TrainingDataset = trainingDataset; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse CreateTuningJobParametersPrivate to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = - CreateTuningJobParametersPrivateToMldev(parameterNode, new JsonObject(), parameterNode); - path = Common.FormatMap("tunedModels", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - /// - /// Lists tuning jobs. - /// - /// A instance that specifies the - /// optional configuration for the list request. A to cancel the operation. A Pager object that - /// contains one page of tuning jobs. When iterating over the pager, it automatically fetches - /// the next page if there are more. - - public async Task> ListAsync( - ListTuningJobsConfig? config = null, CancellationToken cancellationToken = default) { - config ??= new ListTuningJobsConfig(); - var initialResponse = await PrivateListAsync(config, cancellationToken); - - return new Pager( - requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), - extractItems: response => response.TuningJobs, - extractNextPageToken: response => response.NextPageToken, - extractHttpResponse: response => response.SdkHttpResponse, - updateConfigPageToken: (cfg, token) => { - cfg.PageToken = token; - return cfg; - }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); - } - - /// - /// Makes an API request to get a tuning job. - /// - /// The resource name of the tuning job. - /// A for configuring the get - /// request. The cancellation token for the - /// request. A object. - public async Task GetAsync(string name, GetTuningJobConfig? config = null, - CancellationToken cancellationToken = default) { - return await this.PrivateGetAsync(name, config, cancellationToken); - } - - /// - /// Makes an API request to create a supervised fine-tuning job. - /// - /// The base model to tune. - /// The training dataset to use for tuning. - /// A for configuring the create - /// request. The cancellation token for the - /// request. A object. - public async Task TuneAsync(string baseModel, TuningDataset trainingDataset, - CreateTuningJobConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - if (baseModel.StartsWith("projects/")) { - PreTunedModel preTunedModel = new PreTunedModel { TunedModelName = baseModel }; - if (config != null && config.PreTunedModelCheckpointId != null) { - preTunedModel.CheckpointId = config.PreTunedModelCheckpointId; - } - return await this.PrivateTuneAsync(null, preTunedModel, trainingDataset, config, - cancellationToken); - } else { - return await this.PrivateTuneAsync(baseModel, null, trainingDataset, config, - cancellationToken); - } - } else { - TuningOperation operation = await this.PrivateTuneMldevAsync( - baseModel, null, trainingDataset, config, cancellationToken); - string tunedModelName = ""; - if (operation.Metadata != null && operation.Metadata.ContainsKey("tunedModel")) { - tunedModelName = (string)operation.Metadata["tunedModel"]; - } else { - if (operation.Name == null) { - throw new ArgumentException("Operation name is required."); - } - tunedModelName = - operation.Name.Split(new string[] { "/operations/" }, StringSplitOptions.None)[0]; - } - return new TuningJob { Name = tunedModelName, State = JobState.JobStateQueued }; - } - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + + public sealed class Tunings { + private readonly ApiClient _apiClient; + + internal JsonNode CancelTuningJobParametersToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + return toObject; + } + + internal JsonNode CancelTuningJobParametersToVertex(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + return toObject; + } + + internal JsonNode CancelTuningJobResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode CancelTuningJobResponseFromVertex(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode CreateTuningJobConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "validationDataset" }))) { + throw new NotSupportedException( + "validationDataset parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "description" }))) { + throw new NotSupportedException("description parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "tuningTask", "hyperparameters", "epochCount" }, + Common.GetValueByPath(fromObject, new string[] { "epochCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "tuningTask", "hyperparameters", "learningRateMultiplier" }, + Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }))) { + throw new NotSupportedException( + "exportLastCheckpointOnly parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "preTunedModelCheckpointId" }))) { + throw new NotSupportedException( + "preTunedModelCheckpointId parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "adapterSize" }))) { + throw new NotSupportedException("adapterSize parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "tuningMode" }))) { + throw new NotSupportedException("tuningMode parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "customBaseModel" }))) { + throw new NotSupportedException( + "customBaseModel parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "batchSize" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "tuningTask", "hyperparameters", "batchSize" }, + Common.GetValueByPath(fromObject, new string[] { "batchSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "learningRate" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "tuningTask", "hyperparameters", "learningRate" }, + Common.GetValueByPath(fromObject, new string[] { "learningRate" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "labels" }))) { + throw new NotSupportedException("labels parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "beta" }))) { + throw new NotSupportedException("beta parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "baseTeacherModel" }))) { + throw new NotSupportedException( + "baseTeacherModel parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "tunedTeacherModelSource" }))) { + throw new NotSupportedException( + "tunedTeacherModelSource parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "sftLossWeightMultiplier" }))) { + throw new NotSupportedException( + "sftLossWeightMultiplier parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "outputUri" }))) { + throw new NotSupportedException("outputUri parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" }))) { + throw new NotSupportedException("encryptionSpec parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode CreateTuningJobConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + JsonNode discriminatorValidationDataset = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueValidationDataset = + discriminatorValidationDataset == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorValidationDataset.GetValue(); + if (discriminatorValueValidationDataset == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "validationDataset" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "supervisedTuningSpec" }, + TuningValidationDatasetToVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "validationDataset" })), + toObject, rootObject)); + } + } else if (discriminatorValueValidationDataset == "PREFERENCE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "validationDataset" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "preferenceOptimizationSpec" }, + TuningValidationDatasetToVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "validationDataset" })), + toObject, rootObject)); + } + } else if (discriminatorValueValidationDataset == "DISTILLATION") { + if (Common.GetValueByPath(fromObject, new string[] { "validationDataset" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "distillationSpec" }, + TuningValidationDatasetToVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "validationDataset" })), + toObject, rootObject)); + } + } + if (Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "tunedModelDisplayName" }, + Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + JsonNode discriminatorEpochCount = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueEpochCount = discriminatorEpochCount == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorEpochCount.GetValue(); + if (discriminatorValueEpochCount == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "supervisedTuningSpec", "hyperParameters", "epochCount" }, + Common.GetValueByPath(fromObject, new string[] { "epochCount" })); + } + } else if (discriminatorValueEpochCount == "PREFERENCE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "preferenceOptimizationSpec", "hyperParameters", "epochCount" }, + Common.GetValueByPath(fromObject, new string[] { "epochCount" })); + } + } else if (discriminatorValueEpochCount == "DISTILLATION") { + if (Common.GetValueByPath(fromObject, new string[] { "epochCount" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "distillationSpec", "hyperParameters", "epochCount" }, + Common.GetValueByPath(fromObject, new string[] { "epochCount" })); + } + } + + JsonNode discriminatorLearningRateMultiplier = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueLearningRateMultiplier = + discriminatorLearningRateMultiplier == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorLearningRateMultiplier.GetValue(); + if (discriminatorValueLearningRateMultiplier == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "supervisedTuningSpec", "hyperParameters", "learningRateMultiplier" }, + Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); + } + } else if (discriminatorValueLearningRateMultiplier == "PREFERENCE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "preferenceOptimizationSpec", "hyperParameters", + "learningRateMultiplier" }, + Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); + } + } else if (discriminatorValueLearningRateMultiplier == "DISTILLATION") { + if (Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "distillationSpec", "hyperParameters", "learningRateMultiplier" }, + Common.GetValueByPath(fromObject, new string[] { "learningRateMultiplier" })); + } + } + + JsonNode discriminatorExportLastCheckpointOnly = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueExportLastCheckpointOnly = + discriminatorExportLastCheckpointOnly == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorExportLastCheckpointOnly.GetValue(); + if (discriminatorValueExportLastCheckpointOnly == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }) != + null) { + Common.SetValueByPath( + parentObject, new string[] { "supervisedTuningSpec", "exportLastCheckpointOnly" }, + Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" })); + } + } else if (discriminatorValueExportLastCheckpointOnly == "PREFERENCE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }) != + null) { + Common.SetValueByPath( + parentObject, + new string[] { "preferenceOptimizationSpec", "exportLastCheckpointOnly" }, + Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" })); + } + } else if (discriminatorValueExportLastCheckpointOnly == "DISTILLATION") { + if (Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" }) != + null) { + Common.SetValueByPath( + parentObject, new string[] { "distillationSpec", "exportLastCheckpointOnly" }, + Common.GetValueByPath(fromObject, new string[] { "exportLastCheckpointOnly" })); + } + } + + JsonNode discriminatorAdapterSize = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueAdapterSize = discriminatorAdapterSize == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorAdapterSize.GetValue(); + if (discriminatorValueAdapterSize == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "adapterSize" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "supervisedTuningSpec", "hyperParameters", "adapterSize" }, + Common.GetValueByPath(fromObject, new string[] { "adapterSize" })); + } + } else if (discriminatorValueAdapterSize == "PREFERENCE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "adapterSize" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "preferenceOptimizationSpec", "hyperParameters", "adapterSize" }, + Common.GetValueByPath(fromObject, new string[] { "adapterSize" })); + } + } else if (discriminatorValueAdapterSize == "DISTILLATION") { + if (Common.GetValueByPath(fromObject, new string[] { "adapterSize" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "distillationSpec", "hyperParameters", "adapterSize" }, + Common.GetValueByPath(fromObject, new string[] { "adapterSize" })); + } + } + + JsonNode discriminatorTuningMode = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueTuningMode = discriminatorTuningMode == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorTuningMode.GetValue(); + if (discriminatorValueTuningMode == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "tuningMode" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "supervisedTuningSpec", "tuningMode" }, + Common.GetValueByPath(fromObject, new string[] { "tuningMode" })); + } + } + if (Common.GetValueByPath(fromObject, new string[] { "customBaseModel" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "customBaseModel" }, + Common.GetValueByPath(fromObject, new string[] { "customBaseModel" })); + } + + JsonNode discriminatorBatchSize = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueBatchSize = discriminatorBatchSize == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorBatchSize.GetValue(); + if (discriminatorValueBatchSize == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "batchSize" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "supervisedTuningSpec", "hyperParameters", "batchSize" }, + Common.GetValueByPath(fromObject, new string[] { "batchSize" })); + } + } + + JsonNode discriminatorLearningRate = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueLearningRate = discriminatorLearningRate == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorLearningRate.GetValue(); + if (discriminatorValueLearningRate == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "learningRate" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "supervisedTuningSpec", "hyperParameters", "learningRate" }, + Common.GetValueByPath(fromObject, new string[] { "learningRate" })); + } + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "beta" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "preferenceOptimizationSpec", "hyperParameters", "beta" }, + Common.GetValueByPath(fromObject, new string[] { "beta" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "baseTeacherModel" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "distillationSpec", "baseTeacherModel" }, + Common.GetValueByPath(fromObject, new string[] { "baseTeacherModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tunedTeacherModelSource" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "distillationSpec", "tunedTeacherModelSource" }, + Common.GetValueByPath(fromObject, new string[] { "tunedTeacherModelSource" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "sftLossWeightMultiplier" }) != null) { + Common.SetValueByPath( + parentObject, + new string[] { "distillationSpec", "hyperParameters", "sftLossWeightMultiplier" }, + Common.GetValueByPath(fromObject, new string[] { "sftLossWeightMultiplier" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputUri" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "outputUri" }, + Common.GetValueByPath(fromObject, new string[] { "outputUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "encryptionSpec" }, + Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" })); + } + + return toObject; + } + + internal JsonNode CreateTuningJobParametersPrivateToMldev(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "baseModel" }, + Common.GetValueByPath(fromObject, new string[] { "baseModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "preTunedModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "preTunedModel" }, + Common.GetValueByPath(fromObject, new string[] { "preTunedModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "trainingDataset" }) != null) { + _ = TuningDatasetToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "trainingDataset" })), + toObject, rootObject); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateTuningJobConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode CreateTuningJobParametersPrivateToVertex(JsonNode fromObject, + JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "baseModel" }, + Common.GetValueByPath(fromObject, new string[] { "baseModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "preTunedModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "preTunedModel" }, + Common.GetValueByPath(fromObject, new string[] { "preTunedModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "trainingDataset" }) != null) { + _ = TuningDatasetToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "trainingDataset" })), + toObject, rootObject); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateTuningJobConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode EvaluationConfigFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode EvaluationConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode GenerationConfigFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "modelConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "modelSelectionConfig" }, + Common.GetValueByPath(fromObject, new string[] { "modelConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { + Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, + Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "candidateCount" }, + Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "enableAffectiveDialog" }, + Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "frequencyPenalty" }, + Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobs" }, + Common.GetValueByPath(fromObject, new string[] { "logprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "presencePenalty" }, + Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseMimeType" }, + Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "routingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(toObject, new string[] { "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "speechConfig" }, + Common.GetValueByPath(fromObject, new string[] { "speechConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { + Common.SetValueByPath(toObject, new string[] { "stopSequences" }, + Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + return toObject; + } + + internal JsonNode GenerationConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "modelConfig" }, + Common.GetValueByPath(fromObject, new string[] { "modelSelectionConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" }) != null) { + Common.SetValueByPath(toObject, new string[] { "audioTimestamp" }, + Common.GetValueByPath(fromObject, new string[] { "audioTimestamp" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "candidateCount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "candidateCount" }, + Common.GetValueByPath(fromObject, new string[] { "candidateCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "enableAffectiveDialog" }, + Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "frequencyPenalty" }, + Common.GetValueByPath(fromObject, new string[] { "frequencyPenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "logprobs" }) != null) { + Common.SetValueByPath(toObject, new string[] { "logprobs" }, + Common.GetValueByPath(fromObject, new string[] { "logprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "presencePenalty" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "presencePenalty" }, + Common.GetValueByPath(fromObject, new string[] { "presencePenalty" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseLogprobs" }, + Common.GetValueByPath(fromObject, new string[] { "responseLogprobs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseMimeType" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseMimeType" }, + Common.GetValueByPath(fromObject, new string[] { "responseMimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseSchema" }) != null) { + Common.SetValueByPath(toObject, new string[] { "responseSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "routingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "routingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "routingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(toObject, new string[] { "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "speechConfig" }, + SpeechConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "speechConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "stopSequences" }) != null) { + Common.SetValueByPath(toObject, new string[] { "stopSequences" }, + Common.GetValueByPath(fromObject, new string[] { "stopSequences" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(toObject, new string[] { "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thinkingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "thinkingConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(toObject, new string[] { "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enableEnhancedCivicAnswers" }))) { + throw new NotSupportedException( + "enableEnhancedCivicAnswers parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode GetTuningJobParametersToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + return toObject; + } + + internal JsonNode GetTuningJobParametersToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + return toObject; + } + + internal JsonNode ListTuningJobsConfigToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, + Common.GetValueByPath(fromObject, new string[] { "filter" })); + } + + return toObject; + } + + internal JsonNode ListTuningJobsConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "filter" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "filter" }, + Common.GetValueByPath(fromObject, new string[] { "filter" })); + } + + return toObject; + } + + internal JsonNode ListTuningJobsParametersToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListTuningJobsConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode ListTuningJobsParametersToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListTuningJobsConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject, rootObject); + } + + return toObject; + } + + internal JsonNode ListTuningJobsResponseFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tunedModels" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tunedModels" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(TuningJobFromMldev(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "tuningJobs" }, result); + } + + return toObject; + } + + internal JsonNode ListTuningJobsResponseFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tuningJobs" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tuningJobs" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(TuningJobFromVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "tuningJobs" }, result); + } + + return toObject; + } + + internal JsonNode MultiSpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add( + SpeakerVoiceConfigToVertex(Common.ParseToJsonNode(record), toObject, rootObject)); + } + Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); + } + + return toObject; + } + + internal JsonNode ReplicatedVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "voiceSampleAudio" }, + Common.GetValueByPath(fromObject, new string[] { "voiceSampleAudio" })); + } + + return toObject; + } + + internal JsonNode SpeakerVoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { + Common.SetValueByPath(toObject, new string[] { "speaker" }, + Common.GetValueByPath(fromObject, new string[] { "speaker" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, + VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode SpeechConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "voiceConfig" }, + VoiceConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "languageCode" }, + Common.GetValueByPath(fromObject, new string[] { "languageCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, + MultiSpeakerVoiceConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "multiSpeakerVoiceConfig" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode TunedModelFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "model" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "endpoint" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + return toObject; + } + + internal JsonNode TuningDatasetToMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "gcsUri" }))) { + throw new NotSupportedException("gcsUri parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }))) { + throw new NotSupportedException( + "vertexDatasetResource parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "examples" }) != null) { + Common.SetValueByPath(toObject, new string[] { "examples", "examples" }, + Common.GetValueByPath(fromObject, new string[] { "examples" })); + } + + return toObject; + } + + internal JsonNode TuningDatasetToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + JsonNode discriminatorGcsUri = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueGcsUri = discriminatorGcsUri == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorGcsUri.GetValue(); + if (discriminatorValueGcsUri == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "supervisedTuningSpec", "trainingDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + } else if (discriminatorValueGcsUri == "PREFERENCE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "preferenceOptimizationSpec", "trainingDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + } else if (discriminatorValueGcsUri == "DISTILLATION") { + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "distillationSpec", "promptDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + } + + JsonNode discriminatorVertexDatasetResource = + Common.GetValueByPath(rootObject, new string[] { "config", "method" }); + string discriminatorValueVertexDatasetResource = + discriminatorVertexDatasetResource == null + ? "SUPERVISED_FINE_TUNING" + : discriminatorVertexDatasetResource.GetValue(); + if (discriminatorValueVertexDatasetResource == "SUPERVISED_FINE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "supervisedTuningSpec", "trainingDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); + } + } else if (discriminatorValueVertexDatasetResource == "PREFERENCE_TUNING") { + if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "preferenceOptimizationSpec", "trainingDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); + } + } else if (discriminatorValueVertexDatasetResource == "DISTILLATION") { + if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "distillationSpec", "promptDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); + } + } + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "examples" }))) { + throw new NotSupportedException("examples parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode TuningJobFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "state" }) != null) { + Common.SetValueByPath(toObject, new string[] { "state" }, + Transformers.TTuningJobStatus( + Common.GetValueByPath(fromObject, new string[] { "state" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "createTime" }, + Common.GetValueByPath(fromObject, new string[] { "createTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tuningTask", "startTime" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "startTime" }, + Common.GetValueByPath(fromObject, new string[] { "tuningTask", "startTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tuningTask", "completeTime" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "endTime" }, + Common.GetValueByPath(fromObject, new string[] { "tuningTask", "completeTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "updateTime" }, + Common.GetValueByPath(fromObject, new string[] { "updateTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "baseModel" }, + Common.GetValueByPath(fromObject, new string[] { "baseModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tunedModel" }, + TunedModelFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject, rootObject)); + } + + return toObject; + } + + internal JsonNode TuningJobFromVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "state" }) != null) { + Common.SetValueByPath(toObject, new string[] { "state" }, + Transformers.TTuningJobStatus( + Common.GetValueByPath(fromObject, new string[] { "state" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "createTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "createTime" }, + Common.GetValueByPath(fromObject, new string[] { "createTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "startTime" }, + Common.GetValueByPath(fromObject, new string[] { "startTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "endTime" }, + Common.GetValueByPath(fromObject, new string[] { "endTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "updateTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "updateTime" }, + Common.GetValueByPath(fromObject, new string[] { "updateTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "baseModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "baseModel" }, + Common.GetValueByPath(fromObject, new string[] { "baseModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tunedModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tunedModel" }, + Common.GetValueByPath(fromObject, new string[] { "tunedModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "preTunedModel" }) != null) { + Common.SetValueByPath(toObject, new string[] { "preTunedModel" }, + Common.GetValueByPath(fromObject, new string[] { "preTunedModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "supervisedTuningSpec" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "supervisedTuningSpec" }, + Common.GetValueByPath(fromObject, new string[] { "supervisedTuningSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "preferenceOptimizationSpec" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "preferenceOptimizationSpec" }, + Common.GetValueByPath(fromObject, new string[] { "preferenceOptimizationSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "distillationSpec" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "distillationSpec" }, + Common.GetValueByPath(fromObject, new string[] { "distillationSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tuningDataStats" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "tuningDataStats" }, + Common.GetValueByPath(fromObject, new string[] { "tuningDataStats" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" }) != null) { + Common.SetValueByPath(toObject, new string[] { "encryptionSpec" }, + Common.GetValueByPath(fromObject, new string[] { "encryptionSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "partnerModelTuningSpec" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "partnerModelTuningSpec" }, + Common.GetValueByPath(fromObject, new string[] { "partnerModelTuningSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "customBaseModel" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "customBaseModel" }, + Common.GetValueByPath(fromObject, new string[] { "customBaseModel" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "evaluateDatasetRuns" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "evaluateDatasetRuns" }, + Common.GetValueByPath(fromObject, new string[] { "evaluateDatasetRuns" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "experiment" }) != null) { + Common.SetValueByPath(toObject, new string[] { "experiment" }, + Common.GetValueByPath(fromObject, new string[] { "experiment" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fullFineTuningSpec" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "fullFineTuningSpec" }, + Common.GetValueByPath(fromObject, new string[] { "fullFineTuningSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "labels" }) != null) { + Common.SetValueByPath(toObject, new string[] { "labels" }, + Common.GetValueByPath(fromObject, new string[] { "labels" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "outputUri" }, + Common.GetValueByPath(fromObject, new string[] { "outputUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pipelineJob" }) != null) { + Common.SetValueByPath(toObject, new string[] { "pipelineJob" }, + Common.GetValueByPath(fromObject, new string[] { "pipelineJob" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "serviceAccount" }) != null) { + Common.SetValueByPath(toObject, new string[] { "serviceAccount" }, + Common.GetValueByPath(fromObject, new string[] { "serviceAccount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "tunedModelDisplayName" }, + Common.GetValueByPath(fromObject, new string[] { "tunedModelDisplayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tuningJobState" }) != null) { + Common.SetValueByPath(toObject, new string[] { "tuningJobState" }, + Common.GetValueByPath(fromObject, new string[] { "tuningJobState" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "veoTuningSpec" }) != null) { + Common.SetValueByPath(toObject, new string[] { "veoTuningSpec" }, + Common.GetValueByPath(fromObject, new string[] { "veoTuningSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "distillationSamplingSpec" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "distillationSamplingSpec" }, + Common.GetValueByPath(fromObject, new string[] { "distillationSamplingSpec" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tuningJobMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "tuningJobMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "tuningJobMetadata" })); + } + + return toObject; + } + + internal JsonNode TuningOperationFromMldev(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + return toObject; + } + + internal JsonNode TuningValidationDatasetToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "validationDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "validationDatasetUri" }, + Common.GetValueByPath(fromObject, new string[] { "vertexDatasetResource" })); + } + + return toObject; + } + + internal JsonNode VoiceConfigToVertex(JsonNode fromObject, JsonObject parentObject, + JsonNode rootObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "replicatedVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "replicatedVoiceConfig" }, + ReplicatedVoiceConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "replicatedVoiceConfig" })), + toObject, rootObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "prebuiltVoiceConfig" }, + Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" })); + } + + return toObject; + } + + public Tunings(ApiClient apiClient) { + _apiClient = apiClient; + } + + private async Task PrivateGetAsync(string name, GetTuningJobConfig? config, + CancellationToken cancellationToken = default) { + GetTuningJobParameters parameter = new GetTuningJobParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetTuningJobParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GetTuningJobParametersToVertex(parameterNode, new JsonObject(), parameterNode); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = GetTuningJobParametersToMldev(parameterNode, new JsonObject(), parameterNode); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = TuningJobFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = TuningJobFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateListAsync( + ListTuningJobsConfig? config, CancellationToken cancellationToken = default) { + ListTuningJobsParameters parameter = new ListTuningJobsParameters(); + + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse ListTuningJobsParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = ListTuningJobsParametersToVertex(parameterNode, new JsonObject(), parameterNode); + path = Common.FormatMap("tuningJobs", body["_url"]); + } else { + body = ListTuningJobsParametersToMldev(parameterNode, new JsonObject(), parameterNode); + path = Common.FormatMap("tunedModels", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + ListTuningJobsResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + ListTuningJobsResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Cancels a tuning job resource. + /// + /// The resource name of the tuning job. For Vertex, this is the full + /// resource name or `tuningJobs/{id}`. A for configuring the cancel request. A to cancel the operation. + + public async Task CancelAsync( + string name, CancelTuningJobConfig? config = null, + CancellationToken cancellationToken = default) { + CancelTuningJobParameters parameter = new CancelTuningJobParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse CancelTuningJobParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = CancelTuningJobParametersToVertex(parameterNode, new JsonObject(), parameterNode); + path = Common.FormatMap("{name}:cancel", body["_url"]); + } else { + body = CancelTuningJobParametersToMldev(parameterNode, new JsonObject(), parameterNode); + path = Common.FormatMap("{name}:cancel", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = + CancelTuningJobResponseFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + responseNode = + CancelTuningJobResponseFromMldev(httpContentNode, new JsonObject(), parameterNode); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + private async Task PrivateTuneAsync(string? baseModel, PreTunedModel? preTunedModel, + TuningDataset trainingDataset, + CreateTuningJobConfig? config, + CancellationToken cancellationToken = default) { + CreateTuningJobParametersPrivate parameter = new CreateTuningJobParametersPrivate(); + + if (!Common.IsZero(baseModel)) { + parameter.BaseModel = baseModel; + } + if (!Common.IsZero(preTunedModel)) { + parameter.PreTunedModel = preTunedModel; + } + if (!Common.IsZero(trainingDataset)) { + parameter.TrainingDataset = trainingDataset; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse CreateTuningJobParametersPrivate to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = CreateTuningJobParametersPrivateToVertex(parameterNode, new JsonObject(), + parameterNode); + path = Common.FormatMap("tuningJobs", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = TuningJobFromVertex(httpContentNode, new JsonObject(), parameterNode); + } + + if (!this._apiClient.VertexAI) { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateTuneMldevAsync( + string? baseModel, PreTunedModel? preTunedModel, TuningDataset trainingDataset, + CreateTuningJobConfig? config, CancellationToken cancellationToken = default) { + CreateTuningJobParametersPrivate parameter = new CreateTuningJobParametersPrivate(); + + if (!Common.IsZero(baseModel)) { + parameter.BaseModel = baseModel; + } + if (!Common.IsZero(preTunedModel)) { + parameter.PreTunedModel = preTunedModel; + } + if (!Common.IsZero(trainingDataset)) { + parameter.TrainingDataset = trainingDataset; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse CreateTuningJobParametersPrivate to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = + CreateTuningJobParametersPrivateToMldev(parameterNode, new JsonObject(), parameterNode); + path = Common.FormatMap("tunedModels", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + /// + /// Lists tuning jobs. + /// + /// A instance that specifies the + /// optional configuration for the list request. A to cancel the operation. A Pager object that + /// contains one page of tuning jobs. When iterating over the pager, it automatically fetches + /// the next page if there are more. + + public async Task> ListAsync( + ListTuningJobsConfig? config = null, CancellationToken cancellationToken = default) { + config ??= new ListTuningJobsConfig(); + var initialResponse = await PrivateListAsync(config, cancellationToken); + + return new Pager( + requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), + extractItems: response => response.TuningJobs, + extractNextPageToken: response => response.NextPageToken, + extractHttpResponse: response => response.SdkHttpResponse, + updateConfigPageToken: (cfg, token) => { + cfg.PageToken = token; + return cfg; + }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); + } + + /// + /// Makes an API request to get a tuning job. + /// + /// The resource name of the tuning job. + /// A for configuring the get + /// request. The cancellation token for the + /// request. A object. + public async Task GetAsync(string name, GetTuningJobConfig? config = null, + CancellationToken cancellationToken = default) { + return await this.PrivateGetAsync(name, config, cancellationToken); + } + + /// + /// Makes an API request to create a supervised fine-tuning job. + /// + /// The base model to tune. + /// The training dataset to use for tuning. + /// A for configuring the create + /// request. The cancellation token for the + /// request. A object. + public async Task TuneAsync(string baseModel, TuningDataset trainingDataset, + CreateTuningJobConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + if (baseModel.StartsWith("projects/")) { + PreTunedModel preTunedModel = new PreTunedModel { TunedModelName = baseModel }; + if (config != null && config.PreTunedModelCheckpointId != null) { + preTunedModel.CheckpointId = config.PreTunedModelCheckpointId; + } + return await this.PrivateTuneAsync(null, preTunedModel, trainingDataset, config, + cancellationToken); + } else { + return await this.PrivateTuneAsync(baseModel, null, trainingDataset, config, + cancellationToken); + } + } else { + TuningOperation operation = await this.PrivateTuneMldevAsync( + baseModel, null, trainingDataset, config, cancellationToken); + string tunedModelName = ""; + if (operation.Metadata != null && operation.Metadata.ContainsKey("tunedModel")) { + tunedModelName = (string)operation.Metadata["tunedModel"]; + } else { + if (operation.Name == null) { + throw new ArgumentException("Operation name is required."); + } + tunedModelName = + operation.Name.Split(new string[] { "/operations/" }, StringSplitOptions.None)[0]; + } + return new TuningJob { Name = tunedModelName, State = JobState.JobStateQueued }; + } + } + } +} diff --git a/Google.GenAI/packages.lock.json b/Google.GenAI/packages.lock.json index da4d7d0e..75ae053f 100644 --- a/Google.GenAI/packages.lock.json +++ b/Google.GenAI/packages.lock.json @@ -1,240 +1,240 @@ -{ - "version": 2, - "dependencies": { - ".NETStandard,Version=v2.0": { - "Google.Apis.Auth": { - "type": "Direct", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "Direct", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "Direct", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==", - "dependencies": { - "System.Buffers": "4.6.1", - "System.Memory": "4.6.3", - "System.Threading.Tasks.Extensions": "4.6.3" - } - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", - "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==", - "dependencies": { - "System.Buffers": "4.6.1", - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "CentralTransitive", - "requested": "[10.0.3, )", - "resolved": "10.0.4", - "contentHash": "2JmoSZ1wDf1/TUyTtLTXeicXCnWxXkeStGnzRRmAw+5CBIGhg6q9ieJXu4FjeLzawSGd5PMhcropNa3lPJDaKA==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.6.3" - } - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "10.0.4", - "System.Buffers": "4.6.1", - "System.IO.Pipelines": "10.0.4", - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2", - "System.Text.Encodings.Web": "10.0.4", - "System.Threading.Tasks.Extensions": "4.6.3" - } - } - }, - "net8.0": { - "Google.Apis.Auth": { - "type": "Direct", - "requested": "[1.69.0, )", - "resolved": "1.69.0", - "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", - "dependencies": { - "Google.Apis": "1.69.0", - "Google.Apis.Core": "1.69.0", - "System.Management": "7.0.2" - } - }, - "Microsoft.Extensions.AI.Abstractions": { - "type": "Direct", - "requested": "[10.4.1, )", - "resolved": "10.4.1", - "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", - "dependencies": { - "System.Text.Json": "10.0.4" - } - }, - "MimeTypes": { - "type": "Direct", - "requested": "[2.5.2, )", - "resolved": "2.5.2", - "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" - }, - "Google.Apis": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", - "dependencies": { - "Google.Apis.Core": "1.69.0" - } - }, - "Google.Apis.Core": { - "type": "Transitive", - "resolved": "1.69.0", - "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", - "dependencies": { - "Newtonsoft.Json": "13.0.3" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.3", - "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" - }, - "System.CodeDom": { - "type": "Transitive", - "resolved": "7.0.0", - "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" - }, - "System.Management": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", - "dependencies": { - "System.CodeDom": "7.0.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "10.0.4", - "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" - }, - "System.Text.Json": { - "type": "CentralTransitive", - "requested": "[10.0.4, )", - "resolved": "10.0.4", - "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", - "dependencies": { - "System.IO.Pipelines": "10.0.4", - "System.Text.Encodings.Web": "10.0.4" - } - } - } - } +{ + "version": 2, + "dependencies": { + ".NETStandard,Version=v2.0": { + "Google.Apis.Auth": { + "type": "Direct", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "Direct", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "Direct", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "NETStandard.Library": { + "type": "Direct", + "requested": "[2.0.3, )", + "resolved": "2.0.3", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "System.Text.Json": { + "type": "Direct", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "10.0.4", + "System.Buffers": "4.6.1", + "System.IO.Pipelines": "10.0.4", + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2", + "System.Text.Encodings.Web": "10.0.4", + "System.Threading.Tasks.Extensions": "4.6.3" + } + }, + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==", + "dependencies": { + "System.Buffers": "4.6.1", + "System.Memory": "4.6.3", + "System.Threading.Tasks.Extensions": "4.6.3" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "dependencies": { + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==", + "dependencies": { + "System.Buffers": "4.6.1", + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.6.3", + "contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "CentralTransitive", + "requested": "[10.0.3, )", + "resolved": "10.0.4", + "contentHash": "2JmoSZ1wDf1/TUyTtLTXeicXCnWxXkeStGnzRRmAw+5CBIGhg6q9ieJXu4FjeLzawSGd5PMhcropNa3lPJDaKA==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.6.3" + } + } + }, + "net8.0": { + "Google.Apis.Auth": { + "type": "Direct", + "requested": "[1.69.0, )", + "resolved": "1.69.0", + "contentHash": "ar07yxn/s41jdqQ3sMh8EAehiSvXQ9yE1MS4McmZINeSWvolnLHmIZ9Yxj4tHVIYYz0c7H/lpToVqm7C2aYx9g==", + "dependencies": { + "Google.Apis": "1.69.0", + "Google.Apis.Core": "1.69.0", + "System.Management": "7.0.2" + } + }, + "Microsoft.Extensions.AI.Abstractions": { + "type": "Direct", + "requested": "[10.4.1, )", + "resolved": "10.4.1", + "contentHash": "ZxhU/wg9BOc3ohibhLl18toPLWm96ysQoE+3OhCgrZ0TUPZd7bsUmGteeatz08yweyuPIEhtyUzEZTF+3bMWEQ==", + "dependencies": { + "System.Text.Json": "10.0.4" + } + }, + "MimeTypes": { + "type": "Direct", + "requested": "[2.5.2, )", + "resolved": "2.5.2", + "contentHash": "vm4xrNt+i6OVRQ8vhfCcmDIUg3qvjyCTkSTNVTDFohsG6CXEpMaVFkidECL6yRYpHDnz4TqXhDoEQAcnHCu/tw==" + }, + "System.Text.Json": { + "type": "Direct", + "requested": "[10.0.4, )", + "resolved": "10.0.4", + "contentHash": "1tRPRt8D/kzjGL7em1uJ3iJlvVIC3G/sZJ+ZgSvtVYLXGGO26Clkqy2b5uts/pyR706Yw8/xA7exeI2PI50dpw==", + "dependencies": { + "System.IO.Pipelines": "10.0.4", + "System.Text.Encodings.Web": "10.0.4" + } + }, + "Google.Apis": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "1TfjsXFejwIf7iWaE7A0FbnOEsk8FPlbdFAt1r+I8aSMQfLLdSVWCLdZz6TzuWVwoCGEuJUHTZ/FXdptdU3qWw==", + "dependencies": { + "Google.Apis.Core": "1.69.0" + } + }, + "Google.Apis.Core": { + "type": "Transitive", + "resolved": "1.69.0", + "contentHash": "SXUcurNUPxYMtOnawvB2Av18VrPBC9W7So9q9ikmXIXLGiv4RX7Zbu4kc+8PbwTdd8wLt54r0PBGOT5RaKoTjQ==", + "dependencies": { + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "GLltyqEsE5/3IE+zYRP5sNa1l44qKl9v+bfdMcwg+M9qnQf47wK3H0SUR/T+3N4JEQXF3vV4CSuuo0rsg+nq2A==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "V7+RO17s/tzCpgqyj6t5vb54HFCvrRaMEwTcKDwpoQK66DRROzSff6kqtzHyiWRj6hrQQUmW80NL4pFSNhYpYA==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "/qEUN91mP/MUQmJnM5y5BdT7ZoPuVrtxnFlbJ8a3kBJGhe2wCzBfnPFtK2wTtEEcf3DMGR9J00GZZfg6HRI6yA==", + "dependencies": { + "System.CodeDom": "7.0.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "10.0.4", + "contentHash": "6g3B7jNsPRNf4luuYt1qE4R8S3JI+zMsfGWL9Idv4Mk1Z9Gh+rCagp9sG3AejPS87yBj1DjopM4i3wSz0WnEqg==" + } + } + } } \ No newline at end of file From caeda097aa1529b066fc57c9e851f2c15b158dad Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Mon, 6 Apr 2026 11:10:36 -0700 Subject: [PATCH 6/7] Harden GoogleGenAIRealtimeSession: error handling, concurrency, tool normalization - Fix SendAsync error handling: rethrow ODE as named ObjectDisposedException, swallow WebSocketException only when disposed (not blanket catch) - Add concurrent enumeration guard (_activeStreamingEnumeration) to GetStreamingResponseAsync to prevent multiple simultaneous readers - Wrap DisposeAsync resources in individual try/catch with ExceptionDispatchInfo to prevent resource leaks on partial failure - Fix CreateSessionAsync to dispose asyncSession on setup failure - Replace shallow tool result serialization with deep NormalizeToolPayload, NormalizeToolArguments, ConvertJsonElementToToolPayload - Use FunctionCallContent.CreateFromParsedArguments for tool call args (consistent with MEAI conventions, AOT-safe) - Add MaxToolPayloadDepth (64) depth guard to prevent stack overflow - Add post-lock disposed recheck for race with concurrent DisposeAsync - Add 5 regression tests, update 3 existing error handling tests --- Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs | 5615 +++++++++-------- Google.GenAI/GoogleGenAIRealtimeClient.cs | 602 +- Google.GenAI/GoogleGenAIRealtimeSession.cs | 1876 +++--- 3 files changed, 4194 insertions(+), 3899 deletions(-) diff --git a/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs index 06116d9a..ac827b85 100644 --- a/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs +++ b/Google.GenAI.Tests/GoogleGenAIRealtimeTest.cs @@ -1,2763 +1,2852 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma warning disable MEAI001 // Experimental AI API - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.WebSockets; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Google.GenAI.Types; -using Microsoft.Extensions.AI; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; - -#pragma warning disable MEAI001 - -namespace Google.GenAI.Tests; - -[TestClass] -public class GoogleGenAIRealtimeClientTest -{ - #region Extension Method Tests - - [TestMethod] - public void AsIRealtimeClient_NullClient_ThrowsArgumentNullException() - { - Client? client = null; - Assert.ThrowsException(() => client!.AsIRealtimeClient("model")); - } - - [TestMethod] - public void AsIRealtimeClient_ValidClient_ReturnsNonNull() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = client.AsIRealtimeClient("model"); - Assert.IsNotNull(realtimeClient); - } - - [TestMethod] - public void AsIRealtimeClient_NullModelId_ReturnsNonNull() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = client.AsIRealtimeClient(); - Assert.IsNotNull(realtimeClient); - } - - #endregion - - #region Client Constructor Tests - - [TestMethod] - public void RealtimeClient_NullClient_ThrowsArgumentNullException() - { - Assert.ThrowsException( - () => new GoogleGenAIRealtimeClient((Client)null!, "model")); - } - - [TestMethod] - public void RealtimeClient_NullModelId_Succeeds() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client); - Assert.IsNotNull(realtimeClient); - } - - #endregion - - #region Client GetService Tests - - [TestMethod] - public void RealtimeClient_GetService_ReturnsMetadata() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "my-model"); - - var metadata = realtimeClient.GetService(typeof(ChatClientMetadata)) as ChatClientMetadata; - Assert.IsNotNull(metadata); - Assert.AreEqual("google-genai", metadata.ProviderName); - Assert.AreEqual("my-model", metadata.DefaultModelId); - } - - [TestMethod] - public void RealtimeClient_GetService_ReturnsSelf() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); - - var self = realtimeClient.GetService(typeof(GoogleGenAIRealtimeClient)); - Assert.AreSame(realtimeClient, self); - } - - [TestMethod] - public void RealtimeClient_GetService_ReturnsInnerClient() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); - - var inner = realtimeClient.GetService(typeof(Client)); - Assert.AreSame(client, inner); - } - - [TestMethod] - public void RealtimeClient_GetService_NullServiceType_ThrowsArgumentNullException() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); - - Assert.ThrowsException( - () => realtimeClient.GetService(null!)); - } - - [TestMethod] - public void RealtimeClient_GetService_WithKey_ReturnsNull() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); - - var result = realtimeClient.GetService(typeof(ChatClientMetadata), serviceKey: "some-key"); - Assert.IsNull(result); - } - - [TestMethod] - public void RealtimeClient_GetService_UnknownType_ReturnsNull() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); - - var result = realtimeClient.GetService(typeof(string)); - Assert.IsNull(result); - } - - #endregion - - #region Client Dispose Tests - - [TestMethod] - public void RealtimeClient_Dispose_IsIdempotent() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); - - // Should not throw - realtimeClient.Dispose(); - realtimeClient.Dispose(); - } - - #endregion - - #region Client CreateSession Tests - - [TestMethod] - public async Task RealtimeClient_CreateSession_NoModel_ThrowsInvalidOperationException() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client); - - await Assert.ThrowsExceptionAsync( - () => realtimeClient.CreateSessionAsync(new RealtimeSessionOptions())); - } - - [TestMethod] - public async Task RealtimeClient_CreateSession_Cancelled_ThrowsOperationCanceledException() - { - var client = new Client(apiKey: "fake-api-key"); - var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); - using var cts = new CancellationTokenSource(); - cts.Cancel(); - - await Assert.ThrowsExceptionAsync( - () => realtimeClient.CreateSessionAsync(new RealtimeSessionOptions(), cts.Token)); - } - - #endregion - - #region ToGoogleFunctionDeclaration Tests - - [TestMethod] - public void ToGoogleFunctionDeclaration_MapsNameAndDescription() - { - var function = AIFunctionFactory.Create(() => "result", "myFunc", "A test function"); - - var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); - - Assert.AreEqual("myFunc", declaration.Name); - Assert.AreEqual("A test function", declaration.Description); - } - - [TestMethod] - public void ToGoogleFunctionDeclaration_WithJsonSchema_MapsParameters() - { - var function = AIFunctionFactory.Create((string name, int age) => $"{name} is {age}", - "greet", "Greets a person"); - - var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); - - Assert.AreEqual("greet", declaration.Name); - Assert.AreEqual("Greets a person", declaration.Description); - - // The Parameters (Google Schema) should be set since the function has parameters - Assert.IsNotNull(declaration.Parameters); - } - - [TestMethod] - public void ToGoogleFunctionDeclaration_NoParameters_HasNoParameterSchema() - { - var function = AIFunctionFactory.Create(() => "hello", "noParams", "No parameters"); - - var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); - - Assert.AreEqual("noParams", declaration.Name); - } - - #endregion -} - -[TestClass] -public class GoogleGenAIRealtimeSessionTest -{ - private Mock _mockWebSocket = null!; - private AsyncSession _asyncSession = null!; - private GoogleGenAIRealtimeSession _session = null!; - - [TestInitialize] - public void TestInitialize() - { - _mockWebSocket = new Mock(); - _mockWebSocket.Setup(ws => ws.State).Returns(WebSocketState.Open); - _asyncSession = new AsyncSession(_mockWebSocket.Object, new TestApiClient(false)); - _session = new GoogleGenAIRealtimeSession(_asyncSession, "test-model", null); - } - - [TestCleanup] - public async Task TestCleanup() - { - await _session.DisposeAsync(); - } - - #region Constructor Tests - - [TestMethod] - public void Session_NullAsyncSession_ThrowsArgumentNullException() - { - Assert.ThrowsException( - () => new GoogleGenAIRealtimeSession(null!, "model", null)); - } - - [TestMethod] - public void Session_ValidConstruction_SetsOptions() - { - var options = new RealtimeSessionOptions { Model = "my-model" }; - var session = new GoogleGenAIRealtimeSession( - _asyncSession, "my-model", options); - - Assert.AreSame(options, session.Options); - } - - [TestMethod] - public void Session_NullOptions_OptionsIsNull() - { - Assert.IsNull(_session.Options); - } - - #endregion - - #region Session GetService Tests - - [TestMethod] - public void Session_GetService_ReturnsMetadata() - { - var metadata = _session.GetService(typeof(ChatClientMetadata)) as ChatClientMetadata; - Assert.IsNotNull(metadata); - Assert.AreEqual("google-genai", metadata.ProviderName); - Assert.AreEqual("test-model", metadata.DefaultModelId); - } - - [TestMethod] - public void Session_GetService_ReturnsSelf() - { - var self = _session.GetService(typeof(GoogleGenAIRealtimeSession)); - Assert.AreSame(_session, self); - } - - [TestMethod] - public void Session_GetService_ReturnsInnerAsyncSession() - { - var inner = _session.GetService(typeof(AsyncSession)); - Assert.AreSame(_asyncSession, inner); - } - - [TestMethod] - public void Session_GetService_NullServiceType_ThrowsArgumentNullException() - { - Assert.ThrowsException( - () => _session.GetService(null!)); - } - - [TestMethod] - public void Session_GetService_WithKey_ReturnsNull() - { - var result = _session.GetService(typeof(ChatClientMetadata), serviceKey: "key"); - Assert.IsNull(result); - } - - [TestMethod] - public void Session_GetService_UnknownType_ReturnsNull() - { - var result = _session.GetService(typeof(string)); - Assert.IsNull(result); - } - - #endregion - - #region Session DisposeAsync Tests - - [TestMethod] - public async Task Session_DisposeAsync_IsIdempotent() - { - var session = new GoogleGenAIRealtimeSession(_asyncSession, "model", null); - - // Should not throw on double dispose - await session.DisposeAsync(); - await session.DisposeAsync(); - } - - #endregion - - #region SendAsync Guard Tests - - [TestMethod] - public async Task SendAsync_NullMessage_ThrowsArgumentNullException() - { - await Assert.ThrowsExceptionAsync( - () => _session.SendAsync(null!)); - } - - [TestMethod] - public async Task SendAsync_AfterDispose_ThrowsObjectDisposedException() - { - await _session.DisposeAsync(); - - await Assert.ThrowsExceptionAsync( - () => _session.SendAsync(new CreateResponseRealtimeClientMessage())); - } - - [TestMethod] - public async Task SendAsync_CancelledToken_ThrowsOperationCanceledException() - { - using var cts = new CancellationTokenSource(); - cts.Cancel(); - - await Assert.ThrowsExceptionAsync( - () => _session.SendAsync(new CreateResponseRealtimeClientMessage(), cts.Token)); - } - - [TestMethod] - public async Task SendAsync_SessionUpdate_DoesNotUpdateOptions() - { - var initialOptions = new RealtimeSessionOptions { Model = "initial-model" }; - var session = new GoogleGenAIRealtimeSession(_asyncSession, "initial-model", initialOptions); - - var newOptions = new RealtimeSessionOptions { Model = "updated-model" }; - var msg = new SessionUpdateRealtimeClientMessage(newOptions); - - await session.SendAsync(msg); - - // Gemini does not support mid-session updates; options should remain unchanged. - Assert.AreSame(initialOptions, session.Options); - - await session.DisposeAsync(); - } - - [TestMethod] - public void SendAsync_SessionUpdate_NullOptions_ThrowsArgumentNullException() - { - Assert.ThrowsException( - () => new SessionUpdateRealtimeClientMessage(null!)); - } - - [TestMethod] - public async Task SendAsync_UnknownMessageType_DoesNotThrow() - { - // A generic RealtimeClientMessage should just be ignored (default case) - var msg = new RealtimeClientMessage(); - await _session.SendAsync(msg); - } - - #endregion - - #region Audio Buffering Tests - - [TestMethod] - public void SendAsync_AudioAppend_NoContent_ThrowsArgumentNullException() - { - Assert.ThrowsException( - () => new InputAudioBufferAppendRealtimeClientMessage(null!)); - } - - [TestMethod] - public async Task SendAsync_AudioAppend_NonAudioContent_DoesNotThrow() - { - var msg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent("data:text/plain;base64,SGVsbG8=", "text/plain")); - await _session.SendAsync(msg); - } - - [TestMethod] - public async Task SendAsync_AudioAppendThenCommit_SendsFrames() - { - // Prepare audio content - byte[] audioData = new byte[100]; - new Random(42).NextBytes(audioData); - string base64 = Convert.ToBase64String(audioData); - var dataUri = $"data:audio/pcm;base64,{base64}"; - - var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); - - // Track SendAsync calls on the WebSocket - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - var copy = new byte[buffer.Count]; - Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); - sentMessages.Add(copy); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(appendMsg); - - // Nothing sent yet (just buffered) - Assert.AreEqual(0, sentMessages.Count); - - // Commit triggers actual send - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // Should have sent: ActivityStart + audio frame(s) + ActivityEnd - Assert.IsTrue(sentMessages.Count >= 3, - $"Expected at least 3 WebSocket messages (ActivityStart + audio + ActivityEnd), got {sentMessages.Count}"); - } - - [TestMethod] - public async Task SendAsync_AudioCommit_EmptyBuffer_DoesNothing() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - var copy = new byte[buffer.Count]; - Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); - sentMessages.Add(copy); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - Assert.AreEqual(0, sentMessages.Count); - } - - [TestMethod] - public async Task SendAsync_AudioAppend_ExceedsBufferLimit_ThrowsInvalidOperationException() - { - // Create audio data close to the 10MB limit - byte[] largeAudio = new byte[10 * 1024 * 1024 + 1]; - string base64 = Convert.ToBase64String(largeAudio); - var dataUri = $"data:audio/pcm;base64,{base64}"; - - var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); - - await Assert.ThrowsExceptionAsync( - () => _session.SendAsync(appendMsg)); - } - - [TestMethod] - public async Task SendAsync_AudioAppend_LargeFrame_SplitsIntoMultipleFrames() - { - // Create audio data larger than 32KB frame limit (e.g. 70KB) - byte[] audioData = new byte[70_000]; - new Random(42).NextBytes(audioData); - string base64 = Convert.ToBase64String(audioData); - var dataUri = $"data:audio/pcm;base64,{base64}"; - - var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); - - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - var copy = new byte[buffer.Count]; - Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); - sentMessages.Add(copy); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(appendMsg); - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // 70KB audio = 3 frames (32K + 32K + 6K) + ActivityStart + ActivityEnd = 5 messages - Assert.AreEqual(5, sentMessages.Count, - $"Expected 5 WebSocket messages (ActivityStart + 3 audio frames + ActivityEnd), got {sentMessages.Count}"); - } - - #endregion - - #region GetStreamingResponseAsync Tests - - [TestMethod] - public async Task GetStreamingResponseAsync_AfterDispose_ThrowsObjectDisposedException() - { - await _session.DisposeAsync(); - - await Assert.ThrowsExceptionAsync(async () => - { - await foreach (var _ in _session.GetStreamingResponseAsync()) - { - } - }); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_NullMessage_YieldsBreak() - { - // AsyncSession.ReceiveAsync returns null on close - var closeResult = new WebSocketReceiveResult( - 0, WebSocketMessageType.Close, true, - WebSocketCloseStatus.NormalClosure, "done"); - - _mockWebSocket - .Setup(ws => ws.ReceiveAsync( - It.IsAny>(), - It.IsAny())) - .ReturnsAsync(closeResult); - - var messages = new List(); - await foreach (var msg in _session.GetStreamingResponseAsync()) - { - messages.Add(msg); - } - - Assert.AreEqual(0, messages.Count); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_CallerCancelled_YieldsNoResults() - { - // A pre-cancelled token causes the while-loop condition to be false immediately, - // so the iterator completes without throwing. - using var cts = new CancellationTokenSource(); - cts.Cancel(); - - var messages = new List(); - await foreach (var msg in _session.GetStreamingResponseAsync(cts.Token)) - { - messages.Add(msg); - } - - Assert.AreEqual(0, messages.Count); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_WebSocketException_YieldsBreak() - { - _mockWebSocket - .Setup(ws => ws.ReceiveAsync( - It.IsAny>(), - It.IsAny())) - .ThrowsAsync(new WebSocketException("connection closed")); - - var messages = new List(); - await foreach (var msg in _session.GetStreamingResponseAsync()) - { - messages.Add(msg); - } - - Assert.AreEqual(0, messages.Count); - } - - #endregion - - #region MapServerMessage Tests - - [TestMethod] - public async Task GetStreamingResponseAsync_SetupComplete_IsSkipped() - { - SetupReceiveOnce(new LiveServerMessage { SetupComplete = new LiveServerSetupComplete() }); - - var messages = await CollectMessages(); - - Assert.AreEqual(0, messages.Count); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_TextResponse_EmitsResponseCreatedAndTextDelta() - { - SetupReceiveOnce(new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List { new Part { Text = "Hello world" } } - } - } - }); - - var messages = await CollectMessages(); - - // Expect: ResponseCreated + OutputTextDelta - Assert.AreEqual(2, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); - Assert.AreEqual("Hello world", ((OutputTextAudioRealtimeServerMessage)messages[1]).Text); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_AudioResponse_EmitsAudioDelta() - { - byte[] audioBytes = new byte[] { 1, 2, 3, 4 }; - SetupReceiveOnce(new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List - { - new Part - { - InlineData = new Blob - { - Data = audioBytes, - MimeType = "audio/pcm", - } - } - } - } - } - }); - - var messages = await CollectMessages(); - - // Expect: ResponseCreated + OutputAudioDelta - Assert.AreEqual(2, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputAudioDelta, messages[1].Type); - var audioMsg = (OutputTextAudioRealtimeServerMessage)messages[1]; - Assert.AreEqual(Convert.ToBase64String(audioBytes), audioMsg.Audio); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_TurnComplete_EmitsResponseDone() - { - SetupReceiveSequence(new[] - { - new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List { new Part { Text = "Hi" } } - } - } - }, - new LiveServerMessage - { - ServerContent = new LiveServerContent { TurnComplete = true } - } - }); - - var messages = await CollectMessages(); - - // Expect: ResponseCreated + OutputTextDelta + ResponseDone - Assert.AreEqual(3, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_InputTranscription_EmitsTranscriptionCompleted() - { - SetupReceiveOnce(new LiveServerMessage - { - ServerContent = new LiveServerContent - { - InputTranscription = new Transcription { Text = "what is the weather?" } - } - }); - - var messages = await CollectMessages(); - - Assert.AreEqual(1, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.InputAudioTranscriptionCompleted, messages[0].Type); - var transcription = (InputAudioTranscriptionRealtimeServerMessage)messages[0]; - Assert.AreEqual("what is the weather?", transcription.Transcription); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_OutputTranscription_EmitsTranscriptionDelta() - { - SetupReceiveOnce(new LiveServerMessage - { - ServerContent = new LiveServerContent - { - OutputTranscription = new Transcription { Text = "The weather is sunny." } - } - }); - - var messages = await CollectMessages(); - - Assert.AreEqual(1, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.OutputAudioTranscriptionDelta, messages[0].Type); - var outputMsg = (OutputTextAudioRealtimeServerMessage)messages[0]; - Assert.AreEqual("The weather is sunny.", outputMsg.Text); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_ToolCall_EmitsResponseCreatedAndFunctionCall() - { - SetupReceiveOnce(new LiveServerMessage - { - ToolCall = new LiveServerToolCall - { - FunctionCalls = new List - { - new FunctionCall - { - Id = "call-1", - Name = "get_weather", - Args = new Dictionary { ["city"] = "Seattle" } - } - } - } - }); - - var messages = await CollectMessages(); - - // Expect: ResponseCreated + ResponseOutputItemAdded + ResponseOutputItemDone - Assert.AreEqual(3, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[1].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[2].Type); - - var itemMsg = (ResponseOutputItemRealtimeServerMessage)messages[1]; - var functionCall = itemMsg.Item?.Contents?.OfType().First(); - Assert.IsNotNull(functionCall); - Assert.AreEqual("call-1", functionCall.CallId); - Assert.AreEqual("get_weather", functionCall.Name); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_GoAway_EmitsError() - { - SetupReceiveOnce(new LiveServerMessage - { - GoAway = new LiveServerGoAway() - }); - - var messages = await CollectMessages(); - - Assert.AreEqual(1, messages.Count); - var errorMsg = messages[0] as ErrorRealtimeServerMessage; - Assert.IsNotNull(errorMsg); - Assert.IsNotNull(errorMsg.Error); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_UsageMetadata_EmitsResponseDone_WhenResponseInProgress() - { - SetupReceiveSequence(new[] - { - // Start a response so _responseInProgress is true - new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List { new Part { Text = "response" } } - } - } - }, - // Usage metadata should emit ResponseDone - new LiveServerMessage - { - UsageMetadata = new UsageMetadata - { - PromptTokenCount = 10, - ResponseTokenCount = 20, - TotalTokenCount = 30, - } - } - }); - - var messages = await CollectMessages(); - - // ResponseCreated + OutputTextDelta + ResponseDone(usage) - Assert.AreEqual(3, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); - var responseDone = (ResponseCreatedRealtimeServerMessage)messages[2]; - Assert.IsNotNull(responseDone.Usage); - Assert.AreEqual(10, responseDone.Usage.InputTokenCount); - Assert.AreEqual(20, responseDone.Usage.OutputTokenCount); - Assert.AreEqual(30, responseDone.Usage.TotalTokenCount); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_UsageMetadata_NoResponseInProgress_IsSkipped() - { - // No prior model response — _responseInProgress is false - SetupReceiveOnce(new LiveServerMessage - { - UsageMetadata = new UsageMetadata - { - PromptTokenCount = 5, - ResponseTokenCount = 10, - TotalTokenCount = 15, - } - }); - - var messages = await CollectMessages(); - - // Usage metadata without a response in progress should be skipped - Assert.AreEqual(0, messages.Count); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_TurnComplete_PreventsDoubleResponseDone_FromUsage() - { - SetupReceiveSequence(new[] - { - // Model response - new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List { new Part { Text = "Hi" } } - } - } - }, - // TurnComplete — emits ResponseDone and resets _responseInProgress - new LiveServerMessage - { - ServerContent = new LiveServerContent { TurnComplete = true } - }, - // Usage after TurnComplete — should NOT emit another ResponseDone - new LiveServerMessage - { - UsageMetadata = new UsageMetadata - { - PromptTokenCount = 10, - ResponseTokenCount = 20, - TotalTokenCount = 30, - } - } - }); - - var messages = await CollectMessages(); - - // ResponseCreated + OutputTextDelta + ResponseDone(TurnComplete) — no second ResponseDone - Assert.AreEqual(3, messages.Count); - var responseDoneMessages = messages.Where( - m => m.Type == RealtimeServerMessageType.ResponseDone).ToList(); - Assert.AreEqual(1, responseDoneMessages.Count, "Should only have one ResponseDone"); - } - - #endregion - - #region ConversationItemCreate Tests - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_TextContent_SendsClientContent() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("Hello from user") }, - role: ChatRole.User)); - - await _session.SendAsync(msg); - - Assert.AreEqual(1, sentMessages.Count); - // Verify the sent JSON contains our text - Assert.IsTrue(sentMessages[0].Contains("Hello from user"), - "Sent message should contain the text content"); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_FunctionResult_SendsToolResponse() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new FunctionResultContent("call-123", "sunny") }, - role: ChatRole.Tool)); - - await _session.SendAsync(msg); - - // Function results are buffered until CreateResponse - Assert.AreEqual(0, sentMessages.Count, - "Function results should be buffered, not sent immediately"); - - // Flush via CreateResponse - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - Assert.AreEqual(1, sentMessages.Count, - "Flushed tool response should be sent as a single WebSocket message"); - Assert.IsTrue(sentMessages[0].Contains("call-123"), - "Tool response should contain the call ID"); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_FunctionResult_IncludesFunctionName() - { - // First, simulate receiving a tool call so the session caches the CallId → Name mapping. - SetupReceiveOnce(new LiveServerMessage - { - ToolCall = new LiveServerToolCall - { - FunctionCalls = new List - { - new FunctionCall { Id = "call-42", Name = "get_weather" } - } - } - }); - - await CollectMessages(); - - // Now send the function result back - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new FunctionResultContent("call-42", "sunny") }, - role: ChatRole.Tool)); - - await _session.SendAsync(msg); - - // Function results are buffered until CreateResponse - Assert.AreEqual(0, sentMessages.Count); - - // Flush via CreateResponse - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("get_weather"), - "Tool response should include the function name from the original tool call"); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_EmptyContents_DoesNotSend() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List(), - role: ChatRole.User)); - - await _session.SendAsync(msg); - - Assert.AreEqual(0, sentMessages.Count); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_AssistantRole_MappedToModel() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("I am the model") }, - role: ChatRole.Assistant)); - - await _session.SendAsync(msg); - - Assert.AreEqual(1, sentMessages.Count); - // Gemini uses "model" role, not "assistant" - Assert.IsTrue(sentMessages[0].Contains("model"), - "Assistant role should be mapped to 'model' for Gemini"); - } - - #endregion - - #region BuildLiveConnectConfig Tests - - [TestMethod] - public void BuildLiveConnectConfig_NullOptions_DefaultsToAudioModality() - { - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(null); - - Assert.IsNotNull(config.ResponseModalities); - Assert.AreEqual(1, config.ResponseModalities.Count); - Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); - Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - } - - [TestMethod] - public void BuildLiveConnectConfig_SystemInstructions_MappedCorrectly() - { - var options = new RealtimeSessionOptions - { - Instructions = "You are a helpful assistant.", - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.SystemInstruction); - Assert.AreEqual(1, config.SystemInstruction.Parts.Count); - Assert.AreEqual("You are a helpful assistant.", config.SystemInstruction.Parts[0].Text); - Assert.AreEqual("user", config.SystemInstruction.Role); - } - - [TestMethod] - public void BuildLiveConnectConfig_EmptyInstructions_NoSystemInstruction() - { - var options = new RealtimeSessionOptions - { - Instructions = "", - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNull(config.SystemInstruction); - } - - [TestMethod] - public void BuildLiveConnectConfig_OutputModalities_AudioAndText() - { - var options = new RealtimeSessionOptions - { - OutputModalities = new List { "audio", "text" }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.AreEqual(2, config.ResponseModalities.Count); - Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); - Assert.AreEqual(Modality.Text, config.ResponseModalities[1]); - } - - [TestMethod] - public void BuildLiveConnectConfig_NoOutputModalities_DefaultsToAudio() - { - var options = new RealtimeSessionOptions(); - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.AreEqual(1, config.ResponseModalities.Count); - Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); - } - - [TestMethod] - public void BuildLiveConnectConfig_UnknownModality_DefaultsToText() - { - var options = new RealtimeSessionOptions - { - OutputModalities = new List { "video" }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.AreEqual(1, config.ResponseModalities.Count); - Assert.AreEqual(Modality.Text, config.ResponseModalities[0]); - } - - [TestMethod] - public void BuildLiveConnectConfig_Voice_MappedToSpeechConfig() - { - var options = new RealtimeSessionOptions - { - Voice = "Puck", - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.SpeechConfig); - Assert.IsNotNull(config.SpeechConfig.VoiceConfig); - Assert.AreEqual("Puck", config.SpeechConfig.VoiceConfig.PrebuiltVoiceConfig.VoiceName); - } - - [TestMethod] - public void BuildLiveConnectConfig_NullVoice_NoSpeechConfig() - { - var options = new RealtimeSessionOptions(); - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNull(config.SpeechConfig); - } - - [TestMethod] - public void BuildLiveConnectConfig_MaxOutputTokens_MappedToGenerationConfig() - { - var options = new RealtimeSessionOptions - { - MaxOutputTokens = 500, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.GenerationConfig); - Assert.AreEqual(500, config.GenerationConfig.MaxOutputTokens); - } - - [TestMethod] - public void BuildLiveConnectConfig_NoMaxOutputTokens_NoGenerationConfig() - { - var options = new RealtimeSessionOptions(); - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNull(config.GenerationConfig); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionOptions_EnablesBothDirections() - { - var options = new RealtimeSessionOptions - { - TranscriptionOptions = new TranscriptionOptions(), - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.InputAudioTranscription); - Assert.IsNotNull(config.OutputAudioTranscription); - } - - [TestMethod] - public void BuildLiveConnectConfig_NoTranscriptionOptions_NoTranscription() - { - var options = new RealtimeSessionOptions(); - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNull(config.InputAudioTranscription); - Assert.IsNull(config.OutputAudioTranscription); - } - - [TestMethod] - public void BuildLiveConnectConfig_NoVadOptions_DisablesVadByDefault() - { - var options = new RealtimeSessionOptions(); - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.RealtimeInputConfig); - Assert.IsNotNull(config.RealtimeInputConfig.AutomaticActivityDetection); - Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - } - - [TestMethod] - public void BuildLiveConnectConfig_VadEnabled_EnablesAutomaticActivityDetection() - { - var options = new RealtimeSessionOptions - { - VoiceActivityDetection = new VoiceActivityDetectionOptions - { - Enabled = true, - AllowInterruption = true, - }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.RealtimeInputConfig); - Assert.IsNotNull(config.RealtimeInputConfig.AutomaticActivityDetection); - Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - Assert.AreEqual( - ActivityHandling.StartOfActivityInterrupts, - config.RealtimeInputConfig.ActivityHandling); - } - - [TestMethod] - public void BuildLiveConnectConfig_VadEnabled_NoInterruption() - { - var options = new RealtimeSessionOptions - { - VoiceActivityDetection = new VoiceActivityDetectionOptions - { - Enabled = true, - AllowInterruption = false, - }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.RealtimeInputConfig); - Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - Assert.AreEqual( - ActivityHandling.NoInterruption, - config.RealtimeInputConfig.ActivityHandling); - } - - [TestMethod] - public void BuildLiveConnectConfig_VadDisabled_DisablesAutomaticActivityDetection() - { - var options = new RealtimeSessionOptions - { - VoiceActivityDetection = new VoiceActivityDetectionOptions - { - Enabled = false, - }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.RealtimeInputConfig); - Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - } - - [TestMethod] - public void BuildLiveConnectConfig_Tools_MappedToFunctionDeclarations() - { - var fn = AIFunctionFactory.Create( - (string city) => $"Weather in {city}: sunny", - "get_weather", - "Gets the weather"); - - var options = new RealtimeSessionOptions - { - Tools = new List { fn }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.Tools); - Assert.AreEqual(1, config.Tools.Count); - Assert.AreEqual(1, config.Tools[0].FunctionDeclarations.Count); - Assert.AreEqual("get_weather", config.Tools[0].FunctionDeclarations[0].Name); - Assert.AreEqual("Gets the weather", config.Tools[0].FunctionDeclarations[0].Description); - } - - [TestMethod] - public void BuildLiveConnectConfig_EmptyTools_NoToolsConfig() - { - var options = new RealtimeSessionOptions - { - Tools = new List(), - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNull(config.Tools); - } - - [TestMethod] - public void BuildLiveConnectConfig_AllOptionsCombined_MapsEverything() - { - var fn = AIFunctionFactory.Create( - (string q) => "result", - "search", - "Searches things"); - - var options = new RealtimeSessionOptions - { - Instructions = "Be concise.", - OutputModalities = new List { "audio", "text" }, - Voice = "Aoede", - MaxOutputTokens = 1000, - Tools = new List { fn }, - TranscriptionOptions = new TranscriptionOptions(), - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.AreEqual("Be concise.", config.SystemInstruction.Parts[0].Text); - Assert.AreEqual(2, config.ResponseModalities.Count); - Assert.AreEqual("Aoede", config.SpeechConfig.VoiceConfig.PrebuiltVoiceConfig.VoiceName); - Assert.AreEqual(1000, config.GenerationConfig.MaxOutputTokens); - Assert.AreEqual(1, config.Tools[0].FunctionDeclarations.Count); - Assert.IsNotNull(config.InputAudioTranscription); - Assert.IsNotNull(config.OutputAudioTranscription); - Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionMode_OnlyInputTranscription() - { - var options = new RealtimeSessionOptions - { - SessionKind = RealtimeSessionKind.Transcription, - TranscriptionOptions = new TranscriptionOptions(), - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.InputAudioTranscription); - Assert.IsNull(config.OutputAudioTranscription); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionMode_TextModalityOnly() - { - var options = new RealtimeSessionOptions - { - SessionKind = RealtimeSessionKind.Transcription, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.AreEqual(1, config.ResponseModalities.Count); - Assert.AreEqual(Modality.Text, config.ResponseModalities[0]); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionMode_NoVoiceOrToolsOrInstructions() - { - var fn = AIFunctionFactory.Create( - (string q) => "result", - "search", - "Searches things"); - - var options = new RealtimeSessionOptions - { - SessionKind = RealtimeSessionKind.Transcription, - Instructions = "Be concise.", - Voice = "Aoede", - MaxOutputTokens = 500, - Tools = new List { fn }, - TranscriptionOptions = new TranscriptionOptions(), - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - // Transcription-only: conversation-oriented options are ignored - Assert.IsNull(config.SystemInstruction); - Assert.IsNull(config.SpeechConfig); - Assert.IsNull(config.GenerationConfig); - Assert.IsNull(config.Tools); - Assert.IsNotNull(config.InputAudioTranscription); - Assert.IsNull(config.OutputAudioTranscription); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionMode_LanguageCodeMapped() - { - var options = new RealtimeSessionOptions - { - SessionKind = RealtimeSessionKind.Transcription, - TranscriptionOptions = new TranscriptionOptions { SpeechLanguage = "en-US" }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.InputAudioTranscription); - Assert.AreEqual(1, config.InputAudioTranscription.LanguageCodes.Count); - Assert.AreEqual("en-US", config.InputAudioTranscription.LanguageCodes[0]); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionMode_NoLanguage_NoLanguageCodes() - { - var options = new RealtimeSessionOptions - { - SessionKind = RealtimeSessionKind.Transcription, - TranscriptionOptions = new TranscriptionOptions(), - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.InputAudioTranscription); - Assert.IsNull(config.InputAudioTranscription.LanguageCodes); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionMode_VadEnabled() - { - var options = new RealtimeSessionOptions - { - SessionKind = RealtimeSessionKind.Transcription, - VoiceActivityDetection = new VoiceActivityDetectionOptions { Enabled = true, AllowInterruption = false }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - Assert.AreEqual(ActivityHandling.NoInterruption, config.RealtimeInputConfig.ActivityHandling); - } - - [TestMethod] - public void BuildLiveConnectConfig_TranscriptionMode_DefaultVadDisabled() - { - var options = new RealtimeSessionOptions - { - SessionKind = RealtimeSessionKind.Transcription, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); - } - - [TestMethod] - public void BuildLiveConnectConfig_ConversationMode_LanguageCodeMapped() - { - var options = new RealtimeSessionOptions - { - TranscriptionOptions = new TranscriptionOptions { SpeechLanguage = "ja-JP" }, - }; - - var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); - - Assert.IsNotNull(config.InputAudioTranscription); - Assert.AreEqual(1, config.InputAudioTranscription.LanguageCodes.Count); - Assert.AreEqual("ja-JP", config.InputAudioTranscription.LanguageCodes[0]); - // Output transcription is also enabled in conversation mode (no language codes) - Assert.IsNotNull(config.OutputAudioTranscription); - } - - #endregion - - #region ExtractDataBytes Tests - - [TestMethod] - public void ExtractDataBytes_ValidBase64DataUri_ExtractsBytes() - { - byte[] expected = new byte[] { 1, 2, 3, 4, 5 }; - string base64 = Convert.ToBase64String(expected); - var content = new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"); - - byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); - - CollectionAssert.AreEqual(expected, result); - } - - [TestMethod] - public void ExtractDataBytes_InvalidBase64_FallsBackToData() - { - byte[] expected = new byte[] { 10, 20, 30 }; - var content = new DataContent(expected, "audio/pcm"); - - byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); - - CollectionAssert.AreEqual(expected, result); - } - - [TestMethod] - public void ExtractDataBytes_NullUri_UsesDirectData() - { - byte[] expected = new byte[] { 7, 8, 9 }; - var content = new DataContent(expected, "audio/pcm"); - - byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); - - CollectionAssert.AreEqual(expected, result); - } - - [TestMethod] - public void ExtractDataBytes_DataUriNoComma_FallsBackToData() - { - byte[] expected = new byte[] { 42, 43, 44 }; - // DataContent with raw byte data but no URI - var content = new DataContent(expected, "audio/pcm"); - - byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); - - CollectionAssert.AreEqual(expected, result); - } - - #endregion - - #region CreateResponseRealtimeClientMessage Tests - - [TestMethod] - public async Task SendAsync_CreateResponse_AfterConversationItem_SendsTurnComplete() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - // Send a conversation item with text - var itemMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("Hello") }, - role: ChatRole.User)); - await _session.SendAsync(itemMsg); - - // Text input auto-triggers a response in Gemini's Live API, so the text - // send happened during CreateConversationItem, not CreateResponse. - Assert.AreEqual(1, sentMessages.Count, "Text should be sent via SendRealtimeInputAsync"); - Assert.IsTrue(sentMessages[0].Contains("Hello"), - "The sent message should contain the text content"); - - sentMessages.Clear(); - - // CreateResponse after text input does nothing — text auto-triggers. - var createResp = new CreateResponseRealtimeClientMessage(); - await _session.SendAsync(createResp); - - Assert.AreEqual(0, sentMessages.Count, - "CreateResponse should not send additional messages after text input (auto-triggers)"); - } - - [TestMethod] - public async Task SendAsync_CreateResponse_AfterAudioCommit_DoesNotSendTurnComplete() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - // Send audio append + commit (sets _lastInputWasRealtime = true) - byte[] audioData = new byte[100]; - new Random(42).NextBytes(audioData); - string base64 = Convert.ToBase64String(audioData); - - await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - int countBeforeCreateResponse = sentMessages.Count; - - // CreateResponse should NOT send anything (audio commit already sent ActivityEnd) - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - Assert.AreEqual(countBeforeCreateResponse, sentMessages.Count, - "CreateResponse should not send TurnComplete after realtime audio input"); - } - - [TestMethod] - public async Task SendAsync_CreateResponse_AfterToolResponse_DoesNotSendTurnComplete() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - // Send a function result (simulates middleware returning tool response) - var toolResultMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new FunctionResultContent("call-1", "sunny") }, - role: ChatRole.Tool)); - await _session.SendAsync(toolResultMsg); - - // CreateResponse should flush the tool response but NOT send TurnComplete - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - // Should have exactly 1 message: the batched tool response. No TurnComplete. - Assert.AreEqual(1, sentMessages.Count, - "Should send tool response but NOT TurnComplete after function result"); - Assert.IsTrue(sentMessages[0].Contains("call-1"), - "Tool response should contain the call ID"); - Assert.IsFalse(sentMessages[0].Contains("turnComplete"), - "Should not contain turnComplete after tool response"); - } - - [TestMethod] - public async Task SendAsync_CreateResponse_BatchesMultipleToolResponses() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - // Simulate middleware sending separate CreateConversationItem per function result - var toolResult1 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new FunctionResultContent("call-1", "sunny") }, - role: ChatRole.Tool)); - var toolResult2 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new FunctionResultContent("call-2", "72F") }, - role: ChatRole.Tool)); - - await _session.SendAsync(toolResult1); - await _session.SendAsync(toolResult2); - - // No WebSocket sends yet — results are buffered - Assert.AreEqual(0, sentMessages.Count, - "Function results should be buffered until CreateResponse"); - - // CreateResponse flushes all results in a single batched SendToolResponseAsync - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - Assert.AreEqual(1, sentMessages.Count, - "All function results should be sent in a single WebSocket message"); - Assert.IsTrue(sentMessages[0].Contains("call-1"), - "Batched tool response should contain first call ID"); - Assert.IsTrue(sentMessages[0].Contains("call-2"), - "Batched tool response should contain second call ID"); - Assert.IsFalse(sentMessages[0].Contains("turnComplete"), - "Should not contain turnComplete after batched tool responses"); - } - - [TestMethod] - public async Task SendAsync_CreateResponse_AfterToolResponse_NextTextSendResetsFlagCorrectly() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - // Complete a full tool call cycle: tool response → CreateResponse - var toolResultMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new FunctionResultContent("call-1", "sunny") }, - role: ChatRole.Tool)); - await _session.SendAsync(toolResultMsg); - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - sentMessages.Clear(); - - // Now send normal text → CreateResponse. Text auto-triggers; CreateResponse is a no-op. - var textMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("Hello again") }, - role: ChatRole.User)); - await _session.SendAsync(textMsg); - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - // Should have 1 message: the text content sent via SendRealtimeInputAsync. - // CreateResponse does not send a turnComplete — text auto-triggers in Gemini. - Assert.AreEqual(1, sentMessages.Count, - "Normal text followed by CreateResponse should send only the text content"); - Assert.IsTrue(sentMessages[0].Contains("Hello again"), - "The sent message should contain the text content"); - } - - #endregion - - #region Additional MapServerMessage Tests - - [TestMethod] - public async Task GetStreamingResponseAsync_GenerationComplete_EmitsResponseDone() - { - SetupReceiveSequence(new[] - { - new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List { new Part { Text = "Response" } } - } - } - }, - new LiveServerMessage - { - ServerContent = new LiveServerContent { GenerationComplete = true } - } - }); - - var messages = await CollectMessages(); - - Assert.AreEqual(3, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_ToolCallCancellation_EmitsRawContentOnly() - { - SetupReceiveOnce(new LiveServerMessage - { - ToolCallCancellation = new LiveServerToolCallCancellation - { - Ids = new List { "call-1", "call-2" }, - } - }); - - var messages = await CollectMessages(); - - Assert.AreEqual(1, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.RawContentOnly, messages[0].Type); - Assert.IsNotNull(messages[0].RawRepresentation); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_MultipleTextParts_EmitsMultipleDeltas() - { - SetupReceiveOnce(new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List - { - new Part { Text = "Part one" }, - new Part { Text = "Part two" }, - } - } - } - }); - - var messages = await CollectMessages(); - - // ResponseCreated + 2 OutputTextDelta - Assert.AreEqual(3, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[2].Type); - Assert.AreEqual("Part one", ((OutputTextAudioRealtimeServerMessage)messages[1]).Text); - Assert.AreEqual("Part two", ((OutputTextAudioRealtimeServerMessage)messages[2]).Text); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_MixedAudioAndText_EmitsBothDeltas() - { - byte[] audioBytes = new byte[] { 10, 20, 30 }; - SetupReceiveOnce(new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List - { - new Part - { - InlineData = new Blob - { - Data = audioBytes, - MimeType = "audio/pcm", - } - }, - new Part { Text = "Hello there" }, - } - } - } - }); - - var messages = await CollectMessages(); - - // ResponseCreated + OutputAudioDelta + OutputTextDelta - Assert.AreEqual(3, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputAudioDelta, messages[1].Type); - Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[2].Type); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_MultipleModelTurns_OnlyOneResponseCreated() - { - SetupReceiveSequence(new[] - { - new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List { new Part { Text = "First chunk" } } - } - } - }, - new LiveServerMessage - { - ServerContent = new LiveServerContent - { - ModelTurn = new Content - { - Parts = new List { new Part { Text = "Second chunk" } } - } - } - }, - }); - - var messages = await CollectMessages(); - - // Only one ResponseCreated for the entire response cycle - var responseCreatedCount = messages.Count(m => m.Type == RealtimeServerMessageType.ResponseCreated); - Assert.AreEqual(1, responseCreatedCount, - "Should only emit one ResponseCreated across multiple ModelTurn messages in the same response"); - - // But both text deltas should appear - var textDeltas = messages.Where(m => m.Type == RealtimeServerMessageType.OutputTextDelta).ToList(); - Assert.AreEqual(2, textDeltas.Count); - Assert.AreEqual("First chunk", ((OutputTextAudioRealtimeServerMessage)textDeltas[0]).Text); - Assert.AreEqual("Second chunk", ((OutputTextAudioRealtimeServerMessage)textDeltas[1]).Text); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_MultipleToolCalls_EmitsAllItems() - { - SetupReceiveOnce(new LiveServerMessage - { - ToolCall = new LiveServerToolCall - { - FunctionCalls = new List - { - new FunctionCall { Id = "c1", Name = "fn1", Args = new Dictionary { ["a"] = "1" } }, - new FunctionCall { Id = "c2", Name = "fn2", Args = new Dictionary { ["b"] = "2" } }, - } - } - }); - - var messages = await CollectMessages(); - - // ResponseCreated + (ResponseOutputItemAdded + ResponseOutputItemDone) × 2 - Assert.AreEqual(5, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[1].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[2].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[3].Type); - Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[4].Type); - - var fn1 = ((ResponseOutputItemRealtimeServerMessage)messages[1]).Item?.Contents?.OfType().First(); - var fn2 = ((ResponseOutputItemRealtimeServerMessage)messages[3]).Item?.Contents?.OfType().First(); - Assert.AreEqual("fn1", fn1?.Name); - Assert.AreEqual("fn2", fn2?.Name); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_ToolCallThenTurnComplete_EmitsResponseDone() - { - SetupReceiveSequence(new[] - { - new LiveServerMessage - { - ToolCall = new LiveServerToolCall - { - FunctionCalls = new List - { - new FunctionCall { Id = "c1", Name = "fn1" } - } - } - }, - new LiveServerMessage - { - ServerContent = new LiveServerContent { TurnComplete = true } - } - }); - - var messages = await CollectMessages(); - - // ResponseCreated + ItemAdded + ItemDone + ResponseDone - Assert.AreEqual(4, messages.Count); - Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[3].Type); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_ToolCallWithNullArgs_EmitsEmptyArguments() - { - SetupReceiveOnce(new LiveServerMessage - { - ToolCall = new LiveServerToolCall - { - FunctionCalls = new List - { - new FunctionCall { Id = "c1", Name = "no_args_fn", Args = null } - } - } - }); - - var messages = await CollectMessages(); - - Assert.AreEqual(3, messages.Count); - var fc = ((ResponseOutputItemRealtimeServerMessage)messages[1]).Item?.Contents?.OfType().First(); - Assert.IsNotNull(fc); - Assert.AreEqual("no_args_fn", fc.Name); - Assert.IsNull(fc.Arguments); - } - - #endregion - - #region ConversationItemCreate Additional Tests - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_UserRole_MapsToUser() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("User text") }, - role: ChatRole.User)); - - await _session.SendAsync(msg); - - // Gemini's SendRealtimeInputAsync sends text only (no role field). - // Verify the text content was sent. - Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("User text"), - "User text should be sent via SendRealtimeInputAsync"); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_NullRole_DefaultsToUser() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("No role text") })); - - await _session.SendAsync(msg); - - // Gemini's SendRealtimeInputAsync sends text only (no role field). - // Verify the text content was sent regardless of the absence of a role. - Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("No role text"), - "Text should be sent via SendRealtimeInputAsync even without a role"); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_MultipleFunctionResults_SendsAll() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - // Multiple function results should all be sent in a single SendToolResponseAsync call - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List - { - new FunctionResultContent("call-1", "result-one"), - new FunctionResultContent("call-2", "result-two"), - }, - role: ChatRole.Tool)); - - await _session.SendAsync(msg); - - // Function results are buffered until CreateResponse - Assert.AreEqual(0, sentMessages.Count, - "Function results should be buffered, not sent immediately"); - - // Flush via CreateResponse - await _session.SendAsync(new CreateResponseRealtimeClientMessage()); - - // All function results are batched into one WebSocket message - Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("call-1")); - Assert.IsTrue(sentMessages[0].Contains("result-one")); - Assert.IsTrue(sentMessages[0].Contains("call-2")); - Assert.IsTrue(sentMessages[0].Contains("result-two")); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_NullContents_DoesNotSend() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List())); - - await _session.SendAsync(msg); - - Assert.AreEqual(0, sentMessages.Count); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_AudioContent_SendsInlineData() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - byte[] audioData = new byte[] { 1, 2, 3 }; - string base64 = Convert.ToBase64String(audioData); - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm") }, - role: ChatRole.User)); - - await _session.SendAsync(msg); - - Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("audio/pcm")); - } - - [TestMethod] - public async Task SendAsync_ConversationItemCreate_ImageContent_SendsInlineData() - { - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - byte[] imageData = new byte[] { 0x89, 0x50, 0x4E, 0x47 }; - string base64 = Convert.ToBase64String(imageData); - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new DataContent($"data:image/png;base64,{base64}", "image/png") }, - role: ChatRole.User)); - - await _session.SendAsync(msg); - - Assert.AreEqual(1, sentMessages.Count); - Assert.IsTrue(sentMessages[0].Contains("image/png")); - } - - #endregion - - #region Audio Frame Content Verification Tests - - [TestMethod] - public async Task SendAsync_AudioCommit_FrameContentVerification_PreservesData() - { - // Use a known pattern so we can verify exact bytes - byte[] audioData = new byte[64_000]; // Exactly 2 frames - for (int i = 0; i < audioData.Length; i++) - { - audioData[i] = (byte)(i % 256); - } - - string base64 = Convert.ToBase64String(audioData); - var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); - - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(appendMsg); - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // ActivityStart + 2 audio frames + ActivityEnd = 4 messages - Assert.AreEqual(4, sentMessages.Count); - - // Verify the first message is ActivityStart - Assert.IsTrue(sentMessages[0].Contains("activityStart"), - "First message should be ActivityStart"); - - // Verify audio frames contain base64-encoded data - Assert.IsTrue(sentMessages[1].Contains("audio/pcm"), - "Audio frame should contain mime type"); - Assert.IsTrue(sentMessages[2].Contains("audio/pcm"), - "Audio frame should contain mime type"); - - // Verify the last message is ActivityEnd - Assert.IsTrue(sentMessages[3].Contains("activityEnd"), - "Last message should be ActivityEnd"); - } - - [TestMethod] - public async Task SendAsync_AudioCommit_ExactFrameSize_SingleFrame() - { - // Exactly 32000 bytes = exactly 1 frame (no splitting needed) - byte[] audioData = new byte[32_000]; - new Random(42).NextBytes(audioData); - - string base64 = Convert.ToBase64String(audioData); - var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); - - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(appendMsg); - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // ActivityStart + 1 frame + ActivityEnd = 3 - Assert.AreEqual(3, sentMessages.Count); - } - - [TestMethod] - public async Task SendAsync_AudioCommit_FrameBoundary_SplitsCorrectly() - { - // 32001 bytes = 32000 + 1 → 2 frames - byte[] audioData = new byte[32_001]; - new Random(42).NextBytes(audioData); - - string base64 = Convert.ToBase64String(audioData); - var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); - - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(appendMsg); - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // ActivityStart + 2 frames (32000 + 1) + ActivityEnd = 4 - Assert.AreEqual(4, sentMessages.Count); - } - - [TestMethod] - public async Task SendAsync_AudioAppend_MultipleAppends_PreservesOrder() - { - byte[] chunk1 = new byte[100]; - byte[] chunk2 = new byte[200]; - new Random(42).NextBytes(chunk1); - new Random(43).NextBytes(chunk2); - - string base64_1 = Convert.ToBase64String(chunk1); - string base64_2 = Convert.ToBase64String(chunk2); - - await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64_1}", "audio/pcm"))); - await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64_2}", "audio/pcm"))); - - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // ActivityStart + 2 audio frames (both under 32KB) + ActivityEnd = 4 - Assert.AreEqual(4, sentMessages.Count); - } - - #endregion - - #region Audio MIME Type Tests - - [TestMethod] - public async Task SendAsync_AudioCommit_UsesDefaultMimeType_WhenNoInputAudioFormat() - { - // Default session (null options) should use audio/pcm - byte[] audioData = new byte[] { 1, 2, 3 }; - string base64 = Convert.ToBase64String(audioData); - - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // Verify audio frame uses default audio/pcm mime type - Assert.IsTrue(sentMessages[1].Contains("audio/pcm"), "Audio frame should use default audio/pcm"); - } - - [TestMethod] - public async Task SendAsync_AudioCommit_UsesCustomMimeType_WhenInputAudioFormatSet() - { - // Create session with custom audio format - var customOptions = new RealtimeSessionOptions - { - InputAudioFormat = new RealtimeAudioFormat("audio/opus", 48000), - }; - var customSession = new GoogleGenAIRealtimeSession(_asyncSession, "test-model", customOptions); - - byte[] audioData = new byte[] { 1, 2, 3 }; - string base64 = Convert.ToBase64String(audioData); - - var sentMessages = new List(); - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Callback, WebSocketMessageType, bool, CancellationToken>( - (buffer, _, _, _) => - { - sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); - }) - .Returns(Task.CompletedTask); - - await customSession.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/opus;base64,{base64}", "audio/opus"))); - await customSession.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // Verify audio frame uses the custom mime type - Assert.IsTrue(sentMessages[1].Contains("audio/opus"), - "Audio frame should use the configured audio/opus mime type"); - Assert.IsFalse(sentMessages[1].Contains("audio/pcm"), - "Audio frame should NOT contain the default audio/pcm"); - } - - #endregion - - #region Dispose Race Safety Tests - - [TestMethod] - public async Task SendAsync_ConcurrentDispose_DoesNotThrow() - { - // Simulate a race: SendAsync acquires the lock, then DisposeAsync runs. - // The Release() in finally should not throw even if the semaphore is disposed. - byte[] audioData = new byte[] { 1, 2, 3 }; - string base64 = Convert.ToBase64String(audioData); - - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Returns(Task.CompletedTask); - - _mockWebSocket - .Setup(ws => ws.CloseAsync( - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns(Task.CompletedTask); - - // Append audio, then commit and dispose concurrently - await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( - new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); - await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); - - // Dispose should not throw even after sends completed - await _session.DisposeAsync(); - - // Double dispose should also be safe - await _session.DisposeAsync(); - } - - #endregion - - #region DisposeAsync Additional Tests - - [TestMethod] - public async Task DisposeAsync_ClosesUnderlyingWebSocket() - { - _mockWebSocket - .Setup(ws => ws.CloseAsync( - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns(Task.CompletedTask); - - await _session.DisposeAsync(); - - _mockWebSocket.Verify(ws => ws.CloseAsync( - It.IsAny(), - It.IsAny(), - It.IsAny()), Times.Once); - } - - #endregion - - #region Exception Handling Tests - - [TestMethod] - public async Task SendAsync_ObjectDisposedException_FromAsyncSession_Swallowed() - { - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .ThrowsAsync(new ObjectDisposedException("WebSocket")); - - // Send a conversation item that triggers a real send to the WebSocket - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("Hello") }, - role: ChatRole.User)); - - // Should NOT throw — teardown exception is swallowed - await _session.SendAsync(msg); - } - - [TestMethod] - public async Task SendAsync_WebSocketException_FromAsyncSession_Swallowed() - { - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .ThrowsAsync(new WebSocketException("connection lost")); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("Hello") }, - role: ChatRole.User)); - - // Should NOT throw — teardown exception is swallowed - await _session.SendAsync(msg); - } - - [TestMethod] - public async Task SendAsync_InternalOperationCancelled_Swallowed() - { - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .ThrowsAsync(new OperationCanceledException("internal disposal")); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("Hello") }, - role: ChatRole.User)); - - // Should NOT throw — internal cancellation (not the caller's token) is swallowed - await _session.SendAsync(msg); - } - - [TestMethod] - public async Task SendAsync_CallerCancellation_Propagated() - { - using var cts = new CancellationTokenSource(); - cts.Cancel(); - - var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("Hello") }, - role: ChatRole.User)); - - // Caller cancellation should propagate (ThrowIfCancellationRequested at top) - await Assert.ThrowsExceptionAsync( - () => _session.SendAsync(msg, cts.Token)); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_ObjectDisposedException_Swallowed() - { - _mockWebSocket - .Setup(ws => ws.ReceiveAsync( - It.IsAny>(), - It.IsAny())) - .ThrowsAsync(new ObjectDisposedException("WebSocket")); - - var messages = new List(); - await foreach (var msg in _session.GetStreamingResponseAsync()) - { - messages.Add(msg); - } - - Assert.AreEqual(0, messages.Count); - } - - [TestMethod] - public async Task GetStreamingResponseAsync_InternalOperationCancelled_Swallowed() - { - _mockWebSocket - .Setup(ws => ws.ReceiveAsync( - It.IsAny>(), - It.IsAny())) - .ThrowsAsync(new OperationCanceledException("internal cancellation")); - - var messages = new List(); - await foreach (var msg in _session.GetStreamingResponseAsync()) - { - messages.Add(msg); - } - - Assert.AreEqual(0, messages.Count); - } - - #endregion - - #region Helper Methods - - private void SetupReceiveOnce(LiveServerMessage message) - { - string json = JsonSerializer.Serialize(message, JsonConfig.JsonSerializerOptions); - byte[] bytes = Encoding.UTF8.GetBytes(json); - var callCount = 0; - - _mockWebSocket - .Setup(ws => ws.ReceiveAsync( - It.IsAny>(), - It.IsAny())) - .Callback, CancellationToken>((buffer, _) => - { - if (callCount == 0) - { - Buffer.BlockCopy(bytes, 0, buffer.Array!, buffer.Offset, bytes.Length); - } - }) - .Returns, CancellationToken>((_, _) => - { - callCount++; - if (callCount == 1) - { - return Task.FromResult( - new WebSocketReceiveResult(bytes.Length, WebSocketMessageType.Text, true)); - } - return Task.FromResult( - new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, - WebSocketCloseStatus.NormalClosure, "done")); - }); - } - - private void SetupReceiveSequence(LiveServerMessage[] messages) - { - var serialized = messages.Select(m => - { - string json = JsonSerializer.Serialize(m, JsonConfig.JsonSerializerOptions); - return Encoding.UTF8.GetBytes(json); - }).ToList(); - - var callCount = 0; - - _mockWebSocket - .Setup(ws => ws.ReceiveAsync( - It.IsAny>(), - It.IsAny())) - .Callback, CancellationToken>((buffer, _) => - { - if (callCount < serialized.Count) - { - Buffer.BlockCopy(serialized[callCount], 0, buffer.Array!, buffer.Offset, - serialized[callCount].Length); - } - }) - .Returns, CancellationToken>((_, _) => - { - var idx = callCount; - callCount++; - if (idx < serialized.Count) - { - return Task.FromResult( - new WebSocketReceiveResult(serialized[idx].Length, WebSocketMessageType.Text, true)); - } - return Task.FromResult( - new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, - WebSocketCloseStatus.NormalClosure, "done")); - }); - } - - private async Task> CollectMessages() - { - var messages = new List(); - await foreach (var msg in _session.GetStreamingResponseAsync()) - { - messages.Add(msg); - } - return messages; - } - - #endregion - - #region Concurrency Tests - - [TestMethod] - public async Task SendAsync_ConcurrentCalls_AreSerialized() - { - // Verify that concurrent SendAsync calls don't interleave WebSocket sends. - // We track the order of send completions to ensure no overlap. - var sendOrder = new List(); - var gate = new SemaphoreSlim(0); - - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Returns, WebSocketMessageType, bool, CancellationToken>( - async (buffer, _, _, ct) => - { - string json = Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count); - // The first call blocks briefly; if sends aren't serialized, the second - // would start before the first completes. - if (json.Contains("turn-1")) - { - lock (sendOrder) { sendOrder.Add(1); } - gate.Release(); - await Task.Delay(50, ct); - lock (sendOrder) { sendOrder.Add(-1); } - } - else if (json.Contains("turn-2")) - { - await gate.WaitAsync(ct); - lock (sendOrder) { sendOrder.Add(2); } - } - }); - - var msg1 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("turn-1") }, role: ChatRole.User)); - var msg2 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("turn-2") }, role: ChatRole.User)); - - // Launch both sends concurrently - var t1 = _session.SendAsync(msg1); - var t2 = _session.SendAsync(msg2); - await Task.WhenAll(t1, t2); - - // Because sends are serialized, turn-2 can only start after turn-1 completes. - // Expected order: [1, -1, 2] (start-1, end-1, start-2) - Assert.AreEqual(3, sendOrder.Count); - Assert.AreEqual(1, sendOrder[0], "turn-1 should start first"); - Assert.AreEqual(-1, sendOrder[1], "turn-1 should complete before turn-2 starts"); - Assert.AreEqual(2, sendOrder[2], "turn-2 should start after turn-1 completes"); - } - - [TestMethod] - public async Task SendAsync_AudioAppend_DoesNotBlockConcurrentSends() - { - // AudioAppend only buffers — it should NOT acquire the send lock, - // so it completes even while another send holds the lock. - var sendStarted = new TaskCompletionSource(); - var sendCanProceed = new TaskCompletionSource(); - - _mockWebSocket - .Setup(ws => ws.SendAsync( - It.IsAny>(), - WebSocketMessageType.Text, - true, - It.IsAny())) - .Returns, WebSocketMessageType, bool, CancellationToken>( - async (_, _, _, ct) => - { - sendStarted.SetResult(true); - await sendCanProceed.Task; - }); - - // Start a text send that will hold the lock - var textMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( - new List { new TextContent("hello") }, role: ChatRole.User)); - var textSend = _session.SendAsync(textMsg); - - // Wait until the text send has acquired the lock and is in-flight - await sendStarted.Task; - - // AudioAppend should complete immediately (no lock contention) - var audioContent = new DataContent("data:audio/pcm;base64,AQID", "audio/pcm"); - var audioAppend = new InputAudioBufferAppendRealtimeClientMessage(audioContent); - var appendTask = _session.SendAsync(audioAppend); - - // Append should complete quickly even though the text send holds the lock - var completed = await Task.WhenAny(appendTask, Task.Delay(1000)); - Assert.AreSame(appendTask, completed, "AudioAppend should not block on the send lock"); - - // Release the text send - sendCanProceed.SetResult(true); - await textSend; - } - - #endregion -} - -#pragma warning restore MEAI001 +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma warning disable MEAI001 // Experimental AI API + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.WebSockets; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Google.GenAI.Types; +using Microsoft.Extensions.AI; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +#pragma warning disable MEAI001 + +namespace Google.GenAI.Tests; + +[TestClass] +public class GoogleGenAIRealtimeClientTest +{ + #region Extension Method Tests + + [TestMethod] + public void AsIRealtimeClient_NullClient_ThrowsArgumentNullException() + { + Client? client = null; + Assert.ThrowsException(() => client!.AsIRealtimeClient("model")); + } + + [TestMethod] + public void AsIRealtimeClient_ValidClient_ReturnsNonNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = client.AsIRealtimeClient("model"); + Assert.IsNotNull(realtimeClient); + } + + [TestMethod] + public void AsIRealtimeClient_NullModelId_ReturnsNonNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = client.AsIRealtimeClient(); + Assert.IsNotNull(realtimeClient); + } + + #endregion + + #region Client Constructor Tests + + [TestMethod] + public void RealtimeClient_NullClient_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new GoogleGenAIRealtimeClient((Client)null!, "model")); + } + + [TestMethod] + public void RealtimeClient_NullModelId_Succeeds() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client); + Assert.IsNotNull(realtimeClient); + } + + #endregion + + #region Client GetService Tests + + [TestMethod] + public void RealtimeClient_GetService_ReturnsMetadata() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "my-model"); + + var metadata = realtimeClient.GetService(typeof(ChatClientMetadata)) as ChatClientMetadata; + Assert.IsNotNull(metadata); + Assert.AreEqual("google-genai", metadata.ProviderName); + Assert.AreEqual("my-model", metadata.DefaultModelId); + } + + [TestMethod] + public void RealtimeClient_GetService_ReturnsSelf() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var self = realtimeClient.GetService(typeof(GoogleGenAIRealtimeClient)); + Assert.AreSame(realtimeClient, self); + } + + [TestMethod] + public void RealtimeClient_GetService_ReturnsInnerClient() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var inner = realtimeClient.GetService(typeof(Client)); + Assert.AreSame(client, inner); + } + + [TestMethod] + public void RealtimeClient_GetService_NullServiceType_ThrowsArgumentNullException() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + Assert.ThrowsException( + () => realtimeClient.GetService(null!)); + } + + [TestMethod] + public void RealtimeClient_GetService_WithKey_ReturnsNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var result = realtimeClient.GetService(typeof(ChatClientMetadata), serviceKey: "some-key"); + Assert.IsNull(result); + } + + [TestMethod] + public void RealtimeClient_GetService_UnknownType_ReturnsNull() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + var result = realtimeClient.GetService(typeof(string)); + Assert.IsNull(result); + } + + #endregion + + #region Client Dispose Tests + + [TestMethod] + public void RealtimeClient_Dispose_IsIdempotent() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + + // Should not throw + realtimeClient.Dispose(); + realtimeClient.Dispose(); + } + + #endregion + + #region Client CreateSession Tests + + [TestMethod] + public async Task RealtimeClient_CreateSession_NoModel_ThrowsInvalidOperationException() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client); + + await Assert.ThrowsExceptionAsync( + () => realtimeClient.CreateSessionAsync(new RealtimeSessionOptions())); + } + + [TestMethod] + public async Task RealtimeClient_CreateSession_Cancelled_ThrowsOperationCanceledException() + { + var client = new Client(apiKey: "fake-api-key"); + var realtimeClient = new GoogleGenAIRealtimeClient(client, "model"); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsExceptionAsync( + () => realtimeClient.CreateSessionAsync(new RealtimeSessionOptions(), cts.Token)); + } + + #endregion + + #region ToGoogleFunctionDeclaration Tests + + [TestMethod] + public void ToGoogleFunctionDeclaration_MapsNameAndDescription() + { + var function = AIFunctionFactory.Create(() => "result", "myFunc", "A test function"); + + var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); + + Assert.AreEqual("myFunc", declaration.Name); + Assert.AreEqual("A test function", declaration.Description); + } + + [TestMethod] + public void ToGoogleFunctionDeclaration_WithJsonSchema_MapsParameters() + { + var function = AIFunctionFactory.Create((string name, int age) => $"{name} is {age}", + "greet", "Greets a person"); + + var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); + + Assert.AreEqual("greet", declaration.Name); + Assert.AreEqual("Greets a person", declaration.Description); + + // The Parameters (Google Schema) should be set since the function has parameters + Assert.IsNotNull(declaration.Parameters); + } + + [TestMethod] + public void ToGoogleFunctionDeclaration_NoParameters_HasNoParameterSchema() + { + var function = AIFunctionFactory.Create(() => "hello", "noParams", "No parameters"); + + var declaration = GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(function); + + Assert.AreEqual("noParams", declaration.Name); + } + + #endregion +} + +[TestClass] +public class GoogleGenAIRealtimeSessionTest +{ + private Mock _mockWebSocket = null!; + private AsyncSession _asyncSession = null!; + private GoogleGenAIRealtimeSession _session = null!; + + [TestInitialize] + public void TestInitialize() + { + _mockWebSocket = new Mock(); + _mockWebSocket.Setup(ws => ws.State).Returns(WebSocketState.Open); + _asyncSession = new AsyncSession(_mockWebSocket.Object, new TestApiClient(false)); + _session = new GoogleGenAIRealtimeSession(_asyncSession, "test-model", null); + } + + [TestCleanup] + public async Task TestCleanup() + { + await _session.DisposeAsync(); + } + + #region Constructor Tests + + [TestMethod] + public void Session_NullAsyncSession_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new GoogleGenAIRealtimeSession(null!, "model", null)); + } + + [TestMethod] + public void Session_ValidConstruction_SetsOptions() + { + var options = new RealtimeSessionOptions { Model = "my-model" }; + var session = new GoogleGenAIRealtimeSession( + _asyncSession, "my-model", options); + + Assert.AreSame(options, session.Options); + } + + [TestMethod] + public void Session_NullOptions_OptionsIsNull() + { + Assert.IsNull(_session.Options); + } + + #endregion + + #region Session GetService Tests + + [TestMethod] + public void Session_GetService_ReturnsMetadata() + { + var metadata = _session.GetService(typeof(ChatClientMetadata)) as ChatClientMetadata; + Assert.IsNotNull(metadata); + Assert.AreEqual("google-genai", metadata.ProviderName); + Assert.AreEqual("test-model", metadata.DefaultModelId); + } + + [TestMethod] + public void Session_GetService_ReturnsSelf() + { + var self = _session.GetService(typeof(GoogleGenAIRealtimeSession)); + Assert.AreSame(_session, self); + } + + [TestMethod] + public void Session_GetService_ReturnsInnerAsyncSession() + { + var inner = _session.GetService(typeof(AsyncSession)); + Assert.AreSame(_asyncSession, inner); + } + + [TestMethod] + public void Session_GetService_NullServiceType_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => _session.GetService(null!)); + } + + [TestMethod] + public void Session_GetService_WithKey_ReturnsNull() + { + var result = _session.GetService(typeof(ChatClientMetadata), serviceKey: "key"); + Assert.IsNull(result); + } + + [TestMethod] + public void Session_GetService_UnknownType_ReturnsNull() + { + var result = _session.GetService(typeof(string)); + Assert.IsNull(result); + } + + #endregion + + #region Session DisposeAsync Tests + + [TestMethod] + public async Task Session_DisposeAsync_IsIdempotent() + { + var session = new GoogleGenAIRealtimeSession(_asyncSession, "model", null); + + // Should not throw on double dispose + await session.DisposeAsync(); + await session.DisposeAsync(); + } + + #endregion + + #region SendAsync Guard Tests + + [TestMethod] + public async Task SendAsync_NullMessage_ThrowsArgumentNullException() + { + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(null!)); + } + + [TestMethod] + public async Task SendAsync_AfterDispose_ThrowsObjectDisposedException() + { + await _session.DisposeAsync(); + + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(new CreateResponseRealtimeClientMessage())); + } + + [TestMethod] + public async Task SendAsync_CancelledToken_ThrowsOperationCanceledException() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(new CreateResponseRealtimeClientMessage(), cts.Token)); + } + + [TestMethod] + public async Task SendAsync_SessionUpdate_DoesNotUpdateOptions() + { + var initialOptions = new RealtimeSessionOptions { Model = "initial-model" }; + var session = new GoogleGenAIRealtimeSession(_asyncSession, "initial-model", initialOptions); + + var newOptions = new RealtimeSessionOptions { Model = "updated-model" }; + var msg = new SessionUpdateRealtimeClientMessage(newOptions); + + await session.SendAsync(msg); + + // Gemini does not support mid-session updates; options should remain unchanged. + Assert.AreSame(initialOptions, session.Options); + + await session.DisposeAsync(); + } + + [TestMethod] + public void SendAsync_SessionUpdate_NullOptions_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new SessionUpdateRealtimeClientMessage(null!)); + } + + [TestMethod] + public async Task SendAsync_UnknownMessageType_DoesNotThrow() + { + // A generic RealtimeClientMessage should just be ignored (default case) + var msg = new RealtimeClientMessage(); + await _session.SendAsync(msg); + } + + #endregion + + #region Audio Buffering Tests + + [TestMethod] + public void SendAsync_AudioAppend_NoContent_ThrowsArgumentNullException() + { + Assert.ThrowsException( + () => new InputAudioBufferAppendRealtimeClientMessage(null!)); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_NonAudioContent_DoesNotThrow() + { + var msg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent("data:text/plain;base64,SGVsbG8=", "text/plain")); + await _session.SendAsync(msg); + } + + [TestMethod] + public async Task SendAsync_AudioAppendThenCommit_SendsFrames() + { + // Prepare audio content + byte[] audioData = new byte[100]; + new Random(42).NextBytes(audioData); + string base64 = Convert.ToBase64String(audioData); + var dataUri = $"data:audio/pcm;base64,{base64}"; + + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); + + // Track SendAsync calls on the WebSocket + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + var copy = new byte[buffer.Count]; + Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); + sentMessages.Add(copy); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + + // Nothing sent yet (just buffered) + Assert.AreEqual(0, sentMessages.Count); + + // Commit triggers actual send + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Should have sent: ActivityStart + audio frame(s) + ActivityEnd + Assert.IsTrue(sentMessages.Count >= 3, + $"Expected at least 3 WebSocket messages (ActivityStart + audio + ActivityEnd), got {sentMessages.Count}"); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_EmptyBuffer_DoesNothing() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + var copy = new byte[buffer.Count]; + Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); + sentMessages.Add(copy); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + Assert.AreEqual(0, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_ExceedsBufferLimit_ThrowsInvalidOperationException() + { + // Create audio data close to the 10MB limit + byte[] largeAudio = new byte[10 * 1024 * 1024 + 1]; + string base64 = Convert.ToBase64String(largeAudio); + var dataUri = $"data:audio/pcm;base64,{base64}"; + + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); + + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(appendMsg)); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_LargeFrame_SplitsIntoMultipleFrames() + { + // Create audio data larger than 32KB frame limit (e.g. 70KB) + byte[] audioData = new byte[70_000]; + new Random(42).NextBytes(audioData); + string base64 = Convert.ToBase64String(audioData); + var dataUri = $"data:audio/pcm;base64,{base64}"; + + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage(new DataContent(dataUri, "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + var copy = new byte[buffer.Count]; + Buffer.BlockCopy(buffer.Array!, buffer.Offset, copy, 0, buffer.Count); + sentMessages.Add(copy); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // 70KB audio = 3 frames (32K + 32K + 6K) + ActivityStart + ActivityEnd = 5 messages + Assert.AreEqual(5, sentMessages.Count, + $"Expected 5 WebSocket messages (ActivityStart + 3 audio frames + ActivityEnd), got {sentMessages.Count}"); + } + + #endregion + + #region GetStreamingResponseAsync Tests + + [TestMethod] + public async Task GetStreamingResponseAsync_AfterDispose_ThrowsObjectDisposedException() + { + await _session.DisposeAsync(); + + await Assert.ThrowsExceptionAsync(async () => + { + await foreach (var _ in _session.GetStreamingResponseAsync()) + { + } + }); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_NullMessage_YieldsBreak() + { + // AsyncSession.ReceiveAsync returns null on close + var closeResult = new WebSocketReceiveResult( + 0, WebSocketMessageType.Close, true, + WebSocketCloseStatus.NormalClosure, "done"); + + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ReturnsAsync(closeResult); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_CallerCancelled_YieldsNoResults() + { + // A pre-cancelled token causes the while-loop condition to be false immediately, + // so the iterator completes without throwing. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync(cts.Token)) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_WebSocketException_YieldsBreak() + { + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ThrowsAsync(new WebSocketException("connection closed")); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + #endregion + + #region MapServerMessage Tests + + [TestMethod] + public async Task GetStreamingResponseAsync_SetupComplete_IsSkipped() + { + SetupReceiveOnce(new LiveServerMessage { SetupComplete = new LiveServerSetupComplete() }); + + var messages = await CollectMessages(); + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_TextResponse_EmitsResponseCreatedAndTextDelta() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Hello world" } } + } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + OutputTextDelta + Assert.AreEqual(2, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual("Hello world", ((OutputTextAudioRealtimeServerMessage)messages[1]).Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_AudioResponse_EmitsAudioDelta() + { + byte[] audioBytes = new byte[] { 1, 2, 3, 4 }; + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List + { + new Part + { + InlineData = new Blob + { + Data = audioBytes, + MimeType = "audio/pcm", + } + } + } + } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + OutputAudioDelta + Assert.AreEqual(2, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputAudioDelta, messages[1].Type); + var audioMsg = (OutputTextAudioRealtimeServerMessage)messages[1]; + Assert.AreEqual(Convert.ToBase64String(audioBytes), audioMsg.Audio); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_TurnComplete_EmitsResponseDone() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Hi" } } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent { TurnComplete = true } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + OutputTextDelta + ResponseDone + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_InputTranscription_EmitsTranscriptionCompleted() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + InputTranscription = new Transcription { Text = "what is the weather?" } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.InputAudioTranscriptionCompleted, messages[0].Type); + var transcription = (InputAudioTranscriptionRealtimeServerMessage)messages[0]; + Assert.AreEqual("what is the weather?", transcription.Transcription); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_OutputTranscription_EmitsTranscriptionDelta() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + OutputTranscription = new Transcription { Text = "The weather is sunny." } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.OutputAudioTranscriptionDelta, messages[0].Type); + var outputMsg = (OutputTextAudioRealtimeServerMessage)messages[0]; + Assert.AreEqual("The weather is sunny.", outputMsg.Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCall_EmitsResponseCreatedAndFunctionCall() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall + { + Id = "call-1", + Name = "get_weather", + Args = new Dictionary { ["city"] = "Seattle" } + } + } + } + }); + + var messages = await CollectMessages(); + + // Expect: ResponseCreated + ResponseOutputItemAdded + ResponseOutputItemDone + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[2].Type); + + var itemMsg = (ResponseOutputItemRealtimeServerMessage)messages[1]; + var functionCall = itemMsg.Item?.Contents?.OfType().First(); + Assert.IsNotNull(functionCall); + Assert.AreEqual("call-1", functionCall.CallId); + Assert.AreEqual("get_weather", functionCall.Name); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_GoAway_EmitsError() + { + SetupReceiveOnce(new LiveServerMessage + { + GoAway = new LiveServerGoAway() + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + var errorMsg = messages[0] as ErrorRealtimeServerMessage; + Assert.IsNotNull(errorMsg); + Assert.IsNotNull(errorMsg.Error); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_UsageMetadata_EmitsResponseDone_WhenResponseInProgress() + { + SetupReceiveSequence(new[] + { + // Start a response so _responseInProgress is true + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "response" } } + } + } + }, + // Usage metadata should emit ResponseDone + new LiveServerMessage + { + UsageMetadata = new UsageMetadata + { + PromptTokenCount = 10, + ResponseTokenCount = 20, + TotalTokenCount = 30, + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + OutputTextDelta + ResponseDone(usage) + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); + var responseDone = (ResponseCreatedRealtimeServerMessage)messages[2]; + Assert.IsNotNull(responseDone.Usage); + Assert.AreEqual(10, responseDone.Usage.InputTokenCount); + Assert.AreEqual(20, responseDone.Usage.OutputTokenCount); + Assert.AreEqual(30, responseDone.Usage.TotalTokenCount); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_UsageMetadata_NoResponseInProgress_IsSkipped() + { + // No prior model response — _responseInProgress is false + SetupReceiveOnce(new LiveServerMessage + { + UsageMetadata = new UsageMetadata + { + PromptTokenCount = 5, + ResponseTokenCount = 10, + TotalTokenCount = 15, + } + }); + + var messages = await CollectMessages(); + + // Usage metadata without a response in progress should be skipped + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_TurnComplete_PreventsDoubleResponseDone_FromUsage() + { + SetupReceiveSequence(new[] + { + // Model response + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Hi" } } + } + } + }, + // TurnComplete — emits ResponseDone and resets _responseInProgress + new LiveServerMessage + { + ServerContent = new LiveServerContent { TurnComplete = true } + }, + // Usage after TurnComplete — should NOT emit another ResponseDone + new LiveServerMessage + { + UsageMetadata = new UsageMetadata + { + PromptTokenCount = 10, + ResponseTokenCount = 20, + TotalTokenCount = 30, + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + OutputTextDelta + ResponseDone(TurnComplete) — no second ResponseDone + Assert.AreEqual(3, messages.Count); + var responseDoneMessages = messages.Where( + m => m.Type == RealtimeServerMessageType.ResponseDone).ToList(); + Assert.AreEqual(1, responseDoneMessages.Count, "Should only have one ResponseDone"); + } + + #endregion + + #region ConversationItemCreate Tests + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_TextContent_SendsClientContent() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello from user") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + // Verify the sent JSON contains our text + Assert.IsTrue(sentMessages[0].Contains("Hello from user"), + "Sent message should contain the text content"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_FunctionResult_SendsToolResponse() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-123", "sunny") }, + role: ChatRole.Tool)); + + await _session.SendAsync(msg); + + // Function results are buffered until CreateResponse + Assert.AreEqual(0, sentMessages.Count, + "Function results should be buffered, not sent immediately"); + + // Flush via CreateResponse + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(1, sentMessages.Count, + "Flushed tool response should be sent as a single WebSocket message"); + Assert.IsTrue(sentMessages[0].Contains("call-123"), + "Tool response should contain the call ID"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_FunctionResult_IncludesFunctionName() + { + // First, simulate receiving a tool call so the session caches the CallId → Name mapping. + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "call-42", Name = "get_weather" } + } + } + }); + + await CollectMessages(); + + // Now send the function result back + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-42", "sunny") }, + role: ChatRole.Tool)); + + await _session.SendAsync(msg); + + // Function results are buffered until CreateResponse + Assert.AreEqual(0, sentMessages.Count); + + // Flush via CreateResponse + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("get_weather"), + "Tool response should include the function name from the original tool call"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_EmptyContents_DoesNotSend() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List(), + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(0, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_AssistantRole_MappedToModel() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("I am the model") }, + role: ChatRole.Assistant)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + // Gemini uses "model" role, not "assistant" + Assert.IsTrue(sentMessages[0].Contains("model"), + "Assistant role should be mapped to 'model' for Gemini"); + } + + #endregion + + #region BuildLiveConnectConfig Tests + + [TestMethod] + public void BuildLiveConnectConfig_NullOptions_DefaultsToAudioModality() + { + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(null); + + Assert.IsNotNull(config.ResponseModalities); + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_SystemInstructions_MappedCorrectly() + { + var options = new RealtimeSessionOptions + { + Instructions = "You are a helpful assistant.", + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.SystemInstruction); + Assert.AreEqual(1, config.SystemInstruction.Parts.Count); + Assert.AreEqual("You are a helpful assistant.", config.SystemInstruction.Parts[0].Text); + Assert.AreEqual("user", config.SystemInstruction.Role); + } + + [TestMethod] + public void BuildLiveConnectConfig_EmptyInstructions_NoSystemInstruction() + { + var options = new RealtimeSessionOptions + { + Instructions = "", + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.SystemInstruction); + } + + [TestMethod] + public void BuildLiveConnectConfig_OutputModalities_AudioAndText() + { + var options = new RealtimeSessionOptions + { + OutputModalities = new List { "audio", "text" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(2, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); + Assert.AreEqual(Modality.Text, config.ResponseModalities[1]); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoOutputModalities_DefaultsToAudio() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Audio, config.ResponseModalities[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_UnknownModality_DefaultsToText() + { + var options = new RealtimeSessionOptions + { + OutputModalities = new List { "video" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Text, config.ResponseModalities[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_Voice_MappedToSpeechConfig() + { + var options = new RealtimeSessionOptions + { + Voice = "Puck", + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.SpeechConfig); + Assert.IsNotNull(config.SpeechConfig.VoiceConfig); + Assert.AreEqual("Puck", config.SpeechConfig.VoiceConfig.PrebuiltVoiceConfig.VoiceName); + } + + [TestMethod] + public void BuildLiveConnectConfig_NullVoice_NoSpeechConfig() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.SpeechConfig); + } + + [TestMethod] + public void BuildLiveConnectConfig_MaxOutputTokens_MappedToGenerationConfig() + { + var options = new RealtimeSessionOptions + { + MaxOutputTokens = 500, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.GenerationConfig); + Assert.AreEqual(500, config.GenerationConfig.MaxOutputTokens); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoMaxOutputTokens_NoGenerationConfig() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.GenerationConfig); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionOptions_EnablesBothDirections() + { + var options = new RealtimeSessionOptions + { + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNotNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoTranscriptionOptions_NoTranscription() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.InputAudioTranscription); + Assert.IsNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_NoVadOptions_DisablesVadByDefault() + { + var options = new RealtimeSessionOptions(); + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsNotNull(config.RealtimeInputConfig.AutomaticActivityDetection); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_VadEnabled_EnablesAutomaticActivityDetection() + { + var options = new RealtimeSessionOptions + { + VoiceActivityDetection = new VoiceActivityDetectionOptions + { + Enabled = true, + AllowInterruption = true, + }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsNotNull(config.RealtimeInputConfig.AutomaticActivityDetection); + Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + Assert.AreEqual( + ActivityHandling.StartOfActivityInterrupts, + config.RealtimeInputConfig.ActivityHandling); + } + + [TestMethod] + public void BuildLiveConnectConfig_VadEnabled_NoInterruption() + { + var options = new RealtimeSessionOptions + { + VoiceActivityDetection = new VoiceActivityDetectionOptions + { + Enabled = true, + AllowInterruption = false, + }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + Assert.AreEqual( + ActivityHandling.NoInterruption, + config.RealtimeInputConfig.ActivityHandling); + } + + [TestMethod] + public void BuildLiveConnectConfig_VadDisabled_DisablesAutomaticActivityDetection() + { + var options = new RealtimeSessionOptions + { + VoiceActivityDetection = new VoiceActivityDetectionOptions + { + Enabled = false, + }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.RealtimeInputConfig); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_Tools_MappedToFunctionDeclarations() + { + var fn = AIFunctionFactory.Create( + (string city) => $"Weather in {city}: sunny", + "get_weather", + "Gets the weather"); + + var options = new RealtimeSessionOptions + { + Tools = new List { fn }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.Tools); + Assert.AreEqual(1, config.Tools.Count); + Assert.AreEqual(1, config.Tools[0].FunctionDeclarations.Count); + Assert.AreEqual("get_weather", config.Tools[0].FunctionDeclarations[0].Name); + Assert.AreEqual("Gets the weather", config.Tools[0].FunctionDeclarations[0].Description); + } + + [TestMethod] + public void BuildLiveConnectConfig_EmptyTools_NoToolsConfig() + { + var options = new RealtimeSessionOptions + { + Tools = new List(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNull(config.Tools); + } + + [TestMethod] + public void BuildLiveConnectConfig_AllOptionsCombined_MapsEverything() + { + var fn = AIFunctionFactory.Create( + (string q) => "result", + "search", + "Searches things"); + + var options = new RealtimeSessionOptions + { + Instructions = "Be concise.", + OutputModalities = new List { "audio", "text" }, + Voice = "Aoede", + MaxOutputTokens = 1000, + Tools = new List { fn }, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual("Be concise.", config.SystemInstruction.Parts[0].Text); + Assert.AreEqual(2, config.ResponseModalities.Count); + Assert.AreEqual("Aoede", config.SpeechConfig.VoiceConfig.PrebuiltVoiceConfig.VoiceName); + Assert.AreEqual(1000, config.GenerationConfig.MaxOutputTokens); + Assert.AreEqual(1, config.Tools[0].FunctionDeclarations.Count); + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNotNull(config.OutputAudioTranscription); + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_OnlyInputTranscription() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_TextModalityOnly() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.AreEqual(1, config.ResponseModalities.Count); + Assert.AreEqual(Modality.Text, config.ResponseModalities[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_NoVoiceOrToolsOrInstructions() + { + var fn = AIFunctionFactory.Create( + (string q) => "result", + "search", + "Searches things"); + + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + Instructions = "Be concise.", + Voice = "Aoede", + MaxOutputTokens = 500, + Tools = new List { fn }, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + // Transcription-only: conversation-oriented options are ignored + Assert.IsNull(config.SystemInstruction); + Assert.IsNull(config.SpeechConfig); + Assert.IsNull(config.GenerationConfig); + Assert.IsNull(config.Tools); + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNull(config.OutputAudioTranscription); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_LanguageCodeMapped() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + TranscriptionOptions = new TranscriptionOptions { SpeechLanguage = "en-US" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.AreEqual(1, config.InputAudioTranscription.LanguageCodes.Count); + Assert.AreEqual("en-US", config.InputAudioTranscription.LanguageCodes[0]); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_NoLanguage_NoLanguageCodes() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + TranscriptionOptions = new TranscriptionOptions(), + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.IsNull(config.InputAudioTranscription.LanguageCodes); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_VadEnabled() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + VoiceActivityDetection = new VoiceActivityDetectionOptions { Enabled = true, AllowInterruption = false }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsFalse(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + Assert.AreEqual(ActivityHandling.NoInterruption, config.RealtimeInputConfig.ActivityHandling); + } + + [TestMethod] + public void BuildLiveConnectConfig_TranscriptionMode_DefaultVadDisabled() + { + var options = new RealtimeSessionOptions + { + SessionKind = RealtimeSessionKind.Transcription, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsTrue(config.RealtimeInputConfig.AutomaticActivityDetection.Disabled); + } + + [TestMethod] + public void BuildLiveConnectConfig_ConversationMode_LanguageCodeMapped() + { + var options = new RealtimeSessionOptions + { + TranscriptionOptions = new TranscriptionOptions { SpeechLanguage = "ja-JP" }, + }; + + var config = GoogleGenAIRealtimeClient.BuildLiveConnectConfig(options); + + Assert.IsNotNull(config.InputAudioTranscription); + Assert.AreEqual(1, config.InputAudioTranscription.LanguageCodes.Count); + Assert.AreEqual("ja-JP", config.InputAudioTranscription.LanguageCodes[0]); + // Output transcription is also enabled in conversation mode (no language codes) + Assert.IsNotNull(config.OutputAudioTranscription); + } + + #endregion + + #region ExtractDataBytes Tests + + [TestMethod] + public void ExtractDataBytes_ValidBase64DataUri_ExtractsBytes() + { + byte[] expected = new byte[] { 1, 2, 3, 4, 5 }; + string base64 = Convert.ToBase64String(expected); + var content = new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void ExtractDataBytes_InvalidBase64_FallsBackToData() + { + byte[] expected = new byte[] { 10, 20, 30 }; + var content = new DataContent(expected, "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void ExtractDataBytes_NullUri_UsesDirectData() + { + byte[] expected = new byte[] { 7, 8, 9 }; + var content = new DataContent(expected, "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + [TestMethod] + public void ExtractDataBytes_DataUriNoComma_FallsBackToData() + { + byte[] expected = new byte[] { 42, 43, 44 }; + // DataContent with raw byte data but no URI + var content = new DataContent(expected, "audio/pcm"); + + byte[] result = GoogleGenAIRealtimeSession.ExtractDataBytes(content); + + CollectionAssert.AreEqual(expected, result); + } + + #endregion + + #region CreateResponseRealtimeClientMessage Tests + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterConversationItem_SendsTurnComplete() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Send a conversation item with text + var itemMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + await _session.SendAsync(itemMsg); + + // Text input auto-triggers a response in Gemini's Live API, so the text + // send happened during CreateConversationItem, not CreateResponse. + Assert.AreEqual(1, sentMessages.Count, "Text should be sent via SendRealtimeInputAsync"); + Assert.IsTrue(sentMessages[0].Contains("Hello"), + "The sent message should contain the text content"); + + sentMessages.Clear(); + + // CreateResponse after text input does nothing — text auto-triggers. + var createResp = new CreateResponseRealtimeClientMessage(); + await _session.SendAsync(createResp); + + Assert.AreEqual(0, sentMessages.Count, + "CreateResponse should not send additional messages after text input (auto-triggers)"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterAudioCommit_DoesNotSendTurnComplete() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Send audio append + commit (sets _lastInputWasRealtime = true) + byte[] audioData = new byte[100]; + new Random(42).NextBytes(audioData); + string base64 = Convert.ToBase64String(audioData); + + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + int countBeforeCreateResponse = sentMessages.Count; + + // CreateResponse should NOT send anything (audio commit already sent ActivityEnd) + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(countBeforeCreateResponse, sentMessages.Count, + "CreateResponse should not send TurnComplete after realtime audio input"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterToolResponse_DoesNotSendTurnComplete() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Send a function result (simulates middleware returning tool response) + var toolResultMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-1", "sunny") }, + role: ChatRole.Tool)); + await _session.SendAsync(toolResultMsg); + + // CreateResponse should flush the tool response but NOT send TurnComplete + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + // Should have exactly 1 message: the batched tool response. No TurnComplete. + Assert.AreEqual(1, sentMessages.Count, + "Should send tool response but NOT TurnComplete after function result"); + Assert.IsTrue(sentMessages[0].Contains("call-1"), + "Tool response should contain the call ID"); + Assert.IsFalse(sentMessages[0].Contains("turnComplete"), + "Should not contain turnComplete after tool response"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_BatchesMultipleToolResponses() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Simulate middleware sending separate CreateConversationItem per function result + var toolResult1 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-1", "sunny") }, + role: ChatRole.Tool)); + var toolResult2 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-2", "72F") }, + role: ChatRole.Tool)); + + await _session.SendAsync(toolResult1); + await _session.SendAsync(toolResult2); + + // No WebSocket sends yet — results are buffered + Assert.AreEqual(0, sentMessages.Count, + "Function results should be buffered until CreateResponse"); + + // CreateResponse flushes all results in a single batched SendToolResponseAsync + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + Assert.AreEqual(1, sentMessages.Count, + "All function results should be sent in a single WebSocket message"); + Assert.IsTrue(sentMessages[0].Contains("call-1"), + "Batched tool response should contain first call ID"); + Assert.IsTrue(sentMessages[0].Contains("call-2"), + "Batched tool response should contain second call ID"); + Assert.IsFalse(sentMessages[0].Contains("turnComplete"), + "Should not contain turnComplete after batched tool responses"); + } + + [TestMethod] + public async Task SendAsync_CreateResponse_AfterToolResponse_NextTextSendResetsFlagCorrectly() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Complete a full tool call cycle: tool response → CreateResponse + var toolResultMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new FunctionResultContent("call-1", "sunny") }, + role: ChatRole.Tool)); + await _session.SendAsync(toolResultMsg); + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + sentMessages.Clear(); + + // Now send normal text → CreateResponse. Text auto-triggers; CreateResponse is a no-op. + var textMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello again") }, + role: ChatRole.User)); + await _session.SendAsync(textMsg); + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + // Should have 1 message: the text content sent via SendRealtimeInputAsync. + // CreateResponse does not send a turnComplete — text auto-triggers in Gemini. + Assert.AreEqual(1, sentMessages.Count, + "Normal text followed by CreateResponse should send only the text content"); + Assert.IsTrue(sentMessages[0].Contains("Hello again"), + "The sent message should contain the text content"); + } + + #endregion + + #region Additional MapServerMessage Tests + + [TestMethod] + public async Task GetStreamingResponseAsync_GenerationComplete_EmitsResponseDone() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Response" } } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent { GenerationComplete = true } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[2].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCallCancellation_EmitsRawContentOnly() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCallCancellation = new LiveServerToolCallCancellation + { + Ids = new List { "call-1", "call-2" }, + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.RawContentOnly, messages[0].Type); + Assert.IsNotNull(messages[0].RawRepresentation); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MultipleTextParts_EmitsMultipleDeltas() + { + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List + { + new Part { Text = "Part one" }, + new Part { Text = "Part two" }, + } + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + 2 OutputTextDelta + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[2].Type); + Assert.AreEqual("Part one", ((OutputTextAudioRealtimeServerMessage)messages[1]).Text); + Assert.AreEqual("Part two", ((OutputTextAudioRealtimeServerMessage)messages[2]).Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MixedAudioAndText_EmitsBothDeltas() + { + byte[] audioBytes = new byte[] { 10, 20, 30 }; + SetupReceiveOnce(new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List + { + new Part + { + InlineData = new Blob + { + Data = audioBytes, + MimeType = "audio/pcm", + } + }, + new Part { Text = "Hello there" }, + } + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + OutputAudioDelta + OutputTextDelta + Assert.AreEqual(3, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputAudioDelta, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.OutputTextDelta, messages[2].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MultipleModelTurns_OnlyOneResponseCreated() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "First chunk" } } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent + { + ModelTurn = new Content + { + Parts = new List { new Part { Text = "Second chunk" } } + } + } + }, + }); + + var messages = await CollectMessages(); + + // Only one ResponseCreated for the entire response cycle + var responseCreatedCount = messages.Count(m => m.Type == RealtimeServerMessageType.ResponseCreated); + Assert.AreEqual(1, responseCreatedCount, + "Should only emit one ResponseCreated across multiple ModelTurn messages in the same response"); + + // But both text deltas should appear + var textDeltas = messages.Where(m => m.Type == RealtimeServerMessageType.OutputTextDelta).ToList(); + Assert.AreEqual(2, textDeltas.Count); + Assert.AreEqual("First chunk", ((OutputTextAudioRealtimeServerMessage)textDeltas[0]).Text); + Assert.AreEqual("Second chunk", ((OutputTextAudioRealtimeServerMessage)textDeltas[1]).Text); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_MultipleToolCalls_EmitsAllItems() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "c1", Name = "fn1", Args = new Dictionary { ["a"] = "1" } }, + new FunctionCall { Id = "c2", Name = "fn2", Args = new Dictionary { ["b"] = "2" } }, + } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + (ResponseOutputItemAdded + ResponseOutputItemDone) × 2 + Assert.AreEqual(5, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseCreated, messages[0].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[1].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[2].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemAdded, messages[3].Type); + Assert.AreEqual(RealtimeServerMessageType.ResponseOutputItemDone, messages[4].Type); + + var fn1 = ((ResponseOutputItemRealtimeServerMessage)messages[1]).Item?.Contents?.OfType().First(); + var fn2 = ((ResponseOutputItemRealtimeServerMessage)messages[3]).Item?.Contents?.OfType().First(); + Assert.AreEqual("fn1", fn1?.Name); + Assert.AreEqual("fn2", fn2?.Name); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCallThenTurnComplete_EmitsResponseDone() + { + SetupReceiveSequence(new[] + { + new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "c1", Name = "fn1" } + } + } + }, + new LiveServerMessage + { + ServerContent = new LiveServerContent { TurnComplete = true } + } + }); + + var messages = await CollectMessages(); + + // ResponseCreated + ItemAdded + ItemDone + ResponseDone + Assert.AreEqual(4, messages.Count); + Assert.AreEqual(RealtimeServerMessageType.ResponseDone, messages[3].Type); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ToolCallWithNullArgs_EmitsEmptyArguments() + { + SetupReceiveOnce(new LiveServerMessage + { + ToolCall = new LiveServerToolCall + { + FunctionCalls = new List + { + new FunctionCall { Id = "c1", Name = "no_args_fn", Args = null } + } + } + }); + + var messages = await CollectMessages(); + + Assert.AreEqual(3, messages.Count); + var fc = ((ResponseOutputItemRealtimeServerMessage)messages[1]).Item?.Contents?.OfType().First(); + Assert.IsNotNull(fc); + Assert.AreEqual("no_args_fn", fc.Name); + Assert.IsNull(fc.Arguments); + } + + #endregion + + #region ConversationItemCreate Additional Tests + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_UserRole_MapsToUser() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("User text") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + // Gemini's SendRealtimeInputAsync sends text only (no role field). + // Verify the text content was sent. + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("User text"), + "User text should be sent via SendRealtimeInputAsync"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_NullRole_DefaultsToUser() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("No role text") })); + + await _session.SendAsync(msg); + + // Gemini's SendRealtimeInputAsync sends text only (no role field). + // Verify the text content was sent regardless of the absence of a role. + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("No role text"), + "Text should be sent via SendRealtimeInputAsync even without a role"); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_MultipleFunctionResults_SendsAll() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + // Multiple function results should all be sent in a single SendToolResponseAsync call + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List + { + new FunctionResultContent("call-1", "result-one"), + new FunctionResultContent("call-2", "result-two"), + }, + role: ChatRole.Tool)); + + await _session.SendAsync(msg); + + // Function results are buffered until CreateResponse + Assert.AreEqual(0, sentMessages.Count, + "Function results should be buffered, not sent immediately"); + + // Flush via CreateResponse + await _session.SendAsync(new CreateResponseRealtimeClientMessage()); + + // All function results are batched into one WebSocket message + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("call-1")); + Assert.IsTrue(sentMessages[0].Contains("result-one")); + Assert.IsTrue(sentMessages[0].Contains("call-2")); + Assert.IsTrue(sentMessages[0].Contains("result-two")); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_NullContents_DoesNotSend() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List())); + + await _session.SendAsync(msg); + + Assert.AreEqual(0, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_AudioContent_SendsInlineData() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("audio/pcm")); + } + + [TestMethod] + public async Task SendAsync_ConversationItemCreate_ImageContent_SendsInlineData() + { + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + byte[] imageData = new byte[] { 0x89, 0x50, 0x4E, 0x47 }; + string base64 = Convert.ToBase64String(imageData); + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new DataContent($"data:image/png;base64,{base64}", "image/png") }, + role: ChatRole.User)); + + await _session.SendAsync(msg); + + Assert.AreEqual(1, sentMessages.Count); + Assert.IsTrue(sentMessages[0].Contains("image/png")); + } + + #endregion + + #region Audio Frame Content Verification Tests + + [TestMethod] + public async Task SendAsync_AudioCommit_FrameContentVerification_PreservesData() + { + // Use a known pattern so we can verify exact bytes + byte[] audioData = new byte[64_000]; // Exactly 2 frames + for (int i = 0; i < audioData.Length; i++) + { + audioData[i] = (byte)(i % 256); + } + + string base64 = Convert.ToBase64String(audioData); + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 2 audio frames + ActivityEnd = 4 messages + Assert.AreEqual(4, sentMessages.Count); + + // Verify the first message is ActivityStart + Assert.IsTrue(sentMessages[0].Contains("activityStart"), + "First message should be ActivityStart"); + + // Verify audio frames contain base64-encoded data + Assert.IsTrue(sentMessages[1].Contains("audio/pcm"), + "Audio frame should contain mime type"); + Assert.IsTrue(sentMessages[2].Contains("audio/pcm"), + "Audio frame should contain mime type"); + + // Verify the last message is ActivityEnd + Assert.IsTrue(sentMessages[3].Contains("activityEnd"), + "Last message should be ActivityEnd"); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_ExactFrameSize_SingleFrame() + { + // Exactly 32000 bytes = exactly 1 frame (no splitting needed) + byte[] audioData = new byte[32_000]; + new Random(42).NextBytes(audioData); + + string base64 = Convert.ToBase64String(audioData); + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 1 frame + ActivityEnd = 3 + Assert.AreEqual(3, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_FrameBoundary_SplitsCorrectly() + { + // 32001 bytes = 32000 + 1 → 2 frames + byte[] audioData = new byte[32_001]; + new Random(42).NextBytes(audioData); + + string base64 = Convert.ToBase64String(audioData); + var appendMsg = new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm")); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(appendMsg); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 2 frames (32000 + 1) + ActivityEnd = 4 + Assert.AreEqual(4, sentMessages.Count); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_MultipleAppends_PreservesOrder() + { + byte[] chunk1 = new byte[100]; + byte[] chunk2 = new byte[200]; + new Random(42).NextBytes(chunk1); + new Random(43).NextBytes(chunk2); + + string base64_1 = Convert.ToBase64String(chunk1); + string base64_2 = Convert.ToBase64String(chunk2); + + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64_1}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64_2}", "audio/pcm"))); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // ActivityStart + 2 audio frames (both under 32KB) + ActivityEnd = 4 + Assert.AreEqual(4, sentMessages.Count); + } + + #endregion + + #region Audio MIME Type Tests + + [TestMethod] + public async Task SendAsync_AudioCommit_UsesDefaultMimeType_WhenNoInputAudioFormat() + { + // Default session (null options) should use audio/pcm + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Verify audio frame uses default audio/pcm mime type + Assert.IsTrue(sentMessages[1].Contains("audio/pcm"), "Audio frame should use default audio/pcm"); + } + + [TestMethod] + public async Task SendAsync_AudioCommit_UsesCustomMimeType_WhenInputAudioFormatSet() + { + // Create session with custom audio format + var customOptions = new RealtimeSessionOptions + { + InputAudioFormat = new RealtimeAudioFormat("audio/opus", 48000), + }; + var customSession = new GoogleGenAIRealtimeSession(_asyncSession, "test-model", customOptions); + + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + + var sentMessages = new List(); + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Callback, WebSocketMessageType, bool, CancellationToken>( + (buffer, _, _, _) => + { + sentMessages.Add(Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count)); + }) + .Returns(Task.CompletedTask); + + await customSession.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/opus;base64,{base64}", "audio/opus"))); + await customSession.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Verify audio frame uses the custom mime type + Assert.IsTrue(sentMessages[1].Contains("audio/opus"), + "Audio frame should use the configured audio/opus mime type"); + Assert.IsFalse(sentMessages[1].Contains("audio/pcm"), + "Audio frame should NOT contain the default audio/pcm"); + } + + #endregion + + #region Dispose Race Safety Tests + + [TestMethod] + public async Task SendAsync_ConcurrentDispose_DoesNotThrow() + { + // Simulate a race: SendAsync acquires the lock, then DisposeAsync runs. + // The Release() in finally should not throw even if the semaphore is disposed. + byte[] audioData = new byte[] { 1, 2, 3 }; + string base64 = Convert.ToBase64String(audioData); + + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Returns(Task.CompletedTask); + + _mockWebSocket + .Setup(ws => ws.CloseAsync( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Task.CompletedTask); + + // Append audio, then commit and dispose concurrently + await _session.SendAsync(new InputAudioBufferAppendRealtimeClientMessage( + new DataContent($"data:audio/pcm;base64,{base64}", "audio/pcm"))); + await _session.SendAsync(new InputAudioBufferCommitRealtimeClientMessage()); + + // Dispose should not throw even after sends completed + await _session.DisposeAsync(); + + // Double dispose should also be safe + await _session.DisposeAsync(); + } + + #endregion + + #region DisposeAsync Additional Tests + + [TestMethod] + public async Task DisposeAsync_ClosesUnderlyingWebSocket() + { + _mockWebSocket + .Setup(ws => ws.CloseAsync( + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Task.CompletedTask); + + await _session.DisposeAsync(); + + _mockWebSocket.Verify(ws => ws.CloseAsync( + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Once); + } + + #endregion + + #region Exception Handling Tests + + [TestMethod] + public async Task SendAsync_ObjectDisposedException_FromAsyncSession_Throws() + { + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .ThrowsAsync(new ObjectDisposedException("WebSocket")); + + // Send a conversation item that triggers a real send to the WebSocket + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // ObjectDisposedException is re-surfaced so callers know the session is gone + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(msg)); + } + + [TestMethod] + public async Task SendAsync_WebSocketException_FromAsyncSession_WhenNotDisposed_Throws() + { + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .ThrowsAsync(new WebSocketException("connection lost")); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // WebSocketException when session is NOT disposed indicates a real error + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(msg)); + } + + [TestMethod] + public async Task SendAsync_InternalOperationCancelled_Propagated() + { + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .ThrowsAsync(new OperationCanceledException("internal disposal")); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // Internal cancellation (not the caller's token) is now propagated + // so callers can observe unexpected teardown + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(msg)); + } + + [TestMethod] + public async Task SendAsync_CallerCancellation_Propagated() + { + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + var msg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("Hello") }, + role: ChatRole.User)); + + // Caller cancellation should propagate (ThrowIfCancellationRequested at top) + await Assert.ThrowsExceptionAsync( + () => _session.SendAsync(msg, cts.Token)); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_ObjectDisposedException_Swallowed() + { + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ThrowsAsync(new ObjectDisposedException("WebSocket")); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + [TestMethod] + public async Task GetStreamingResponseAsync_InternalOperationCancelled_Swallowed() + { + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .ThrowsAsync(new OperationCanceledException("internal cancellation")); + + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + + Assert.AreEqual(0, messages.Count); + } + + #endregion + + #region Helper Methods + + private void SetupReceiveOnce(LiveServerMessage message) + { + string json = JsonSerializer.Serialize(message, JsonConfig.JsonSerializerOptions); + byte[] bytes = Encoding.UTF8.GetBytes(json); + var callCount = 0; + + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .Callback, CancellationToken>((buffer, _) => + { + if (callCount == 0) + { + Buffer.BlockCopy(bytes, 0, buffer.Array!, buffer.Offset, bytes.Length); + } + }) + .Returns, CancellationToken>((_, _) => + { + callCount++; + if (callCount == 1) + { + return Task.FromResult( + new WebSocketReceiveResult(bytes.Length, WebSocketMessageType.Text, true)); + } + return Task.FromResult( + new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, + WebSocketCloseStatus.NormalClosure, "done")); + }); + } + + private void SetupReceiveSequence(LiveServerMessage[] messages) + { + var serialized = messages.Select(m => + { + string json = JsonSerializer.Serialize(m, JsonConfig.JsonSerializerOptions); + return Encoding.UTF8.GetBytes(json); + }).ToList(); + + var callCount = 0; + + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .Callback, CancellationToken>((buffer, _) => + { + if (callCount < serialized.Count) + { + Buffer.BlockCopy(serialized[callCount], 0, buffer.Array!, buffer.Offset, + serialized[callCount].Length); + } + }) + .Returns, CancellationToken>((_, _) => + { + var idx = callCount; + callCount++; + if (idx < serialized.Count) + { + return Task.FromResult( + new WebSocketReceiveResult(serialized[idx].Length, WebSocketMessageType.Text, true)); + } + return Task.FromResult( + new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, + WebSocketCloseStatus.NormalClosure, "done")); + }); + } + + private async Task> CollectMessages() + { + var messages = new List(); + await foreach (var msg in _session.GetStreamingResponseAsync()) + { + messages.Add(msg); + } + return messages; + } + + #endregion + + #region Concurrency Tests + + [TestMethod] + public async Task SendAsync_ConcurrentCalls_AreSerialized() + { + // Verify that concurrent SendAsync calls don't interleave WebSocket sends. + // We track the order of send completions to ensure no overlap. + var sendOrder = new List(); + var gate = new SemaphoreSlim(0); + + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Returns, WebSocketMessageType, bool, CancellationToken>( + async (buffer, _, _, ct) => + { + string json = Encoding.UTF8.GetString(buffer.Array!, buffer.Offset, buffer.Count); + // The first call blocks briefly; if sends aren't serialized, the second + // would start before the first completes. + if (json.Contains("turn-1")) + { + lock (sendOrder) { sendOrder.Add(1); } + gate.Release(); + await Task.Delay(50, ct); + lock (sendOrder) { sendOrder.Add(-1); } + } + else if (json.Contains("turn-2")) + { + await gate.WaitAsync(ct); + lock (sendOrder) { sendOrder.Add(2); } + } + }); + + var msg1 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("turn-1") }, role: ChatRole.User)); + var msg2 = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("turn-2") }, role: ChatRole.User)); + + // Launch both sends concurrently + var t1 = _session.SendAsync(msg1); + var t2 = _session.SendAsync(msg2); + await Task.WhenAll(t1, t2); + + // Because sends are serialized, turn-2 can only start after turn-1 completes. + // Expected order: [1, -1, 2] (start-1, end-1, start-2) + Assert.AreEqual(3, sendOrder.Count); + Assert.AreEqual(1, sendOrder[0], "turn-1 should start first"); + Assert.AreEqual(-1, sendOrder[1], "turn-1 should complete before turn-2 starts"); + Assert.AreEqual(2, sendOrder[2], "turn-2 should start after turn-1 completes"); + } + + [TestMethod] + public async Task SendAsync_AudioAppend_DoesNotBlockConcurrentSends() + { + // AudioAppend only buffers — it should NOT acquire the send lock, + // so it completes even while another send holds the lock. + var sendStarted = new TaskCompletionSource(); + var sendCanProceed = new TaskCompletionSource(); + + _mockWebSocket + .Setup(ws => ws.SendAsync( + It.IsAny>(), + WebSocketMessageType.Text, + true, + It.IsAny())) + .Returns, WebSocketMessageType, bool, CancellationToken>( + async (_, _, _, ct) => + { + sendStarted.SetResult(true); + await sendCanProceed.Task; + }); + + // Start a text send that will hold the lock + var textMsg = new CreateConversationItemRealtimeClientMessage(new RealtimeConversationItem( + new List { new TextContent("hello") }, role: ChatRole.User)); + var textSend = _session.SendAsync(textMsg); + + // Wait until the text send has acquired the lock and is in-flight + await sendStarted.Task; + + // AudioAppend should complete immediately (no lock contention) + var audioContent = new DataContent("data:audio/pcm;base64,AQID", "audio/pcm"); + var audioAppend = new InputAudioBufferAppendRealtimeClientMessage(audioContent); + var appendTask = _session.SendAsync(audioAppend); + + // Append should complete quickly even though the text send holds the lock + var completed = await Task.WhenAny(appendTask, Task.Delay(1000)); + Assert.AreSame(appendTask, completed, "AudioAppend should not block on the send lock"); + + // Release the text send + sendCanProceed.SetResult(true); + await textSend; + } + + #endregion + + #region Tool Payload Normalization Tests + + [TestMethod] + public void NormalizeToolPayload_ByteArray_EncodesAsBase64() + { + byte[] payload = [1, 2, 3, 4]; + var result = GoogleGenAIRealtimeSession.NormalizeToolPayload(payload); + Assert.AreEqual(Convert.ToBase64String(payload), result); + } + + [TestMethod] + public void NormalizeToolPayload_JsonElement_DecomposesCorrectly() + { + var json = JsonSerializer.SerializeToElement(new { name = "test", count = 42 }); + var result = GoogleGenAIRealtimeSession.NormalizeToolPayload(json) as Dictionary; + Assert.IsNotNull(result); + Assert.AreEqual("test", result["name"]); + // JSON numbers may deserialize as int64 or double depending on the runtime + Assert.IsTrue( + result["count"] is 42L or 42.0, + $"Expected 42 as long or double, got {result["count"]} ({result["count"]?.GetType()})"); + } + + [TestMethod] + public void NormalizeToolPayload_TooDeep_Throws() + { + var payload = new Dictionary(); + IDictionary current = payload; + for (int i = 0; i < 80; i++) + { + var next = new Dictionary(); + current[$"level{i}"] = next; + current = next; + } + + Assert.ThrowsException( + () => GoogleGenAIRealtimeSession.NormalizeToolPayload(payload)); + } + + [TestMethod] + public void NormalizeToolArguments_NormalizesNestedJsonElements() + { + var jsonElement = JsonSerializer.SerializeToElement("hello"); + var args = new Dictionary { ["key"] = jsonElement }; + var result = GoogleGenAIRealtimeSession.NormalizeToolArguments(args); + Assert.AreEqual("hello", result["key"]); + } + + #endregion + + #region Concurrent Enumeration Guard Tests + + [TestMethod] + public async Task GetStreamingResponseAsync_ConcurrentEnumeration_Throws() + { + // Set up WebSocket to block on receive so the first enumeration stays active + var receiveGate = new TaskCompletionSource(); + _mockWebSocket + .Setup(ws => ws.ReceiveAsync( + It.IsAny>(), + It.IsAny())) + .Returns(receiveGate.Task); + + // Start first enumeration + using var cts = new CancellationTokenSource(); + var enumerator = _session.GetStreamingResponseAsync(cts.Token).GetAsyncEnumerator(cts.Token); + var firstMoveNext = enumerator.MoveNextAsync(); + + // Attempt second concurrent enumeration — should throw + await Assert.ThrowsExceptionAsync(async () => + { + await foreach (var _ in _session.GetStreamingResponseAsync()) + { + } + }); + + // Clean up + cts.Cancel(); + receiveGate.SetCanceled(); + try { await firstMoveNext; } catch { } + await enumerator.DisposeAsync(); + } + + #endregion +} + +#pragma warning restore MEAI001 diff --git a/Google.GenAI/GoogleGenAIRealtimeClient.cs b/Google.GenAI/GoogleGenAIRealtimeClient.cs index dad6263b..c830d44c 100644 --- a/Google.GenAI/GoogleGenAIRealtimeClient.cs +++ b/Google.GenAI/GoogleGenAIRealtimeClient.cs @@ -1,297 +1,305 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Google.GenAI; -using Google.GenAI.Types; - -namespace Microsoft.Extensions.AI; - -/// -/// Provides an implementation for Google GenAI's Live API. -/// -#if NET8_0_OR_GREATER -[System.Diagnostics.CodeAnalysis.Experimental("MEAI001")] -#endif -public sealed class GoogleGenAIRealtimeClient : IRealtimeClient -{ - private readonly Client _client; - private readonly string? _defaultModelId; - private ChatClientMetadata? _metadata; - - /// Initializes a new instance wrapping an existing . - /// The Google GenAI client. - /// The default model to use for realtime sessions (e.g. "gemini-2.5-flash-native-audio-preview-12-2025"). - /// is . - public GoogleGenAIRealtimeClient(Client client, string? defaultModelId = null) - { - _client = client ?? throw new ArgumentNullException(nameof(client)); - _defaultModelId = defaultModelId; - } - - /// Initializes a new instance using an API key. - /// The Google GenAI API key. - /// The default model to use for realtime sessions. - /// is or empty. - public GoogleGenAIRealtimeClient(string apiKey, string? defaultModelId = null) - { - if (string.IsNullOrEmpty(apiKey)) - { - throw new ArgumentNullException(nameof(apiKey)); - } - - _client = new Client(apiKey: apiKey); - _defaultModelId = defaultModelId; - } - - /// - public async Task CreateSessionAsync( - RealtimeSessionOptions? options = null, - CancellationToken cancellationToken = default) - { - string model = options?.Model ?? _defaultModelId - ?? throw new InvalidOperationException( - "No model specified. Provide a model via RealtimeSessionOptions.Model or the defaultModelId constructor parameter."); - - var config = BuildLiveConnectConfig(options); - - var asyncSession = await _client.Live.ConnectAsync(model, config, cancellationToken).ConfigureAwait(false); - - // The Google SDK's ConnectAsync sends the setup message but does NOT wait - // for the server's SetupComplete acknowledgment. We must drain it here so - // the session is fully ready (tools configured, modalities set) before the - // caller starts sending audio or text. - var setupResponse = await asyncSession.ReceiveAsync(cancellationToken).ConfigureAwait(false); - if (setupResponse?.SetupComplete is null) - { - throw new InvalidOperationException( - "Expected SetupComplete from Gemini server after connection, but received an unexpected message."); - } - - return new GoogleGenAIRealtimeSession(asyncSession, model, options); - } - - /// - public object? GetService(System.Type serviceType, object? serviceKey = null) - { - if (serviceType is null) - { - throw new ArgumentNullException(nameof(serviceType)); - } - - if (serviceKey is not null) - { - return null; - } - - if (serviceType == typeof(ChatClientMetadata)) - { - return _metadata ??= new ChatClientMetadata("google-genai", defaultModelId: _defaultModelId); - } - - if (serviceType.IsInstanceOfType(this)) - { - return this; - } - - if (serviceType.IsInstanceOfType(_client)) - { - return _client; - } - - return null; - } - - /// - public void Dispose() - { - // Client lifecycle is not owned by this wrapper. - } - - /// Converts MEAI session options to a Google GenAI . - internal static LiveConnectConfig BuildLiveConnectConfig(RealtimeSessionOptions? options) - { - var config = new LiveConnectConfig(); - - if (options is null) - { - config.ResponseModalities = new List { Modality.Audio }; - config.RealtimeInputConfig = new RealtimeInputConfig - { - AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true } - }; - return config; - } - - // Transcription-only sessions use a minimal configuration with - // only input audio transcription enabled (no audio output, no tools). - if (options.SessionKind == RealtimeSessionKind.Transcription) - { - return BuildTranscriptionConnectConfig(options); - } - - // System instructions - if (!string.IsNullOrEmpty(options.Instructions)) - { - config.SystemInstruction = new Content - { - Parts = new List { new Part { Text = options.Instructions } }, - Role = "user" - }; - } - - // Output modalities - if (options.OutputModalities is { Count: > 0 }) - { - config.ResponseModalities = new List(); - foreach (var modality in options.OutputModalities) - { - config.ResponseModalities.Add(modality.ToLowerInvariant() switch - { - "audio" => Modality.Audio, - "text" => Modality.Text, - _ => Modality.Text, - }); - } - } - else - { - config.ResponseModalities = new List { Modality.Audio }; - } - - // Voice / speech config - if (!string.IsNullOrEmpty(options.Voice)) - { - config.SpeechConfig = new SpeechConfig - { - VoiceConfig = new VoiceConfig - { - PrebuiltVoiceConfig = new PrebuiltVoiceConfig - { - VoiceName = options.Voice, - } - } - }; - } - - // Generation config - if (options.MaxOutputTokens.HasValue) - { - config.GenerationConfig ??= new GenerationConfig(); - config.GenerationConfig.MaxOutputTokens = options.MaxOutputTokens.Value; - } - - // Tools (AIFunction → Google FunctionDeclaration) - if (options.Tools is { Count: > 0 }) - { - var functionDeclarations = new List(); - foreach (var tool in options.Tools) - { - if (tool is AIFunction aiFunction) - { - functionDeclarations.Add(GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(aiFunction)); - } - } - - if (functionDeclarations.Count > 0) - { - config.Tools = new List - { - new Tool { FunctionDeclarations = functionDeclarations } - }; - } - } - - // Transcription (both directions for conversation sessions) - if (options.TranscriptionOptions is not null) - { - var inputTranscriptionConfig = new AudioTranscriptionConfig(); - if (!string.IsNullOrEmpty(options.TranscriptionOptions.SpeechLanguage)) - { - inputTranscriptionConfig.LanguageCodes = new List { options.TranscriptionOptions.SpeechLanguage }; - } - - config.InputAudioTranscription = inputTranscriptionConfig; - config.OutputAudioTranscription = new AudioTranscriptionConfig(); - } - - // Configure VAD / activity detection based on MEAI options. - config.RealtimeInputConfig = new RealtimeInputConfig(); - - if (options.VoiceActivityDetection is { Enabled: true } vad) - { - // Automatic VAD enabled — the server detects speech boundaries. - config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = false }; - config.RealtimeInputConfig.ActivityHandling = vad.AllowInterruption - ? ActivityHandling.StartOfActivityInterrupts - : ActivityHandling.NoInterruption; - } - else if (options.VoiceActivityDetection is { Enabled: false }) - { - // VAD explicitly disabled — the client controls activity boundaries - // via explicit ActivityStart/ActivityEnd signals. - config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; - } - else - { - // No VAD options specified — disable automatic VAD by default when using - // the MEAI audio buffering pattern (AudioBufferAppend → AudioBufferCommit). - // The client controls activity boundaries via explicit ActivityEnd signals. - config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; - } - - return config; - } - - /// - /// Builds a minimal for transcription-only sessions. - /// Only input audio transcription is enabled; no audio output, tools, or voice config. - /// - private static LiveConnectConfig BuildTranscriptionConnectConfig(RealtimeSessionOptions options) - { - var config = new LiveConnectConfig - { - // No audio output for transcription-only sessions - ResponseModalities = new List { Modality.Text }, - }; - - // Enable input transcription with optional language hint - var transcriptionConfig = new AudioTranscriptionConfig(); - if (!string.IsNullOrEmpty(options.TranscriptionOptions?.SpeechLanguage)) - { - transcriptionConfig.LanguageCodes = new List { options.TranscriptionOptions.SpeechLanguage }; - } - - config.InputAudioTranscription = transcriptionConfig; - - // VAD configuration still applies for speech boundary detection - config.RealtimeInputConfig = new RealtimeInputConfig(); - - if (options.VoiceActivityDetection is { Enabled: true } vad) - { - config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = false }; - config.RealtimeInputConfig.ActivityHandling = vad.AllowInterruption - ? ActivityHandling.StartOfActivityInterrupts - : ActivityHandling.NoInterruption; - } - else - { - config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; - } - - return config; - } -} - +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Google.GenAI; +using Google.GenAI.Types; + +namespace Microsoft.Extensions.AI; + +/// +/// Provides an implementation for Google GenAI's Live API. +/// +#if NET8_0_OR_GREATER +[System.Diagnostics.CodeAnalysis.Experimental("MEAI001")] +#endif +public sealed class GoogleGenAIRealtimeClient : IRealtimeClient +{ + private readonly Client _client; + private readonly string? _defaultModelId; + private ChatClientMetadata? _metadata; + + /// Initializes a new instance wrapping an existing . + /// The Google GenAI client. + /// The default model to use for realtime sessions (e.g. "gemini-2.5-flash-native-audio-preview-12-2025"). + /// is . + public GoogleGenAIRealtimeClient(Client client, string? defaultModelId = null) + { + _client = client ?? throw new ArgumentNullException(nameof(client)); + _defaultModelId = defaultModelId; + } + + /// Initializes a new instance using an API key. + /// The Google GenAI API key. + /// The default model to use for realtime sessions. + /// is or empty. + public GoogleGenAIRealtimeClient(string apiKey, string? defaultModelId = null) + { + if (string.IsNullOrEmpty(apiKey)) + { + throw new ArgumentNullException(nameof(apiKey)); + } + + _client = new Client(apiKey: apiKey); + _defaultModelId = defaultModelId; + } + + /// + public async Task CreateSessionAsync( + RealtimeSessionOptions? options = null, + CancellationToken cancellationToken = default) + { + string model = options?.Model ?? _defaultModelId + ?? throw new InvalidOperationException( + "No model specified. Provide a model via RealtimeSessionOptions.Model or the defaultModelId constructor parameter."); + + var config = BuildLiveConnectConfig(options); + + var asyncSession = await _client.Live.ConnectAsync(model, config, cancellationToken).ConfigureAwait(false); + + try + { + // The Google SDK's ConnectAsync sends the setup message but does NOT wait + // for the server's SetupComplete acknowledgment. We must drain it here so + // the session is fully ready (tools configured, modalities set) before the + // caller starts sending audio or text. + var setupResponse = await asyncSession.ReceiveAsync(cancellationToken).ConfigureAwait(false); + if (setupResponse?.SetupComplete is null) + { + throw new InvalidOperationException( + "Expected SetupComplete from Gemini server after connection, but received an unexpected message."); + } + + return new GoogleGenAIRealtimeSession(asyncSession, model, options); + } + catch + { + await asyncSession.DisposeAsync().ConfigureAwait(false); + throw; + } + } + + /// + public object? GetService(System.Type serviceType, object? serviceKey = null) + { + if (serviceType is null) + { + throw new ArgumentNullException(nameof(serviceType)); + } + + if (serviceKey is not null) + { + return null; + } + + if (serviceType == typeof(ChatClientMetadata)) + { + return _metadata ??= new ChatClientMetadata("google-genai", defaultModelId: _defaultModelId); + } + + if (serviceType.IsInstanceOfType(this)) + { + return this; + } + + if (serviceType.IsInstanceOfType(_client)) + { + return _client; + } + + return null; + } + + /// + public void Dispose() + { + // Client lifecycle is not owned by this wrapper. + } + + /// Converts MEAI session options to a Google GenAI . + internal static LiveConnectConfig BuildLiveConnectConfig(RealtimeSessionOptions? options) + { + var config = new LiveConnectConfig(); + + if (options is null) + { + config.ResponseModalities = new List { Modality.Audio }; + config.RealtimeInputConfig = new RealtimeInputConfig + { + AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true } + }; + return config; + } + + // Transcription-only sessions use a minimal configuration with + // only input audio transcription enabled (no audio output, no tools). + if (options.SessionKind == RealtimeSessionKind.Transcription) + { + return BuildTranscriptionConnectConfig(options); + } + + // System instructions + if (!string.IsNullOrEmpty(options.Instructions)) + { + config.SystemInstruction = new Content + { + Parts = new List { new Part { Text = options.Instructions } }, + Role = "user" + }; + } + + // Output modalities + if (options.OutputModalities is { Count: > 0 }) + { + config.ResponseModalities = new List(); + foreach (var modality in options.OutputModalities) + { + config.ResponseModalities.Add(modality.ToLowerInvariant() switch + { + "audio" => Modality.Audio, + "text" => Modality.Text, + _ => Modality.Text, + }); + } + } + else + { + config.ResponseModalities = new List { Modality.Audio }; + } + + // Voice / speech config + if (!string.IsNullOrEmpty(options.Voice)) + { + config.SpeechConfig = new SpeechConfig + { + VoiceConfig = new VoiceConfig + { + PrebuiltVoiceConfig = new PrebuiltVoiceConfig + { + VoiceName = options.Voice, + } + } + }; + } + + // Generation config + if (options.MaxOutputTokens.HasValue) + { + config.GenerationConfig ??= new GenerationConfig(); + config.GenerationConfig.MaxOutputTokens = options.MaxOutputTokens.Value; + } + + // Tools (AIFunction → Google FunctionDeclaration) + if (options.Tools is { Count: > 0 }) + { + var functionDeclarations = new List(); + foreach (var tool in options.Tools) + { + if (tool is AIFunction aiFunction) + { + functionDeclarations.Add(GoogleGenAIRealtimeSession.ToGoogleFunctionDeclaration(aiFunction)); + } + } + + if (functionDeclarations.Count > 0) + { + config.Tools = new List + { + new Tool { FunctionDeclarations = functionDeclarations } + }; + } + } + + // Transcription (both directions for conversation sessions) + if (options.TranscriptionOptions is not null) + { + var inputTranscriptionConfig = new AudioTranscriptionConfig(); + if (!string.IsNullOrEmpty(options.TranscriptionOptions.SpeechLanguage)) + { + inputTranscriptionConfig.LanguageCodes = new List { options.TranscriptionOptions.SpeechLanguage }; + } + + config.InputAudioTranscription = inputTranscriptionConfig; + config.OutputAudioTranscription = new AudioTranscriptionConfig(); + } + + // Configure VAD / activity detection based on MEAI options. + config.RealtimeInputConfig = new RealtimeInputConfig(); + + if (options.VoiceActivityDetection is { Enabled: true } vad) + { + // Automatic VAD enabled — the server detects speech boundaries. + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = false }; + config.RealtimeInputConfig.ActivityHandling = vad.AllowInterruption + ? ActivityHandling.StartOfActivityInterrupts + : ActivityHandling.NoInterruption; + } + else if (options.VoiceActivityDetection is { Enabled: false }) + { + // VAD explicitly disabled — the client controls activity boundaries + // via explicit ActivityStart/ActivityEnd signals. + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; + } + else + { + // No VAD options specified — disable automatic VAD by default when using + // the MEAI audio buffering pattern (AudioBufferAppend → AudioBufferCommit). + // The client controls activity boundaries via explicit ActivityEnd signals. + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; + } + + return config; + } + + /// + /// Builds a minimal for transcription-only sessions. + /// Only input audio transcription is enabled; no audio output, tools, or voice config. + /// + private static LiveConnectConfig BuildTranscriptionConnectConfig(RealtimeSessionOptions options) + { + var config = new LiveConnectConfig + { + // No audio output for transcription-only sessions + ResponseModalities = new List { Modality.Text }, + }; + + // Enable input transcription with optional language hint + var transcriptionConfig = new AudioTranscriptionConfig(); + if (!string.IsNullOrEmpty(options.TranscriptionOptions?.SpeechLanguage)) + { + transcriptionConfig.LanguageCodes = new List { options.TranscriptionOptions.SpeechLanguage }; + } + + config.InputAudioTranscription = transcriptionConfig; + + // VAD configuration still applies for speech boundary detection + config.RealtimeInputConfig = new RealtimeInputConfig(); + + if (options.VoiceActivityDetection is { Enabled: true } vad) + { + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = false }; + config.RealtimeInputConfig.ActivityHandling = vad.AllowInterruption + ? ActivityHandling.StartOfActivityInterrupts + : ActivityHandling.NoInterruption; + } + else + { + config.RealtimeInputConfig.AutomaticActivityDetection = new AutomaticActivityDetection { Disabled = true }; + } + + return config; + } +} + diff --git a/Google.GenAI/GoogleGenAIRealtimeSession.cs b/Google.GenAI/GoogleGenAIRealtimeSession.cs index 96b32d22..9d076bf5 100644 --- a/Google.GenAI/GoogleGenAIRealtimeSession.cs +++ b/Google.GenAI/GoogleGenAIRealtimeSession.cs @@ -1,839 +1,1037 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Collections.Concurrent; -using System.Linq; -using System.Net.WebSockets; -using System.Runtime.CompilerServices; -using System.Text.Json; - -using Google.GenAI; -using Google.GenAI.Types; - -namespace Microsoft.Extensions.AI; - -/// -/// Provides an implementation for Google GenAI's Live API, -/// wrapping an WebSocket connection. -/// -#if NET8_0_OR_GREATER -[System.Diagnostics.CodeAnalysis.Experimental("MEAI001")] -#endif -public sealed class GoogleGenAIRealtimeSession : IRealtimeClientSession -{ - private readonly AsyncSession _asyncSession; - private readonly ChatClientMetadata _metadata; - private int _disposed; - - // Buffer for audio chunks between Append and Commit. - // Protected by _audioBufferLock. Capped at MaxAudioBufferBytes to prevent unbounded growth. - private readonly List _audioBuffer = new(); - private readonly object _audioBufferLock = new(); - private int _audioBufferSize; - - /// Maximum buffered audio size (10 MB). Exceeding this throws . - private const int MaxAudioBufferBytes = 10 * 1024 * 1024; - - // Track whether a response is in progress to emit ResponseCreated only once per response. - // Accessed only from GetStreamingResponseAsync's single enumeration; callers must not - // enumerate concurrently. - private bool _responseInProgress; - - // Serializes all WebSocket send operations. Required because: - // 1. WebSocket.SendAsync is NOT thread-safe for concurrent calls. - // 2. FunctionInvokingRealtimeSession middleware can call SendAsync (to return - // function results) concurrently with the caller's own SendAsync (e.g., audio). - // 3. HandleAudioCommitAsync sends a multi-message sequence (ActivityStart → - // audio frames → ActivityEnd) that must be atomic. - private readonly SemaphoreSlim _sendLock = new(1, 1); - - // Track whether a tool response was just sent. After SendToolResponseAsync, the server - // automatically continues generating — sending TurnComplete would be unexpected. - private bool _lastSendWasToolResponse; - - // Track whether the last content sent was media (image/video/audio via CreateConversationItem) - // that does not auto-trigger a model response. Unlike text, media input requires an explicit - // ActivityEnd signal in CreateResponse to prompt the model to respond. - private bool _pendingMediaNeedsTrigger; - - // Maps function call IDs to function names. Populated when ToolCall messages arrive, - // consumed when sending FunctionResponse back to the server. - private readonly ConcurrentDictionary _callIdToFunctionName = new(); - - // Accumulates function results across multiple CreateConversationItem sends so they can - // be batched into a single SendToolResponseAsync call. The MEAI middleware sends one - // CreateConversationItem per function result followed by a single CreateResponse. - // Gemini expects all function results in one SendToolResponseAsync call, so we buffer - // them here and flush on CreateResponse. - private readonly List _pendingToolResponses = new(); - - // When true, automatic VAD is enabled and the server handles speech boundary detection. - // ActivityStart/ActivityEnd framing is skipped during audio commit. - private readonly bool _vadEnabled; - - // The MIME type for audio frames sent to the server, derived from InputAudioFormat. - private readonly string _inputAudioMimeType; - - /// - public RealtimeSessionOptions? Options { get; private set; } - - /// Initializes a new instance wrapping a connected . - /// The connected for WebSocket communication. - /// The model name for metadata. - /// Optional initial session options. - public GoogleGenAIRealtimeSession( - AsyncSession asyncSession, - string model, - RealtimeSessionOptions? initialOptions) - { - _asyncSession = asyncSession ?? throw new ArgumentNullException(nameof(asyncSession)); - _metadata = new ChatClientMetadata("google-genai", defaultModelId: model); - Options = initialOptions; - _vadEnabled = initialOptions?.VoiceActivityDetection is { Enabled: true }; - _inputAudioMimeType = initialOptions?.InputAudioFormat?.MediaType ?? "audio/pcm"; - } - - /// - public async Task SendAsync( - RealtimeClientMessage message, - CancellationToken cancellationToken = default) - { - if (message is null) - { - throw new ArgumentNullException(nameof(message)); - } - - if (Volatile.Read(ref _disposed) != 0) - { - throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); - } - - cancellationToken.ThrowIfCancellationRequested(); - - // AudioAppend only buffers data in memory — no WebSocket I/O, no lock needed. - if (message is InputAudioBufferAppendRealtimeClientMessage audioAppend) - { - HandleAudioAppend(audioAppend); - return; - } - - // All other message types perform WebSocket I/O and must be serialized. - // WaitAsync may throw ObjectDisposedException if DisposeAsync races between the - // _disposed check above and this call — treat it the same as a post-dispose send. - try - { - await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); - } - catch (ObjectDisposedException) - { - throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); - } - - try - { - switch (message) - { - case InputAudioBufferCommitRealtimeClientMessage: - await HandleAudioCommitAsync(cancellationToken).ConfigureAwait(false); - break; - - case CreateConversationItemRealtimeClientMessage itemCreate: - await HandleConversationItemCreateAsync(itemCreate, cancellationToken).ConfigureAwait(false); - break; - - case SessionUpdateRealtimeClientMessage: - // Gemini's Live API does not support mid-session reconfiguration. - break; - - case CreateResponseRealtimeClientMessage: - if (_pendingToolResponses.Count > 0) - { - // Flush all buffered function results in a single SendToolResponseAsync call. - // The MEAI middleware sends one CreateConversationItem per function result, - // but Gemini expects all results in one call. - await _asyncSession.SendToolResponseAsync( - new LiveSendToolResponseParameters - { - FunctionResponses = new List(_pendingToolResponses) - }, - cancellationToken).ConfigureAwait(false); - _pendingToolResponses.Clear(); - _lastSendWasToolResponse = true; - } - - if (_lastSendWasToolResponse) - { - // After a tool response, Gemini automatically continues generating. - // Do not send ActivityEnd — it would be unexpected. - _lastSendWasToolResponse = false; - } - else if (_pendingMediaNeedsTrigger) - { - // Media inputs (image, video) via SendRealtimeInputAsync are added to - // the model's context but don't auto-trigger a response. Gemini's Live API - // has no equivalent to OpenAI's CreateResponse command — the only way to - // trigger a response is via text input. Send a minimal whitespace text to - // prompt the model to respond about the media in context without biasing - // the response content. - _pendingMediaNeedsTrigger = false; - await _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - Text = " ", - }, - cancellationToken).ConfigureAwait(false); - } - // For text input: auto-triggers, no signal needed. - // For audio commit: ActivityEnd/AudioStreamEnd already sent in HandleAudioCommitAsync. - break; - - default: - break; - } - } - catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) - { - // The caller explicitly cancelled via their token — propagate so they - // can observe the cancellation they requested. - throw; - } - catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException or WebSocketException) - { - // These exceptions are expected during session teardown and are swallowed: - // - OperationCanceledException: internal cancellation from disposal (not the caller's token). - // - ObjectDisposedException: DisposeAsync was called on another thread while an - // operation was in-flight on the underlying WebSocket. - // - WebSocketException: the connection was closed (server disconnect or local close). - } - finally - { - try - { - _sendLock.Release(); - } - catch (ObjectDisposedException) - { - // DisposeAsync was called concurrently and disposed the semaphore. - } - } - } - - /// - public async IAsyncEnumerable GetStreamingResponseAsync( - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - if (Volatile.Read(ref _disposed) != 0) - { - throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); - } - - while (!cancellationToken.IsCancellationRequested) - { - LiveServerMessage? serverMessage; - try - { - serverMessage = await _asyncSession.ReceiveAsync(cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) - { - // The caller explicitly cancelled via their token — propagate so they - // can observe the cancellation they requested. - throw; - } - catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException or WebSocketException) - { - // These exceptions are expected during session teardown and are swallowed: - // - OperationCanceledException: internal cancellation from disposal (not the caller's token). - // - ObjectDisposedException: DisposeAsync was called on another thread while an - // operation was in-flight on the underlying WebSocket. - // - WebSocketException: the connection was closed (server disconnect or local close). - yield break; - } - - if (serverMessage is null) - { - yield break; - } - - // Map Google Live server messages to MEAI server message types - foreach (var mapped in MapServerMessage(serverMessage)) - { - yield return mapped; - } - } - } - - /// - public object? GetService(System.Type serviceType, object? serviceKey = null) - { - if (serviceType is null) - { - throw new ArgumentNullException(nameof(serviceType)); - } - - if (serviceKey is not null) - { - return null; - } - - if (serviceType == typeof(ChatClientMetadata)) - { - return _metadata; - } - - if (serviceType.IsInstanceOfType(this)) - { - return this; - } - - if (serviceType.IsInstanceOfType(_asyncSession)) - { - return _asyncSession; - } - - return null; - } - - /// - public async ValueTask DisposeAsync() - { - if (Interlocked.Exchange(ref _disposed, 1) != 0) - { - return; - } - - _responseInProgress = false; - await _asyncSession.DisposeAsync().ConfigureAwait(false); - _sendLock.Dispose(); - } - - #region Send Helpers (MEAI → Google GenAI) - - private void HandleAudioAppend( - InputAudioBufferAppendRealtimeClientMessage audioAppend) - { - if (audioAppend.Content is null || !audioAppend.Content.HasTopLevelMediaType("audio")) - { - return; - } - - byte[] audioBytes = ExtractDataBytes(audioAppend.Content); - - // Buffer audio data; it will be sent on commit with proper activity framing. - lock (_audioBufferLock) - { - if (_audioBufferSize + audioBytes.Length > MaxAudioBufferBytes) - { - throw new InvalidOperationException( - $"Audio buffer would exceed {MaxAudioBufferBytes} bytes. " + - "Call AudioBufferCommit before appending more audio."); - } - - _audioBuffer.Add(audioBytes); - _audioBufferSize += audioBytes.Length; - } - } - - private async Task HandleAudioCommitAsync(CancellationToken cancellationToken) - { - List bufferedChunks; - lock (_audioBufferLock) - { - if (_audioBuffer.Count == 0) - { - return; - } - - // Snapshot and clear the buffer. Avoids consolidating all chunks into a - // single array only to re-split — instead we send each buffered chunk directly. - bufferedChunks = new List(_audioBuffer); - _audioBuffer.Clear(); - _audioBufferSize = 0; - } - - _lastSendWasToolResponse = false; - _pendingMediaNeedsTrigger = false; - - // When VAD is disabled, explicit ActivityStart/ActivityEnd framing is required - // to mark speech boundaries and trigger the model to respond. - // When VAD is enabled, the server auto-detects speech boundaries — - // sending explicit framing conflicts with automatic detection. - if (!_vadEnabled) - { - await _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - ActivityStart = new ActivityStart() - }, - cancellationToken).ConfigureAwait(false); - } - - // Send buffered chunks directly, splitting only those that exceed the frame size limit. - const int maxFrameBytes = 32_000; - foreach (var buffered in bufferedChunks) - { - if (buffered.Length <= maxFrameBytes) - { - // Common case: chunk fits in a single frame — send without copying - await SendAudioFrameAsync(buffered, cancellationToken).ConfigureAwait(false); - } - else - { - // Large chunk: split into frames - for (int i = 0; i < buffered.Length; i += maxFrameBytes) - { - int len = Math.Min(maxFrameBytes, buffered.Length - i); - byte[] frame = new byte[len]; - Buffer.BlockCopy(buffered, i, frame, 0, len); - await SendAudioFrameAsync(frame, cancellationToken).ConfigureAwait(false); - } - } - } - - // When VAD is disabled, signal end of user activity to trigger the model's response. - // When VAD is enabled, send AudioStreamEnd to indicate the mic was turned off and the - // server should process the buffered audio. AudioStreamEnd is specifically designed for - // the push-to-talk pattern with automatic activity detection. - if (!_vadEnabled) - { - await _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - ActivityEnd = new ActivityEnd() - }, - cancellationToken).ConfigureAwait(false); - } - else - { - await _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - AudioStreamEnd = true - }, - cancellationToken).ConfigureAwait(false); - } - } - - private Task SendAudioFrameAsync(byte[] data, CancellationToken cancellationToken) - { - return _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - Audio = new Blob - { - Data = data, - MimeType = _inputAudioMimeType, - } - }, - cancellationToken); - } - - private async Task HandleConversationItemCreateAsync( - CreateConversationItemRealtimeClientMessage itemCreate, - CancellationToken cancellationToken) - { - if (itemCreate.Item?.Contents is null or { Count: 0 }) - { - return; - } - - // Collect all function results (tool responses use a separate API call). - var functionResults = new List(); - foreach (var content in itemCreate.Item.Contents) - { - if (content is FunctionResultContent functionResult) - { - _callIdToFunctionName.TryRemove(functionResult.CallId, out var functionName); - functionResults.Add(new FunctionResponse - { - Id = functionResult.CallId, - Name = functionName ?? string.Empty, - Response = new Dictionary - { - ["result"] = functionResult.Result?.ToString() ?? string.Empty - } - }); - } - } - - if (functionResults.Count > 0) - { - // Buffer function results — they will be flushed as a single batched - // SendToolResponseAsync call when CreateResponse arrives. - _pendingToolResponses.AddRange(functionResults); - _lastSendWasToolResponse = true; - return; - } - - // Send text and media via SendRealtimeInputAsync without activity framing. - // Text auto-triggers a model response. Images/audio are treated as streaming - // context by Gemini's Live API — they do NOT auto-trigger a response. - // When only media is sent (no accompanying text), we append a brief text prompt - // so the model knows to respond about the media content. - bool hasText = false; - bool hasMedia = false; - foreach (var content in itemCreate.Item.Contents) - { - if (content is TextContent textContent && !string.IsNullOrEmpty(textContent.Text)) - { - hasText = true; - _lastSendWasToolResponse = false; - await _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - Text = textContent.Text, - }, - cancellationToken).ConfigureAwait(false); - } - else if (content is DataContent dataContent) - { - if (dataContent.HasTopLevelMediaType("image")) - { - hasMedia = true; - _lastSendWasToolResponse = false; - await _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - Video = new Blob - { - Data = ExtractDataBytes(dataContent), - MimeType = dataContent.MediaType ?? "image/jpeg", - } - }, - cancellationToken).ConfigureAwait(false); - } - else if (dataContent.HasTopLevelMediaType("audio")) - { - hasMedia = true; - _lastSendWasToolResponse = false; - await _asyncSession.SendRealtimeInputAsync( - new LiveSendRealtimeInputParameters - { - Audio = new Blob - { - Data = ExtractDataBytes(dataContent), - MimeType = dataContent.MediaType ?? _inputAudioMimeType, - } - }, - cancellationToken).ConfigureAwait(false); - } - } - } - - if (hasMedia && !hasText) - { - // Gemini treats media as streaming context (like a video frame) and won't - // respond until it receives a text/voice prompt. Send a brief text to - // trigger a response about the media content. - _pendingMediaNeedsTrigger = true; - } - } - - internal static byte[] ExtractDataBytes(DataContent content) - { - string? dataUri = content.Uri?.ToString(); - - if (dataUri is not null) - { - int commaIndex = dataUri.LastIndexOf(','); - if (commaIndex >= 0 && commaIndex < dataUri.Length - 1) - { - string base64 = dataUri.Substring(commaIndex + 1); - try - { - return Convert.FromBase64String(base64); - } - catch (FormatException) - { - // Fall through to content.Data.ToArray() below - } - } - } - - return content.Data.ToArray(); - } - - #endregion - - #region Receive Helpers (Google GenAI → MEAI) - - private IEnumerable MapServerMessage(LiveServerMessage serverMessage) - { - // SetupComplete — skip (internal protocol message, not relevant to MEAI consumers) - if (serverMessage.SetupComplete is not null) - { - yield break; - } - - // Server content (model responses — audio, text, transcription) - if (serverMessage.ServerContent is { } serverContent) - { - foreach (var msg in MapServerContent(serverContent, serverMessage)) - { - yield return msg; - } - } - - // Tool calls — emit ResponseCreated (if not already), then ResponseOutputItemAdded + ResponseOutputItemDone for each - if (serverMessage.ToolCall is { FunctionCalls: { Count: > 0 } functionCalls }) - { - if (!_responseInProgress) - { - _responseInProgress = true; - yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseCreated) - { - RawRepresentation = serverMessage, - }; - } - - foreach (var fc in functionCalls) - { - // Ensure every function call has a usable ID for the round-trip mapping. - var callId = fc.Id ?? Guid.NewGuid().ToString(); - var functionName = fc.Name ?? string.Empty; - - _callIdToFunctionName[callId] = functionName; - - var contents = new List - { - new FunctionCallContent( - callId, - functionName, - fc.Args?.ToDictionary(kvp => kvp.Key, kvp => (object?)kvp.Value)) - }; - - var item = new RealtimeConversationItem(contents, id: callId, role: ChatRole.Assistant); - - // Emit ResponseOutputItemAdded (signals start of output item) - yield return new ResponseOutputItemRealtimeServerMessage(RealtimeServerMessageType.ResponseOutputItemAdded) - { - Item = item, - RawRepresentation = serverMessage, - }; - - // Emit ResponseOutputItemDone (required by FunctionInvokingRealtimeSession middleware) - yield return new ResponseOutputItemRealtimeServerMessage(RealtimeServerMessageType.ResponseOutputItemDone) - { - Item = item, - RawRepresentation = serverMessage, - }; - } - } - - // Tool call cancellation - if (serverMessage.ToolCallCancellation is { Ids: { Count: > 0 } }) - { - yield return new RealtimeServerMessage - { - Type = RealtimeServerMessageType.RawContentOnly, - RawRepresentation = serverMessage, - }; - } - - // Usage metadata — emit as ResponseDone only if one wasn't already emitted - // by TurnComplete/GenerationComplete above (which resets _responseInProgress). - if (serverMessage.UsageMetadata is { } usage && _responseInProgress) - { - _responseInProgress = false; - yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseDone) - { - Usage = new UsageDetails - { - InputTokenCount = usage.PromptTokenCount ?? 0, - OutputTokenCount = usage.ResponseTokenCount ?? 0, - TotalTokenCount = usage.TotalTokenCount ?? 0, - }, - RawRepresentation = serverMessage, - }; - } - - // GoAway (server disconnect) - if (serverMessage.GoAway is not null) - { - yield return new ErrorRealtimeServerMessage - { - Error = new ErrorContent("Server is disconnecting (GoAway)"), - RawRepresentation = serverMessage, - }; - } - } - - private IEnumerable MapServerContent( - LiveServerContent serverContent, - LiveServerMessage rawMessage) - { - if (serverContent.ModelTurn?.Parts is { Count: > 0 } parts) - { - // Emit ResponseCreated once when a new response cycle begins - if (!_responseInProgress) - { - _responseInProgress = true; - yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseCreated) - { - RawRepresentation = rawMessage, - }; - } - - foreach (var part in parts) - { - // Audio data - if (part.InlineData is { Data: not null } blob && - blob.MimeType?.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) == true) - { - yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputAudioDelta) - { - Audio = Convert.ToBase64String(blob.Data), - RawRepresentation = rawMessage, - }; - } - - // Text response - if (!string.IsNullOrEmpty(part.Text)) - { - yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputTextDelta) - { - Text = part.Text, - RawRepresentation = rawMessage, - }; - } - } - } - - // Input transcription - if (serverContent.InputTranscription is { Text: not null } inputTranscription) - { - yield return new InputAudioTranscriptionRealtimeServerMessage(RealtimeServerMessageType.InputAudioTranscriptionCompleted) - { - Transcription = inputTranscription.Text, - RawRepresentation = rawMessage, - }; - } - - // Output transcription - if (serverContent.OutputTranscription is { Text: not null } outputTranscription) - { - yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputAudioTranscriptionDelta) - { - Text = outputTranscription.Text, - RawRepresentation = rawMessage, - }; - } - - // Turn complete or generation complete — reset response tracking and emit ResponseDone - if (serverContent.TurnComplete == true || serverContent.GenerationComplete == true) - { - _responseInProgress = false; - yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseDone) - { - RawRepresentation = rawMessage, - }; - } - } - - #endregion - - #region Tool Mapping Helpers - - /// - /// Converts an to a Google GenAI , - /// mapping the function name, description, and JSON schema for parameters. - /// - /// The AI function to convert. - /// A Google GenAI function declaration. - internal static FunctionDeclaration ToGoogleFunctionDeclaration(AIFunction aiFunction) - { - var declaration = new FunctionDeclaration - { - Name = aiFunction.Name, - Description = aiFunction.Description, - }; - - // Convert the MEAI JSON schema to a Google Schema object. - // Google's API expects the Schema type with uppercase type names (STRING, OBJECT, etc.), - // not raw JSON schema with lowercase types. Using Parameters instead of ParametersJsonSchema - // ensures compatibility with the Live API's function calling. - if (aiFunction.JsonSchema is JsonElement schemaElement && - schemaElement.ValueKind == JsonValueKind.Object) - { - declaration.Parameters = ConvertJsonSchemaToGoogleSchema(schemaElement); - } - - return declaration; - } - - /// - /// Recursively converts a standard JSON Schema to a Google GenAI - /// object, mapping lowercase type names to Google's uppercase enum values. - /// - internal static Schema ConvertJsonSchemaToGoogleSchema(JsonElement element) - { - var schema = new Schema(); - - if (element.TryGetProperty("type", out var typeValue)) - { - schema.Type = typeValue.GetString()?.ToLowerInvariant() switch - { - "object" => Google.GenAI.Types.Type.Object, - "string" => Google.GenAI.Types.Type.String, - "integer" => Google.GenAI.Types.Type.Integer, - "number" => Google.GenAI.Types.Type.Number, - "boolean" => Google.GenAI.Types.Type.Boolean, - "array" => Google.GenAI.Types.Type.Array, - _ => null - }; - } - - if (element.TryGetProperty("description", out var desc) && - desc.ValueKind == JsonValueKind.String) - { - schema.Description = desc.GetString(); - } - - if (element.TryGetProperty("properties", out var props) && - props.ValueKind == JsonValueKind.Object) - { - schema.Properties = new Dictionary(); - foreach (var prop in props.EnumerateObject()) - { - schema.Properties[prop.Name] = ConvertJsonSchemaToGoogleSchema(prop.Value); - } - } - - if (element.TryGetProperty("required", out var req) && - req.ValueKind == JsonValueKind.Array) - { - schema.Required = new List(); - foreach (var item in req.EnumerateArray()) - { - if (item.ValueKind == JsonValueKind.String) - { - schema.Required.Add(item.GetString()!); - } - } - } - - if (element.TryGetProperty("items", out var items) && - items.ValueKind == JsonValueKind.Object) - { - schema.Items = ConvertJsonSchemaToGoogleSchema(items); - } - - return schema; - } - - #endregion -} - +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections; +using System.Collections.Concurrent; +using System.Linq; +using System.Net.WebSockets; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; +using System.Text.Json; + +using Google.GenAI; +using Google.GenAI.Types; + +namespace Microsoft.Extensions.AI; + +/// +/// Provides an implementation for Google GenAI's Live API, +/// wrapping an WebSocket connection. +/// +#if NET8_0_OR_GREATER +[System.Diagnostics.CodeAnalysis.Experimental("MEAI001")] +#endif +public sealed class GoogleGenAIRealtimeSession : IRealtimeClientSession +{ + private readonly AsyncSession _asyncSession; + private readonly ChatClientMetadata _metadata; + private int _disposed; + + // Buffer for audio chunks between Append and Commit. + // Protected by _audioBufferLock. Capped at MaxAudioBufferBytes to prevent unbounded growth. + private readonly List _audioBuffer = new(); + private readonly object _audioBufferLock = new(); + private int _audioBufferSize; + + /// Maximum buffered audio size (10 MB). Exceeding this throws . + private const int MaxAudioBufferBytes = 10 * 1024 * 1024; + + // Track whether a response is in progress to emit ResponseCreated only once per response. + // Accessed only from GetStreamingResponseAsync's single enumeration; callers must not + // enumerate concurrently. + private bool _responseInProgress; + + // Guards against multiple concurrent GetStreamingResponseAsync enumerations. A single + // WebSocket can't safely serve two concurrent readers. + private int _activeStreamingEnumeration; + + /// Maximum nesting depth for tool payloads to prevent stack overflow from malicious/malformed data. + private const int MaxToolPayloadDepth = 64; + + // Serializes all WebSocket send operations. Required because: + // 1. WebSocket.SendAsync is NOT thread-safe for concurrent calls. + // 2. FunctionInvokingRealtimeSession middleware can call SendAsync (to return + // function results) concurrently with the caller's own SendAsync (e.g., audio). + // 3. HandleAudioCommitAsync sends a multi-message sequence (ActivityStart → + // audio frames → ActivityEnd) that must be atomic. + private readonly SemaphoreSlim _sendLock = new(1, 1); + + // Track whether a tool response was just sent. After SendToolResponseAsync, the server + // automatically continues generating — sending TurnComplete would be unexpected. + private bool _lastSendWasToolResponse; + + // Track whether the last content sent was media (image/video/audio via CreateConversationItem) + // that does not auto-trigger a model response. Unlike text, media input requires an explicit + // ActivityEnd signal in CreateResponse to prompt the model to respond. + private bool _pendingMediaNeedsTrigger; + + // Maps function call IDs to function names. Populated when ToolCall messages arrive, + // consumed when sending FunctionResponse back to the server. + private readonly ConcurrentDictionary _callIdToFunctionName = new(); + + // Accumulates function results across multiple CreateConversationItem sends so they can + // be batched into a single SendToolResponseAsync call. The MEAI middleware sends one + // CreateConversationItem per function result followed by a single CreateResponse. + // Gemini expects all function results in one SendToolResponseAsync call, so we buffer + // them here and flush on CreateResponse. + private readonly List _pendingToolResponses = new(); + + // When true, automatic VAD is enabled and the server handles speech boundary detection. + // ActivityStart/ActivityEnd framing is skipped during audio commit. + private readonly bool _vadEnabled; + + // The MIME type for audio frames sent to the server, derived from InputAudioFormat. + private readonly string _inputAudioMimeType; + + /// + public RealtimeSessionOptions? Options { get; private set; } + + /// Initializes a new instance wrapping a connected . + /// The connected for WebSocket communication. + /// The model name for metadata. + /// Optional initial session options. + public GoogleGenAIRealtimeSession( + AsyncSession asyncSession, + string model, + RealtimeSessionOptions? initialOptions) + { + _asyncSession = asyncSession ?? throw new ArgumentNullException(nameof(asyncSession)); + _metadata = new ChatClientMetadata("google-genai", defaultModelId: model); + Options = initialOptions; + _vadEnabled = initialOptions?.VoiceActivityDetection is { Enabled: true }; + _inputAudioMimeType = initialOptions?.InputAudioFormat?.MediaType ?? "audio/pcm"; + } + + /// + public async Task SendAsync( + RealtimeClientMessage message, + CancellationToken cancellationToken = default) + { + if (message is null) + { + throw new ArgumentNullException(nameof(message)); + } + + if (Volatile.Read(ref _disposed) != 0) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + + cancellationToken.ThrowIfCancellationRequested(); + + // AudioAppend only buffers data in memory — no WebSocket I/O, no lock needed. + if (message is InputAudioBufferAppendRealtimeClientMessage audioAppend) + { + HandleAudioAppend(audioAppend); + return; + } + + // All other message types perform WebSocket I/O and must be serialized. + // WaitAsync may throw ObjectDisposedException if DisposeAsync races between the + // _disposed check above and this call — treat it the same as a post-dispose send. + try + { + await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); + } + catch (ObjectDisposedException) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + + try + { + // Recheck after acquiring lock to avoid race with DisposeAsync + if (Volatile.Read(ref _disposed) != 0) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + + switch (message) + { + case InputAudioBufferCommitRealtimeClientMessage: + await HandleAudioCommitAsync(cancellationToken).ConfigureAwait(false); + break; + + case CreateConversationItemRealtimeClientMessage itemCreate: + await HandleConversationItemCreateAsync(itemCreate, cancellationToken).ConfigureAwait(false); + break; + + case SessionUpdateRealtimeClientMessage: + // Gemini's Live API does not support mid-session reconfiguration. + break; + + case CreateResponseRealtimeClientMessage: + if (_pendingToolResponses.Count > 0) + { + // Flush all buffered function results in a single SendToolResponseAsync call. + // The MEAI middleware sends one CreateConversationItem per function result, + // but Gemini expects all results in one call. + await _asyncSession.SendToolResponseAsync( + new LiveSendToolResponseParameters + { + FunctionResponses = new List(_pendingToolResponses) + }, + cancellationToken).ConfigureAwait(false); + _pendingToolResponses.Clear(); + _lastSendWasToolResponse = true; + } + + if (_lastSendWasToolResponse) + { + // After a tool response, Gemini automatically continues generating. + // Do not send ActivityEnd — it would be unexpected. + _lastSendWasToolResponse = false; + } + else if (_pendingMediaNeedsTrigger) + { + // Media inputs (image, video) via SendRealtimeInputAsync are added to + // the model's context but don't auto-trigger a response. Gemini's Live API + // has no equivalent to OpenAI's CreateResponse command — the only way to + // trigger a response is via text input. Send a minimal whitespace text to + // prompt the model to respond about the media in context without biasing + // the response content. + _pendingMediaNeedsTrigger = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Text = " ", + }, + cancellationToken).ConfigureAwait(false); + } + // For text input: auto-triggers, no signal needed. + // For audio commit: ActivityEnd/AudioStreamEnd already sent in HandleAudioCommitAsync. + break; + + default: + break; + } + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + // The caller explicitly cancelled via their token — propagate so they + // can observe the cancellation they requested. + throw; + } + catch (ObjectDisposedException) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + catch (WebSocketException) when (Volatile.Read(ref _disposed) != 0) + { + // WebSocketException during/after disposal is expected — swallow. + } + finally + { + try + { + _sendLock.Release(); + } + catch (ObjectDisposedException) + { + // DisposeAsync was called concurrently and disposed the semaphore. + } + } + } + + /// + public async IAsyncEnumerable GetStreamingResponseAsync( + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + if (Volatile.Read(ref _disposed) != 0) + { + throw new ObjectDisposedException(nameof(GoogleGenAIRealtimeSession)); + } + + if (Interlocked.CompareExchange(ref _activeStreamingEnumeration, 1, 0) != 0) + { + throw new InvalidOperationException( + "Only one active streaming enumeration is allowed at a time. " + + "Await or cancel the existing enumeration before starting a new one."); + } + + try + { + while (!cancellationToken.IsCancellationRequested) + { + LiveServerMessage? serverMessage; + try + { + serverMessage = await _asyncSession.ReceiveAsync(cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + // The caller explicitly cancelled via their token — propagate so they + // can observe the cancellation they requested. + throw; + } + catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException or WebSocketException) + { + // These exceptions are expected during session teardown and are swallowed: + // - OperationCanceledException: internal cancellation from disposal (not the caller's token). + // - ObjectDisposedException: DisposeAsync was called on another thread while an + // operation was in-flight on the underlying WebSocket. + // - WebSocketException: the connection was closed (server disconnect or local close). + yield break; + } + + if (serverMessage is null) + { + yield break; + } + + // Map Google Live server messages to MEAI server message types + foreach (var mapped in MapServerMessage(serverMessage)) + { + yield return mapped; + } + } + } + finally + { + Volatile.Write(ref _activeStreamingEnumeration, 0); + } + } + + /// + public object? GetService(System.Type serviceType, object? serviceKey = null) + { + if (serviceType is null) + { + throw new ArgumentNullException(nameof(serviceType)); + } + + if (serviceKey is not null) + { + return null; + } + + if (serviceType == typeof(ChatClientMetadata)) + { + return _metadata; + } + + if (serviceType.IsInstanceOfType(this)) + { + return this; + } + + if (serviceType.IsInstanceOfType(_asyncSession)) + { + return _asyncSession; + } + + return null; + } + + /// + public async ValueTask DisposeAsync() + { + if (Interlocked.Exchange(ref _disposed, 1) != 0) + { + return; + } + + _responseInProgress = false; + + Exception? firstException = null; + try + { + await _asyncSession.DisposeAsync().ConfigureAwait(false); + } + catch (Exception ex) + { + firstException = ex; + } + + try + { + _sendLock.Dispose(); + } + catch (Exception ex) when (firstException is null) + { + firstException = ex; + } + + if (firstException is not null) + { + ExceptionDispatchInfo.Capture(firstException).Throw(); + } + } + + #region Send Helpers (MEAI → Google GenAI) + + private void HandleAudioAppend( + InputAudioBufferAppendRealtimeClientMessage audioAppend) + { + if (audioAppend.Content is null || !audioAppend.Content.HasTopLevelMediaType("audio")) + { + return; + } + + byte[] audioBytes = ExtractDataBytes(audioAppend.Content); + + // Buffer audio data; it will be sent on commit with proper activity framing. + lock (_audioBufferLock) + { + if (_audioBufferSize + audioBytes.Length > MaxAudioBufferBytes) + { + throw new InvalidOperationException( + $"Audio buffer would exceed {MaxAudioBufferBytes} bytes. " + + "Call AudioBufferCommit before appending more audio."); + } + + _audioBuffer.Add(audioBytes); + _audioBufferSize += audioBytes.Length; + } + } + + private async Task HandleAudioCommitAsync(CancellationToken cancellationToken) + { + List bufferedChunks; + lock (_audioBufferLock) + { + if (_audioBuffer.Count == 0) + { + return; + } + + // Snapshot and clear the buffer. Avoids consolidating all chunks into a + // single array only to re-split — instead we send each buffered chunk directly. + bufferedChunks = new List(_audioBuffer); + _audioBuffer.Clear(); + _audioBufferSize = 0; + } + + _lastSendWasToolResponse = false; + _pendingMediaNeedsTrigger = false; + + // When VAD is disabled, explicit ActivityStart/ActivityEnd framing is required + // to mark speech boundaries and trigger the model to respond. + // When VAD is enabled, the server auto-detects speech boundaries — + // sending explicit framing conflicts with automatic detection. + if (!_vadEnabled) + { + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + ActivityStart = new ActivityStart() + }, + cancellationToken).ConfigureAwait(false); + } + + // Send buffered chunks directly, splitting only those that exceed the frame size limit. + const int maxFrameBytes = 32_000; + foreach (var buffered in bufferedChunks) + { + if (buffered.Length <= maxFrameBytes) + { + // Common case: chunk fits in a single frame — send without copying + await SendAudioFrameAsync(buffered, cancellationToken).ConfigureAwait(false); + } + else + { + // Large chunk: split into frames + for (int i = 0; i < buffered.Length; i += maxFrameBytes) + { + int len = Math.Min(maxFrameBytes, buffered.Length - i); + byte[] frame = new byte[len]; + Buffer.BlockCopy(buffered, i, frame, 0, len); + await SendAudioFrameAsync(frame, cancellationToken).ConfigureAwait(false); + } + } + } + + // When VAD is disabled, signal end of user activity to trigger the model's response. + // When VAD is enabled, send AudioStreamEnd to indicate the mic was turned off and the + // server should process the buffered audio. AudioStreamEnd is specifically designed for + // the push-to-talk pattern with automatic activity detection. + if (!_vadEnabled) + { + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + ActivityEnd = new ActivityEnd() + }, + cancellationToken).ConfigureAwait(false); + } + else + { + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + AudioStreamEnd = true + }, + cancellationToken).ConfigureAwait(false); + } + } + + private Task SendAudioFrameAsync(byte[] data, CancellationToken cancellationToken) + { + return _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Audio = new Blob + { + Data = data, + MimeType = _inputAudioMimeType, + } + }, + cancellationToken); + } + + private async Task HandleConversationItemCreateAsync( + CreateConversationItemRealtimeClientMessage itemCreate, + CancellationToken cancellationToken) + { + if (itemCreate.Item?.Contents is null or { Count: 0 }) + { + return; + } + + // Collect all function results (tool responses use a separate API call). + var functionResults = new List(); + foreach (var content in itemCreate.Item.Contents) + { + if (content is FunctionResultContent functionResult) + { + _callIdToFunctionName.TryRemove(functionResult.CallId, out var functionName); + functionResults.Add(new FunctionResponse + { + Id = functionResult.CallId, + Name = functionName ?? string.Empty, + Response = new Dictionary + { + ["result"] = NormalizeToolPayload(functionResult.Result) ?? string.Empty + } + }); + } + } + + if (functionResults.Count > 0) + { + // Buffer function results — they will be flushed as a single batched + // SendToolResponseAsync call when CreateResponse arrives. + _pendingToolResponses.AddRange(functionResults); + _lastSendWasToolResponse = true; + return; + } + + // Send text and media via SendRealtimeInputAsync without activity framing. + // Text auto-triggers a model response. Images/audio are treated as streaming + // context by Gemini's Live API — they do NOT auto-trigger a response. + // When only media is sent (no accompanying text), we append a brief text prompt + // so the model knows to respond about the media content. + bool hasText = false; + bool hasMedia = false; + foreach (var content in itemCreate.Item.Contents) + { + if (content is TextContent textContent && !string.IsNullOrEmpty(textContent.Text)) + { + hasText = true; + _lastSendWasToolResponse = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Text = textContent.Text, + }, + cancellationToken).ConfigureAwait(false); + } + else if (content is DataContent dataContent) + { + if (dataContent.HasTopLevelMediaType("image")) + { + hasMedia = true; + _lastSendWasToolResponse = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Video = new Blob + { + Data = ExtractDataBytes(dataContent), + MimeType = dataContent.MediaType ?? "image/jpeg", + } + }, + cancellationToken).ConfigureAwait(false); + } + else if (dataContent.HasTopLevelMediaType("audio")) + { + hasMedia = true; + _lastSendWasToolResponse = false; + await _asyncSession.SendRealtimeInputAsync( + new LiveSendRealtimeInputParameters + { + Audio = new Blob + { + Data = ExtractDataBytes(dataContent), + MimeType = dataContent.MediaType ?? _inputAudioMimeType, + } + }, + cancellationToken).ConfigureAwait(false); + } + } + } + + if (hasMedia && !hasText) + { + // Gemini treats media as streaming context (like a video frame) and won't + // respond until it receives a text/voice prompt. Send a brief text to + // trigger a response about the media content. + _pendingMediaNeedsTrigger = true; + } + } + + internal static byte[] ExtractDataBytes(DataContent content) + { + string? dataUri = content.Uri?.ToString(); + + if (dataUri is not null) + { + int commaIndex = dataUri.LastIndexOf(','); + if (commaIndex >= 0 && commaIndex < dataUri.Length - 1) + { + string base64 = dataUri.Substring(commaIndex + 1); + try + { + return Convert.FromBase64String(base64); + } + catch (FormatException) + { + // Fall through to content.Data.ToArray() below + } + } + } + + return content.Data.ToArray(); + } + + #endregion + + #region Tool Payload Normalization + + internal static Dictionary NormalizeToolArguments(IReadOnlyDictionary arguments, int depth = 0) + { + ValidateToolPayloadDepth(depth); + + var normalized = new Dictionary(arguments.Count); + foreach (var pair in arguments) + { + normalized[pair.Key] = NormalizeToolPayload(pair.Value, depth + 1); + } + return normalized; + } + + internal static object? NormalizeToolPayload(object? value, int depth = 0) + { + ValidateToolPayloadDepth(depth); + + switch (value) + { + case null: + return null; + + case JsonElement element: + return ConvertJsonElementToToolPayload(element, depth + 1); + + case JsonDocument document: + return ConvertJsonElementToToolPayload(document.RootElement, depth + 1); + + case TextContent textContent: + return textContent.Text ?? ""; + + case DataContent dataContent: + return new Dictionary + { + ["data"] = Convert.ToBase64String(ExtractDataBytes(dataContent)), + ["mimeType"] = dataContent.MediaType, + ["name"] = dataContent.Name, + }; + + case UriContent uriContent: + return new Dictionary + { + ["uri"] = uriContent.Uri.AbsoluteUri, + ["mimeType"] = uriContent.MediaType, + }; + + case IEnumerable> pairs: + return NormalizeToolArguments(pairs.ToDictionary(pair => pair.Key, pair => pair.Value), depth + 1); + + case IDictionary dictionary: + var mapped = new Dictionary(); + foreach (DictionaryEntry entry in dictionary) + { + if (entry.Key is string key) + { + mapped[key] = NormalizeToolPayload(entry.Value, depth + 1); + } + } + return mapped; + + case IEnumerable aiContents: + return aiContents.Select(content => NormalizeToolPayload(content, depth + 1)).ToList(); + + case string or bool or byte or sbyte or short or ushort or int or uint or long or ulong or + float or double or decimal: + return value; + + case byte[] bytes: + return Convert.ToBase64String(bytes); + + case ReadOnlyMemory readOnlyMemory: + return Convert.ToBase64String(readOnlyMemory.ToArray()); + + case Memory memory: + return Convert.ToBase64String(memory.ToArray()); + + case Enum enumValue: + return enumValue.ToString(); + + case IEnumerable enumerable when value is not string: + var list = new List(); + foreach (object? item in enumerable) + { + list.Add(NormalizeToolPayload(item, depth + 1)); + } + return list; + + default: + return value.ToString(); + } + } + + private static object? ConvertJsonElementToToolPayload(JsonElement element, int depth) + { + ValidateToolPayloadDepth(depth); + + switch (element.ValueKind) + { + case JsonValueKind.Object: + var dictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + dictionary[property.Name] = ConvertJsonElementToToolPayload(property.Value, depth + 1); + } + return dictionary; + + case JsonValueKind.Array: + var arrayList = new List(); + foreach (var item in element.EnumerateArray()) + { + arrayList.Add(ConvertJsonElementToToolPayload(item, depth + 1)); + } + return arrayList; + + case JsonValueKind.String: + return element.GetString(); + + case JsonValueKind.Number: + return element.TryGetInt64(out long intValue) ? intValue : element.GetDouble(); + + case JsonValueKind.True: + return true; + + case JsonValueKind.False: + return false; + + case JsonValueKind.Null: + case JsonValueKind.Undefined: + default: + return null; + } + } + + private static void ValidateToolPayloadDepth(int depth) + { + if (depth > MaxToolPayloadDepth) + { + throw new InvalidOperationException( + $"Realtime tool payloads exceed the maximum supported nesting depth of {MaxToolPayloadDepth}."); + } + } + + #endregion + + #region Receive Helpers (Google GenAI → MEAI) + + private IEnumerable MapServerMessage(LiveServerMessage serverMessage) + { + // SetupComplete — skip (internal protocol message, not relevant to MEAI consumers) + if (serverMessage.SetupComplete is not null) + { + yield break; + } + + // Server content (model responses — audio, text, transcription) + if (serverMessage.ServerContent is { } serverContent) + { + foreach (var msg in MapServerContent(serverContent, serverMessage)) + { + yield return msg; + } + } + + // Tool calls — emit ResponseCreated (if not already), then ResponseOutputItemAdded + ResponseOutputItemDone for each + if (serverMessage.ToolCall is { FunctionCalls: { Count: > 0 } functionCalls }) + { + if (!_responseInProgress) + { + _responseInProgress = true; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseCreated) + { + RawRepresentation = serverMessage, + }; + } + + foreach (var fc in functionCalls) + { + // Ensure every function call has a usable ID for the round-trip mapping. + var callId = fc.Id ?? Guid.NewGuid().ToString(); + var functionName = fc.Name ?? string.Empty; + + _callIdToFunctionName[callId] = functionName; + + var contents = new List + { + fc.Args is not null + ? FunctionCallContent.CreateFromParsedArguments( + fc.Args, callId, functionName, + static args => args is IReadOnlyDictionary dictionary + ? NormalizeToolArguments(dictionary) : null) + : new FunctionCallContent(callId, functionName) + }; + + var item = new RealtimeConversationItem(contents, id: callId, role: ChatRole.Assistant); + + // Emit ResponseOutputItemAdded (signals start of output item) + yield return new ResponseOutputItemRealtimeServerMessage(RealtimeServerMessageType.ResponseOutputItemAdded) + { + Item = item, + RawRepresentation = serverMessage, + }; + + // Emit ResponseOutputItemDone (required by FunctionInvokingRealtimeSession middleware) + yield return new ResponseOutputItemRealtimeServerMessage(RealtimeServerMessageType.ResponseOutputItemDone) + { + Item = item, + RawRepresentation = serverMessage, + }; + } + } + + // Tool call cancellation + if (serverMessage.ToolCallCancellation is { Ids: { Count: > 0 } }) + { + yield return new RealtimeServerMessage + { + Type = RealtimeServerMessageType.RawContentOnly, + RawRepresentation = serverMessage, + }; + } + + // Usage metadata — emit as ResponseDone only if one wasn't already emitted + // by TurnComplete/GenerationComplete above (which resets _responseInProgress). + if (serverMessage.UsageMetadata is { } usage && _responseInProgress) + { + _responseInProgress = false; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseDone) + { + Usage = new UsageDetails + { + InputTokenCount = usage.PromptTokenCount ?? 0, + OutputTokenCount = usage.ResponseTokenCount ?? 0, + TotalTokenCount = usage.TotalTokenCount ?? 0, + }, + RawRepresentation = serverMessage, + }; + } + + // GoAway (server disconnect) + if (serverMessage.GoAway is not null) + { + yield return new ErrorRealtimeServerMessage + { + Error = new ErrorContent("Server is disconnecting (GoAway)"), + RawRepresentation = serverMessage, + }; + } + } + + private IEnumerable MapServerContent( + LiveServerContent serverContent, + LiveServerMessage rawMessage) + { + if (serverContent.ModelTurn?.Parts is { Count: > 0 } parts) + { + // Emit ResponseCreated once when a new response cycle begins + if (!_responseInProgress) + { + _responseInProgress = true; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseCreated) + { + RawRepresentation = rawMessage, + }; + } + + foreach (var part in parts) + { + // Audio data + if (part.InlineData is { Data: not null } blob && + blob.MimeType?.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) == true) + { + yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputAudioDelta) + { + Audio = Convert.ToBase64String(blob.Data), + RawRepresentation = rawMessage, + }; + } + + // Text response + if (!string.IsNullOrEmpty(part.Text)) + { + yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputTextDelta) + { + Text = part.Text, + RawRepresentation = rawMessage, + }; + } + } + } + + // Input transcription + if (serverContent.InputTranscription is { Text: not null } inputTranscription) + { + yield return new InputAudioTranscriptionRealtimeServerMessage(RealtimeServerMessageType.InputAudioTranscriptionCompleted) + { + Transcription = inputTranscription.Text, + RawRepresentation = rawMessage, + }; + } + + // Output transcription + if (serverContent.OutputTranscription is { Text: not null } outputTranscription) + { + yield return new OutputTextAudioRealtimeServerMessage(RealtimeServerMessageType.OutputAudioTranscriptionDelta) + { + Text = outputTranscription.Text, + RawRepresentation = rawMessage, + }; + } + + // Turn complete or generation complete — reset response tracking and emit ResponseDone + if (serverContent.TurnComplete == true || serverContent.GenerationComplete == true) + { + _responseInProgress = false; + yield return new ResponseCreatedRealtimeServerMessage(RealtimeServerMessageType.ResponseDone) + { + RawRepresentation = rawMessage, + }; + } + } + + #endregion + + #region Tool Mapping Helpers + + /// + /// Converts an to a Google GenAI , + /// mapping the function name, description, and JSON schema for parameters. + /// + /// The AI function to convert. + /// A Google GenAI function declaration. + internal static FunctionDeclaration ToGoogleFunctionDeclaration(AIFunction aiFunction) + { + var declaration = new FunctionDeclaration + { + Name = aiFunction.Name, + Description = aiFunction.Description, + }; + + // Convert the MEAI JSON schema to a Google Schema object. + // Google's API expects the Schema type with uppercase type names (STRING, OBJECT, etc.), + // not raw JSON schema with lowercase types. Using Parameters instead of ParametersJsonSchema + // ensures compatibility with the Live API's function calling. + if (aiFunction.JsonSchema is JsonElement schemaElement && + schemaElement.ValueKind == JsonValueKind.Object) + { + declaration.Parameters = ConvertJsonSchemaToGoogleSchema(schemaElement); + } + + return declaration; + } + + /// + /// Recursively converts a standard JSON Schema to a Google GenAI + /// object, mapping lowercase type names to Google's uppercase enum values. + /// + internal static Schema ConvertJsonSchemaToGoogleSchema(JsonElement element) + { + var schema = new Schema(); + + if (element.TryGetProperty("type", out var typeValue)) + { + schema.Type = typeValue.GetString()?.ToLowerInvariant() switch + { + "object" => Google.GenAI.Types.Type.Object, + "string" => Google.GenAI.Types.Type.String, + "integer" => Google.GenAI.Types.Type.Integer, + "number" => Google.GenAI.Types.Type.Number, + "boolean" => Google.GenAI.Types.Type.Boolean, + "array" => Google.GenAI.Types.Type.Array, + _ => null + }; + } + + if (element.TryGetProperty("description", out var desc) && + desc.ValueKind == JsonValueKind.String) + { + schema.Description = desc.GetString(); + } + + if (element.TryGetProperty("properties", out var props) && + props.ValueKind == JsonValueKind.Object) + { + schema.Properties = new Dictionary(); + foreach (var prop in props.EnumerateObject()) + { + schema.Properties[prop.Name] = ConvertJsonSchemaToGoogleSchema(prop.Value); + } + } + + if (element.TryGetProperty("required", out var req) && + req.ValueKind == JsonValueKind.Array) + { + schema.Required = new List(); + foreach (var item in req.EnumerateArray()) + { + if (item.ValueKind == JsonValueKind.String) + { + schema.Required.Add(item.GetString()!); + } + } + } + + if (element.TryGetProperty("items", out var items) && + items.ValueKind == JsonValueKind.Object) + { + schema.Items = ConvertJsonSchemaToGoogleSchema(items); + } + + return schema; + } + + #endregion +} + From c1d6f5eaa44f38a91dcbf9732351d1e19a33e153 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Thu, 16 Apr 2026 17:11:12 -0700 Subject: [PATCH 7/7] Revert changes to auto-generated files per review feedback --- Google.GenAI/Batches.cs | 24 +- Google.GenAI/Caches.cs | 2866 +++++++++++++++--------------- Google.GenAI/Files.cs | 1830 +++++++++---------- Google.GenAI/Models.cs | 60 +- Google.GenAI/Operations.cs | 852 ++++----- Google.GenAI/TokensConverters.cs | 1626 ++++++++--------- Google.GenAI/Tunings.cs | 20 +- 7 files changed, 3639 insertions(+), 3639 deletions(-) diff --git a/Google.GenAI/Batches.cs b/Google.GenAI/Batches.cs index 01017728..7a7d9357 100644 --- a/Google.GenAI/Batches.cs +++ b/Google.GenAI/Batches.cs @@ -1659,7 +1659,7 @@ private async Task PrivateCreateAsync(string? model, BatchJobSource sr if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse CreateBatchJobParameters to JsonNode."); @@ -1685,7 +1685,7 @@ private async Task PrivateCreateAsync(string? model, BatchJobSource sr HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1725,7 +1725,7 @@ private async Task PrivateCreateEmbeddingsAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException( @@ -1753,7 +1753,7 @@ private async Task PrivateCreateEmbeddingsAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1802,7 +1802,7 @@ public async Task GetAsync(string name, GetBatchJobConfig? config = nu if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse GetBatchJobParameters to JsonNode."); @@ -1828,7 +1828,7 @@ public async Task GetAsync(string name, GetBatchJobConfig? config = nu HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1876,7 +1876,7 @@ public async Task CancelAsync(string name, CancelBatchJobConfig? config = null, if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse CancelBatchJobParameters to JsonNode."); @@ -1902,7 +1902,7 @@ public async Task CancelAsync(string name, CancelBatchJobConfig? config = null, HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1934,7 +1934,7 @@ private async Task PrivateListAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse ListBatchJobsParameters to JsonNode."); @@ -1960,7 +1960,7 @@ private async Task PrivateListAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -2010,7 +2010,7 @@ public async Task DeleteAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse DeleteBatchJobParameters to JsonNode."); @@ -2036,7 +2036,7 @@ public async Task DeleteAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 diff --git a/Google.GenAI/Caches.cs b/Google.GenAI/Caches.cs index 14c6aab8..0a5f17a8 100644 --- a/Google.GenAI/Caches.cs +++ b/Google.GenAI/Caches.cs @@ -1,1433 +1,1433 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - - public sealed class Caches { - private readonly ApiClient _apiClient; - - internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { - Common.SetValueByPath(toObject, new string[] { "apiKey" }, - Common.GetValueByPath(fromObject, new string[] { "apiKey" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { - throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { - throw new NotSupportedException("authType parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { - throw new NotSupportedException( - "googleServiceAccountConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { - throw new NotSupportedException( - "httpBasicAuthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { - throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { - throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { - Common.SetValueByPath(toObject, new string[] { "data" }, - Common.GetValueByPath(fromObject, new string[] { "data" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode ContentToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode CreateCachedContentConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ToolToMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, - ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "toolConfig" })), - toObject)); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }))) { - throw new NotSupportedException("kmsKeyName parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode CreateCachedContentConfigToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "displayName" }, - Common.GetValueByPath(fromObject, new string[] { "displayName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { - var keyList = - Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); - JsonArray result = new JsonArray(); - - foreach (var record in keyList) { - result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "contents" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "systemInstruction" }, - ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( - fromObject, new string[] { "systemInstruction" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ToolToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, - ToolConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "toolConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "encryption_spec", "kmsKeyName" }, - Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" })); - } - - return toObject; - } - - internal JsonNode CreateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "model" }, - Transformers.TCachesModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateCachedContentConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode CreateCachedContentParametersToVertex(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "model" }, - Transformers.TCachesModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = CreateCachedContentConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentParametersToVertex(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentResponseFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode DeleteCachedContentResponseFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileUri" }, - Common.GetValueByPath(fromObject, new string[] { "fileUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { - Common.SetValueByPath(toObject, new string[] { "id" }, - Common.GetValueByPath(fromObject, new string[] { "id" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { - Common.SetValueByPath(toObject, new string[] { "args" }, - Common.GetValueByPath(fromObject, new string[] { "args" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { - throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { - throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "allowedFunctionNames" }, - Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { - throw new NotSupportedException( - "streamFunctionCallArguments parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode FunctionDeclarationToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { - Common.SetValueByPath(toObject, new string[] { "parameters" }, - Common.GetValueByPath(fromObject, new string[] { "parameters" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parametersJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath(toObject, new string[] { "response" }, - Common.GetValueByPath(fromObject, new string[] { "response" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "behavior" }))) { - throw new NotSupportedException("behavior parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode GetCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode GetCachedContentParametersToVertex(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "authConfig" }, - AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "authConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { - Common.SetValueByPath(toObject, new string[] { "enableWidget" }, - Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { - Common.SetValueByPath(toObject, new string[] { "searchTypes" }, - Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { - throw new NotSupportedException( - "blockingConfidence parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { - throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "timeRangeFilter" }, - Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsConfigToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsParametersToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListCachedContentsConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListCachedContentsParametersToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListCachedContentsConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListCachedContentsResponseFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { - Common.SetValueByPath(toObject, new string[] { "cachedContents" }, - Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); - } - - return toObject; - } - - internal JsonNode ListCachedContentsResponseFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { - Common.SetValueByPath(toObject, new string[] { "cachedContents" }, - Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); - } - - return toObject; - } - - internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileData" }, - FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "fileData" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCall" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "inlineData" }, - BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "inlineData" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolCall" }, - Common.GetValueByPath(fromObject, new string[] { "toolCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "toolResponse" }, - Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "partMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); - } - - return toObject; - } - - internal JsonNode PartToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileData" }, - Common.GetValueByPath(fromObject, new string[] { "fileData" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - Common.GetValueByPath(fromObject, new string[] { "functionCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath(toObject, new string[] { "inlineData" }, - Common.GetValueByPath(fromObject, new string[] { "inlineData" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, - Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolCall" }))) { - throw new NotSupportedException("toolCall parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolResponse" }))) { - throw new NotSupportedException("toolResponse parameter is not supported in Vertex AI."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partMetadata" }))) { - throw new NotSupportedException("partMetadata parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "retrievalConfig" }, - Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionCallingConfig" }, - FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "functionCallingConfig" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "includeServerSideToolInvocations" }, - Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); - } - - return toObject; - } - - internal JsonNode ToolConfigToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "retrievalConfig" }, - Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionCallingConfig" }, - Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" })); - } - - if (!Common.IsZero(Common.GetValueByPath( - fromObject, new string[] { "includeServerSideToolInvocations" }))) { - throw new NotSupportedException( - "includeServerSideToolInvocations parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { - throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "computerUse" }, - Common.GetValueByPath(fromObject, new string[] { "computerUse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileSearch" }, - Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearch" }, - GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleSearch" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleMaps" }, - GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "googleMaps" })), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { - throw new NotSupportedException( - "enterpriseWebSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionDeclarations" }, - Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearchRetrieval" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { - throw new NotSupportedException( - "parallelAiSearch parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath(toObject, new string[] { "urlContext" }, - Common.GetValueByPath(fromObject, new string[] { "urlContext" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mcpServers" }, - Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); - } - - return toObject; - } - - internal JsonNode ToolToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "retrieval" }) != null) { - Common.SetValueByPath(toObject, new string[] { "retrieval" }, - Common.GetValueByPath(fromObject, new string[] { "retrieval" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath(toObject, new string[] { "computerUse" }, - Common.GetValueByPath(fromObject, new string[] { "computerUse" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileSearch" }))) { - throw new NotSupportedException("fileSearch parameter is not supported in Vertex AI."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearch" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleMaps" }, - Common.GetValueByPath(fromObject, new string[] { "googleMaps" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "enterpriseWebSearch" }, - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(FunctionDeclarationToVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearchRetrieval" }, - Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parallelAiSearch" }, - Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath(toObject, new string[] { "urlContext" }, - Common.GetValueByPath(fromObject, new string[] { "urlContext" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mcpServers" }))) { - throw new NotSupportedException("mcpServers parameter is not supported in Vertex AI."); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentConfigToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "ttl" }, - Common.GetValueByPath(fromObject, new string[] { "ttl" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = UpdateCachedContentConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode UpdateCachedContentParametersToVertex(ApiClient apiClient, - JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "name" }, - Transformers.TCachedContentName( - this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = UpdateCachedContentConfigToVertex( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - public Caches(ApiClient apiClient) { - _apiClient = apiClient; - } - - public async Task CreateAsync(string model, - CreateCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - CreateCachedContentParameters parameter = new CreateCachedContentParameters(); - - if (!Common.IsZero(model)) { - parameter.Model = model; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse CreateCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = - CreateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } else { - body = - CreateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - public async Task GetAsync(string name, GetCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - GetCachedContentParameters parameter = new GetCachedContentParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GetCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = GetCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - public async Task DeleteAsync( - string name, DeleteCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - DeleteCachedContentParameters parameter = new DeleteCachedContentParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse DeleteCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = - DeleteCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = - DeleteCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = DeleteCachedContentResponseFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = DeleteCachedContentResponseFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task UpdateAsync(string name, - UpdateCachedContentConfig? config = null, - CancellationToken cancellationToken = default) { - UpdateCachedContentParameters parameter = new UpdateCachedContentParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse UpdateCachedContentParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = - UpdateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } else { - body = - UpdateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); - path = Common.FormatMap("{name}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateListAsync( - ListCachedContentsConfig? config, CancellationToken cancellationToken = default) { - ListCachedContentsParameters parameter = new ListCachedContentsParameters(); - - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse ListCachedContentsParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = ListCachedContentsParametersToVertex(parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } else { - body = ListCachedContentsParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("cachedContents", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - responseNode = ListCachedContentsResponseFromVertex(httpContentNode, new JsonObject()); - } - - if (!this._apiClient.VertexAI) { - responseNode = ListCachedContentsResponseFromMldev(httpContentNode, new JsonObject()); - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - /// - /// Lists cached contents asynchronously. - /// - /// A instance that specifies the - /// optional configuration for the list request. The - /// for the request. A Pager object that - /// contains one page of cached contents. When iterating over the pager, it automatically - /// fetches the next page if there are more. - - public async Task> - ListAsync(ListCachedContentsConfig? config = null, - CancellationToken cancellationToken = default) { - config ??= new ListCachedContentsConfig(); - var initialResponse = await PrivateListAsync(config, cancellationToken); - - return new Pager( - requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), - extractItems: response => response.CachedContents, - extractNextPageToken: response => response.NextPageToken, - extractHttpResponse: response => response.SdkHttpResponse, - updateConfigPageToken: (cfg, token) => { - cfg.PageToken = token; - return cfg; - }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + + public sealed class Caches { + private readonly ApiClient _apiClient; + + internal JsonNode AuthConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "apiKey" }) != null) { + Common.SetValueByPath(toObject, new string[] { "apiKey" }, + Common.GetValueByPath(fromObject, new string[] { "apiKey" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "apiKeyConfig" }))) { + throw new NotSupportedException("apiKeyConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "authType" }))) { + throw new NotSupportedException("authType parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "googleServiceAccountConfig" }))) { + throw new NotSupportedException( + "googleServiceAccountConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "httpBasicAuthConfig" }))) { + throw new NotSupportedException( + "httpBasicAuthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oauthConfig" }))) { + throw new NotSupportedException("oauthConfig parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "oidcConfig" }))) { + throw new NotSupportedException("oidcConfig parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { + Common.SetValueByPath(toObject, new string[] { "data" }, + Common.GetValueByPath(fromObject, new string[] { "data" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode ContentToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode CreateCachedContentConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToMldev(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ToolToMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, + ToolConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "toolConfig" })), + toObject)); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }))) { + throw new NotSupportedException("kmsKeyName parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode CreateCachedContentConfigToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "displayName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "displayName" }, + Common.GetValueByPath(fromObject, new string[] { "displayName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contents" }) != null) { + var keyList = + Transformers.TContents(Common.GetValueByPath(fromObject, new string[] { "contents" })); + JsonArray result = new JsonArray(); + + foreach (var record in keyList) { + result.Add(ContentToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "contents" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "systemInstruction" }, + ContentToVertex(Common.ParseToJsonNode(Transformers.TContent(Common.GetValueByPath( + fromObject, new string[] { "systemInstruction" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ToolToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "toolConfig" }, + ToolConfigToVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "toolConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "encryption_spec", "kmsKeyName" }, + Common.GetValueByPath(fromObject, new string[] { "kmsKeyName" })); + } + + return toObject; + } + + internal JsonNode CreateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "model" }, + Transformers.TCachesModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateCachedContentConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode CreateCachedContentParametersToVertex(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "model" }, + Transformers.TCachesModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = CreateCachedContentConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentParametersToVertex(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentResponseFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode DeleteCachedContentResponseFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileUri" }, + Common.GetValueByPath(fromObject, new string[] { "fileUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode FunctionCallToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "id" }) != null) { + Common.SetValueByPath(toObject, new string[] { "id" }, + Common.GetValueByPath(fromObject, new string[] { "id" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "args" }) != null) { + Common.SetValueByPath(toObject, new string[] { "args" }, + Common.GetValueByPath(fromObject, new string[] { "args" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partialArgs" }))) { + throw new NotSupportedException("partialArgs parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "willContinue" }))) { + throw new NotSupportedException("willContinue parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode FunctionCallingConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "allowedFunctionNames" }, + Common.GetValueByPath(fromObject, new string[] { "allowedFunctionNames" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "streamFunctionCallArguments" }))) { + throw new NotSupportedException( + "streamFunctionCallArguments parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode FunctionDeclarationToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { + Common.SetValueByPath(toObject, new string[] { "parameters" }, + Common.GetValueByPath(fromObject, new string[] { "parameters" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parametersJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath(toObject, new string[] { "response" }, + Common.GetValueByPath(fromObject, new string[] { "response" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "behavior" }))) { + throw new NotSupportedException("behavior parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode GetCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode GetCachedContentParametersToVertex(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode GoogleMapsToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "authConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "authConfig" }, + AuthConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "authConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableWidget" }) != null) { + Common.SetValueByPath(toObject, new string[] { "enableWidget" }, + Common.GetValueByPath(fromObject, new string[] { "enableWidget" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "searchTypes" }) != null) { + Common.SetValueByPath(toObject, new string[] { "searchTypes" }, + Common.GetValueByPath(fromObject, new string[] { "searchTypes" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "blockingConfidence" }))) { + throw new NotSupportedException( + "blockingConfidence parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { + throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "timeRangeFilter" }, + Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsConfigToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsParametersToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListCachedContentsConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListCachedContentsParametersToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListCachedContentsConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListCachedContentsResponseFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { + Common.SetValueByPath(toObject, new string[] { "cachedContents" }, + Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); + } + + return toObject; + } + + internal JsonNode ListCachedContentsResponseFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "cachedContents" }) != null) { + Common.SetValueByPath(toObject, new string[] { "cachedContents" }, + Common.GetValueByPath(fromObject, new string[] { "cachedContents" })); + } + + return toObject; + } + + internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileData" }, + FileDataToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "fileData" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + FunctionCallToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCall" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "inlineData" }, + BlobToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "inlineData" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolCall" }, + Common.GetValueByPath(fromObject, new string[] { "toolCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "toolResponse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "toolResponse" }, + Common.GetValueByPath(fromObject, new string[] { "toolResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "partMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "partMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "partMetadata" })); + } + + return toObject; + } + + internal JsonNode PartToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileData" }, + Common.GetValueByPath(fromObject, new string[] { "fileData" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + Common.GetValueByPath(fromObject, new string[] { "functionCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath(toObject, new string[] { "inlineData" }, + Common.GetValueByPath(fromObject, new string[] { "inlineData" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoMetadata" }, + Common.GetValueByPath(fromObject, new string[] { "videoMetadata" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolCall" }))) { + throw new NotSupportedException("toolCall parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "toolResponse" }))) { + throw new NotSupportedException("toolResponse parameter is not supported in Vertex AI."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "partMetadata" }))) { + throw new NotSupportedException("partMetadata parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode ToolConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "retrievalConfig" }, + Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionCallingConfig" }, + FunctionCallingConfigToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "functionCallingConfig" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "includeServerSideToolInvocations" }, + Common.GetValueByPath(fromObject, new string[] { "includeServerSideToolInvocations" })); + } + + return toObject; + } + + internal JsonNode ToolConfigToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "retrievalConfig" }, + Common.GetValueByPath(fromObject, new string[] { "retrievalConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionCallingConfig" }, + Common.GetValueByPath(fromObject, new string[] { "functionCallingConfig" })); + } + + if (!Common.IsZero(Common.GetValueByPath( + fromObject, new string[] { "includeServerSideToolInvocations" }))) { + throw new NotSupportedException( + "includeServerSideToolInvocations parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { + throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "computerUse" }, + Common.GetValueByPath(fromObject, new string[] { "computerUse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileSearch" }, + Common.GetValueByPath(fromObject, new string[] { "fileSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearch" }, + GoogleSearchToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleSearch" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleMaps" }, + GoogleMapsToMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "googleMaps" })), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { + throw new NotSupportedException( + "enterpriseWebSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionDeclarations" }, + Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearchRetrieval" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }))) { + throw new NotSupportedException( + "parallelAiSearch parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath(toObject, new string[] { "urlContext" }, + Common.GetValueByPath(fromObject, new string[] { "urlContext" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mcpServers" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mcpServers" }, + Common.GetValueByPath(fromObject, new string[] { "mcpServers" })); + } + + return toObject; + } + + internal JsonNode ToolToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "retrieval" }) != null) { + Common.SetValueByPath(toObject, new string[] { "retrieval" }, + Common.GetValueByPath(fromObject, new string[] { "retrieval" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath(toObject, new string[] { "computerUse" }, + Common.GetValueByPath(fromObject, new string[] { "computerUse" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "fileSearch" }))) { + throw new NotSupportedException("fileSearch parameter is not supported in Vertex AI."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearch" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleMaps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleMaps" }, + Common.GetValueByPath(fromObject, new string[] { "googleMaps" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "enterpriseWebSearch" }, + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(FunctionDeclarationToVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearchRetrieval" }, + Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parallelAiSearch" }, + Common.GetValueByPath(fromObject, new string[] { "parallelAiSearch" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath(toObject, new string[] { "urlContext" }, + Common.GetValueByPath(fromObject, new string[] { "urlContext" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "mcpServers" }))) { + throw new NotSupportedException("mcpServers parameter is not supported in Vertex AI."); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentConfigToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "ttl" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "ttl" }, + Common.GetValueByPath(fromObject, new string[] { "ttl" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentParametersToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = UpdateCachedContentConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode UpdateCachedContentParametersToVertex(ApiClient apiClient, + JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "name" }, + Transformers.TCachedContentName( + this._apiClient, Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = UpdateCachedContentConfigToVertex( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + public Caches(ApiClient apiClient) { + _apiClient = apiClient; + } + + public async Task CreateAsync(string model, + CreateCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + CreateCachedContentParameters parameter = new CreateCachedContentParameters(); + + if (!Common.IsZero(model)) { + parameter.Model = model; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse CreateCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = + CreateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } else { + body = + CreateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + public async Task GetAsync(string name, GetCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + GetCachedContentParameters parameter = new GetCachedContentParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GetCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = GetCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + public async Task DeleteAsync( + string name, DeleteCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + DeleteCachedContentParameters parameter = new DeleteCachedContentParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse DeleteCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = + DeleteCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = + DeleteCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = DeleteCachedContentResponseFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = DeleteCachedContentResponseFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task UpdateAsync(string name, + UpdateCachedContentConfig? config = null, + CancellationToken cancellationToken = default) { + UpdateCachedContentParameters parameter = new UpdateCachedContentParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse UpdateCachedContentParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = + UpdateCachedContentParametersToVertex(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } else { + body = + UpdateCachedContentParametersToMldev(this._apiClient, parameterNode, new JsonObject()); + path = Common.FormatMap("{name}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateListAsync( + ListCachedContentsConfig? config, CancellationToken cancellationToken = default) { + ListCachedContentsParameters parameter = new ListCachedContentsParameters(); + + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse ListCachedContentsParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = ListCachedContentsParametersToVertex(parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } else { + body = ListCachedContentsParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("cachedContents", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + responseNode = ListCachedContentsResponseFromVertex(httpContentNode, new JsonObject()); + } + + if (!this._apiClient.VertexAI) { + responseNode = ListCachedContentsResponseFromMldev(httpContentNode, new JsonObject()); + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + /// + /// Lists cached contents asynchronously. + /// + /// A instance that specifies the + /// optional configuration for the list request. The + /// for the request. A Pager object that + /// contains one page of cached contents. When iterating over the pager, it automatically + /// fetches the next page if there are more. + + public async Task> + ListAsync(ListCachedContentsConfig? config = null, + CancellationToken cancellationToken = default) { + config ??= new ListCachedContentsConfig(); + var initialResponse = await PrivateListAsync(config, cancellationToken); + + return new Pager( + requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), + extractItems: response => response.CachedContents, + extractNextPageToken: response => response.NextPageToken, + extractHttpResponse: response => response.SdkHttpResponse, + updateConfigPageToken: (cfg, token) => { + cfg.PageToken = token; + return cfg; + }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); + } + } +} diff --git a/Google.GenAI/Files.cs b/Google.GenAI/Files.cs index a04369d3..fc716c2a 100644 --- a/Google.GenAI/Files.cs +++ b/Google.GenAI/Files.cs @@ -1,915 +1,915 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -using System.IO; -using System.Collections.Generic; -using Google.Apis.Auth.OAuth2; - -namespace Google.GenAI { - - public sealed class Files { - private readonly UploadClient _uploadClient; - private readonly ApiClient _apiClient; - - internal JsonNode CreateFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "file" }) != null) { - Common.SetValueByPath(toObject, new string[] { "file" }, - Common.GetValueByPath(fromObject, new string[] { "file" })); - } - - return toObject; - } - - internal JsonNode CreateFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode DeleteFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "file" }, - Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode DeleteFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - return toObject; - } - - internal JsonNode GetFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "_url", "file" }, - Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); - } - - return toObject; - } - - internal JsonNode InternalRegisterFilesParametersToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "uris" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uris" }, - Common.GetValueByPath(fromObject, new string[] { "uris" })); - } - - return toObject; - } - - internal JsonNode ListFilesConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, - Common.GetValueByPath(fromObject, new string[] { "pageSize" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, - Common.GetValueByPath(fromObject, new string[] { "pageToken" })); - } - - return toObject; - } - - internal JsonNode ListFilesParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - _ = ListFilesConfigToMldev( - Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), - toObject); - } - - return toObject; - } - - internal JsonNode ListFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { - Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, - Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { - Common.SetValueByPath(toObject, new string[] { "files" }, - Common.GetValueByPath(fromObject, new string[] { "files" })); - } - - return toObject; - } - - internal JsonNode RegisterFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "sdkHttpResponse" }, - Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { - Common.SetValueByPath(toObject, new string[] { "files" }, - Common.GetValueByPath(fromObject, new string[] { "files" })); - } - - return toObject; - } - - public Files(ApiClient apiClient) { - { - _apiClient = apiClient; - _uploadClient = new UploadClient(_apiClient); - } - } - - private async Task PrivateListAsync( - ListFilesConfig? config, CancellationToken cancellationToken = default) { - ListFilesParameters parameter = new ListFilesParameters(); - - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse ListFilesParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = ListFilesParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateCreateAsync( - Google.GenAI.Types.File file, CreateFileConfig? config, - CancellationToken cancellationToken = default) { - CreateFileParameters parameter = new CreateFileParameters(); - - if (!Common.IsZero(file)) { - parameter.File = file; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse CreateFileParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = CreateFileParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("upload/v1beta/files", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - - if (config?.ShouldReturnHttpResponse == true) { - var httpHeaders = response.GetHeaders(); - Dictionary? headers = null; - - if (httpHeaders != null) { - headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var header in httpHeaders) { - headers[header.Key] = string.Join(", ", header.Value); - } - } - - return new CreateFileResponse { - SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } - }; - } - - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - public async Task GetAsync( - string name, GetFileConfig? config = null, CancellationToken cancellationToken = default) { - GetFileParameters parameter = new GetFileParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetFileParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = GetFileParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files/{file}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task DeleteAsync( - string name, DeleteFileConfig? config = null, - CancellationToken cancellationToken = default) { - DeleteFileParameters parameter = new DeleteFileParameters(); - - if (!Common.IsZero(name)) { - parameter.Name = name; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse DeleteFileParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = DeleteFileParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files/{file}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, - cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException("Failed to deserialize Task."); - } - - private async Task PrivateRegisterFilesAsync( - List uris, RegisterFilesConfig? config, - CancellationToken cancellationToken = default) { - InternalRegisterFilesParameters parameter = new InternalRegisterFilesParameters(); - - if (!Common.IsZero(uris)) { - parameter.Uris = uris; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse InternalRegisterFilesParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } else { - body = InternalRegisterFilesParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("files:register", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - - if (config?.ShouldReturnHttpResponse == true) { - var httpHeaders = response.GetHeaders(); - Dictionary? headers = null; - - if (httpHeaders != null) { - headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var header in httpHeaders) { - headers[header.Key] = string.Join(", ", header.Value); - } - } - - return new RegisterFilesResponse { - SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } - }; - } - - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - - if (!this._apiClient.VertexAI) { - responseNode = httpContentNode; - } - - return responseNode.Deserialize() ?? - throw new InvalidOperationException( - "Failed to deserialize Task."); - } - - public async Task> ListAsync( - ListFilesConfig? config = null, CancellationToken cancellationToken = default) { - config ??= new ListFilesConfig(); - var initialResponse = await PrivateListAsync(config, cancellationToken); - - return new Pager( - requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), - extractItems: response => response.Files, - extractNextPageToken: response => response.NextPageToken, - extractHttpResponse: response => response.SdkHttpResponse, - updateConfigPageToken: (cfg, token) => { - cfg.PageToken = token; - return cfg; - }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); - } - - /// - /// Registers Google Cloud Storage files for use with the API. - /// - /// The list of GCS URIs to register. - /// The to use for - /// authorization. A instance - /// that specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task - /// result contains the . - public async Task RegisterFilesAsync( - IEnumerable uris, GoogleCredential credential, RegisterFilesConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (uris == null) - throw new ArgumentNullException(nameof(uris)); - if (credential == null) - throw new ArgumentNullException(nameof(credential)); - - ICredential cred = (ICredential)credential; - string accessToken = - await cred.GetAccessTokenForRequestAsync(cancellationToken: cancellationToken); - if (string.IsNullOrEmpty(accessToken)) { - throw new InvalidOperationException("Failed to obtain access token from credentials."); - } - - var localConfig = config ?? new RegisterFilesConfig(); - var httpOptions = localConfig.HttpOptions ?? new HttpOptions(); - var headers = httpOptions.Headers != null - ? new Dictionary(httpOptions.Headers, - StringComparer.OrdinalIgnoreCase) - : new Dictionary(StringComparer.OrdinalIgnoreCase); - - headers["Authorization"] = $"Bearer {accessToken}"; - if (!string.IsNullOrEmpty(credential.QuotaProject)) { - headers["x-goog-user-project"] = credential.QuotaProject; - } - - // Create an effective configuration with updated headers without mutating the input object. - var effectiveConfig = - localConfig with { HttpOptions = httpOptions with { Headers = headers } }; - - return await PrivateRegisterFilesAsync(uris.ToList(), effectiveConfig, cancellationToken); - } - - /// - /// Uploads a file from a file path. - /// - /// The path to the file to upload. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains the uploaded - /// metadata. - public async Task UploadAsync( - string filePath, UploadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - var fileInfo = new FileInfo(filePath); - using var stream = fileInfo.OpenRead(); - string mimeType = MimeTypes.GetMimeType(Path.GetExtension(filePath)); - - return await UploadAsync(stream, fileInfo.Length, fileInfo.Name, mimeType, config, - cancellationToken); - } - - /// - /// Uploads a file from a byte array. - /// - /// The file content as a byte array. - /// Optional file name to use. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains the uploaded - /// metadata. - public async Task UploadAsync( - byte[] bytes, string? fileName = null, UploadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - using var stream = new MemoryStream(bytes); - return await UploadAsync(stream, bytes.Length, fileName, null, config, cancellationToken); - } - - /// - /// Uploads a file from a stream. - /// - /// The stream containing the file data. - /// The size of the file in bytes. - /// Optional file name to use. - /// Optional MIME type. If not provided, defaults to - /// application/octet-stream. A - /// instance that specifies the optional configurations. A to cancel the operation. - /// A that represents the asynchronous operation. The task - /// result contains the uploaded metadata. - public async Task UploadAsync( - Stream stream, long size, string? fileName = null, string? mimeType = null, - UploadFileConfig? config = null, CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - string uploadUrl = - await CreateFileInApiAsync(config, mimeType, fileName, size, cancellationToken); - HttpContent responseContent = await _uploadClient.UploadAsync( - uploadUrl, stream, size, config?.HttpOptions, cancellationToken); - return await FileFromUploadResponseBodyAsync(responseContent); - } - - /// - /// Downloads a file and returns it as a . Caller is responsible for - /// disposing the returned stream. - /// - /// The name of the file to download (e.g., "files/abc123" or - /// "abc123"). A instance that - /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task result contains a - /// with the file data. - public async Task DownloadAsync(string fileName, DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - string extractedFileName = Transformers.TFileName(fileName); - return await DownloadStreamAsync(extractedFileName, config, cancellationToken); - } - - /// - /// Downloads a object and returns it as a . Caller is - /// responsible for disposing the returned stream. - /// - /// The object to download. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains a with the file - /// data. - public async Task DownloadAsync(Google.GenAI.Types.File file, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(file.Name)) - throw new ArgumentException("File.Name is required", nameof(file)); - - return await DownloadAsync(file.Name, config, cancellationToken); - } - - /// - /// Downloads a object and returns it as a . Caller is - /// responsible for disposing the returned stream. - /// - /// The object to download. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains a with the file - /// data. - public async Task DownloadAsync(Video video, DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(video.Uri)) - throw new ArgumentException("Video.Uri is required", nameof(video)); - - return await DownloadAsync(video.Uri, config, cancellationToken); - } - - /// - /// Downloads a object and returns it as a . - /// Caller is responsible for disposing the returned stream. - /// - /// The object to download. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the - /// asynchronous operation. The task result contains a with the file - /// data. - public async Task DownloadAsync(GeneratedVideo generatedVideo, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (generatedVideo.Video == null) - throw new ArgumentException("Video is empty", nameof(generatedVideo)); - - return await DownloadAsync(generatedVideo.Video, config, cancellationToken); - } - - /// - /// Downloads a file directly to a file path. - /// - /// The name of the file to download (e.g., "files/abc123" or - /// "abc123"). The path where the file should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(string fileName, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - using var stream = await DownloadAsync(fileName, config, cancellationToken); - using var fileStream = - new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None, - bufferSize: UploadClient.DEFAULT_CHUNK_SIZE, useAsync: true); - -#if NETSTANDARD2_0 - await stream.CopyToAsync(fileStream, bufferSize: 81920, cancellationToken); -#else - await stream.CopyToAsync(fileStream, cancellationToken); -#endif - } - - /// - /// Downloads a object directly to a file path. - /// - /// The object to download. - /// The path where the file should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(Google.GenAI.Types.File file, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(file.Name)) - throw new ArgumentException("Google.GenAI.Types.File.Name is required", nameof(file)); - - await DownloadToFileAsync(file.Name, outputPath, config, cancellationToken); - } - - /// - /// Downloads a object directly to a file path. - /// - /// The object to download. - /// The path where the Video should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(GeneratedVideo generatedVideo, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (generatedVideo.Video == null) - throw new ArgumentException("Video is empty", nameof(generatedVideo)); - - await DownloadToFileAsync(generatedVideo.Video, outputPath, config, cancellationToken); - } - - /// - /// Downloads a object directly to a file path. - /// - /// The object to download. - /// The path where the Video should be saved. - /// A instance that specifies the optional - /// configurations. A to - /// cancel the operation. A that represents the asynchronous - /// operation. - public async Task DownloadToFileAsync(Video video, string outputPath, - DownloadFileConfig? config = null, - CancellationToken cancellationToken = default) { - if (this._apiClient.VertexAI) { - throw new NotSupportedException( - "This method is only supported in the Gemini Developer API client."); - } - if (string.IsNullOrEmpty(video.Uri)) - throw new ArgumentException("Video.Uri is required", nameof(video)); - - await DownloadToFileAsync(video.Uri, outputPath, config, cancellationToken); - } - - private async Task FileFromUploadResponseBodyAsync( - HttpContent responseContent) { - string responseString = await responseContent.ReadAsStringAsync(); - JsonNode? responseNode = JsonNode.Parse(responseString); - - if (responseNode?["file"] is not JsonNode fileNode) { - throw new InvalidOperationException("Upload response does not contain file object"); - } - - return JsonSerializer.Deserialize(fileNode.ToString(), JsonConfig.JsonSerializerOptions) ?? - throw new InvalidOperationException("Failed to deserialize File"); - } - - private async Task CreateFileInApiAsync(UploadFileConfig? config, string? mimeType, - string? fileName, long size, - CancellationToken cancellationToken = default) { - var fileBuilder = new Google.GenAI.Types.File(); - - if (config != null) { - if (!string.IsNullOrEmpty(config.Name)) { - fileBuilder.Name = - config.Name.StartsWith("files/") ? config.Name : $"files/{config.Name}"; - } - - mimeType = config.MimeType ?? mimeType; - - if (!string.IsNullOrEmpty(config.DisplayName)) { - fileBuilder.DisplayName = config.DisplayName; - } - } - - var createFileHttpOptions = UploadClient.BuildResumableUploadHttpOptions( - config?.HttpOptions, mimeType, fileName, size); - - var createFileConfig = new CreateFileConfig { HttpOptions = createFileHttpOptions, - ShouldReturnHttpResponse = true }; - - var createFileResponse = - await PrivateCreateAsync(fileBuilder, createFileConfig, cancellationToken); - - string errorMessage = "Upload URL not found in create file response"; - if (createFileResponse.SdkHttpResponse?.Headers == null) { - throw new InvalidOperationException(errorMessage); - } - var headers = new Dictionary(createFileResponse.SdkHttpResponse.Headers, - StringComparer.OrdinalIgnoreCase); - if (!headers.TryGetValue("x-goog-upload-url", out string? uploadUrl)) { - throw new InvalidOperationException(errorMessage); - } - return uploadUrl; - } - - private async Task DownloadStreamAsync(string fileName, DownloadFileConfig? config, - CancellationToken cancellationToken = default) { - string path = $"files/{fileName}:download?alt=media"; - var response = await _apiClient.RequestAsync(HttpMethod.Get, path, "", config?.HttpOptions, - cancellationToken); -#if NETSTANDARD2_0 - return await response.GetEntity().ReadAsStreamAsync(); -#else - return await response.GetEntity().ReadAsStreamAsync(cancellationToken); -#endif - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +using System.IO; +using System.Collections.Generic; +using Google.Apis.Auth.OAuth2; + +namespace Google.GenAI { + + public sealed class Files { + private readonly UploadClient _uploadClient; + private readonly ApiClient _apiClient; + + internal JsonNode CreateFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "file" }) != null) { + Common.SetValueByPath(toObject, new string[] { "file" }, + Common.GetValueByPath(fromObject, new string[] { "file" })); + } + + return toObject; + } + + internal JsonNode CreateFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode DeleteFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "file" }, + Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode DeleteFileResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + return toObject; + } + + internal JsonNode GetFileParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "_url", "file" }, + Transformers.TFileName(Common.GetValueByPath(fromObject, new string[] { "name" }))); + } + + return toObject; + } + + internal JsonNode InternalRegisterFilesParametersToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "uris" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uris" }, + Common.GetValueByPath(fromObject, new string[] { "uris" })); + } + + return toObject; + } + + internal JsonNode ListFilesConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "pageSize" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageSize" }, + Common.GetValueByPath(fromObject, new string[] { "pageSize" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "pageToken" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "_query", "pageToken" }, + Common.GetValueByPath(fromObject, new string[] { "pageToken" })); + } + + return toObject; + } + + internal JsonNode ListFilesParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + _ = ListFilesConfigToMldev( + Common.ParseToJsonNode(Common.GetValueByPath(fromObject, new string[] { "config" })), + toObject); + } + + return toObject; + } + + internal JsonNode ListFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "nextPageToken" }) != null) { + Common.SetValueByPath(toObject, new string[] { "nextPageToken" }, + Common.GetValueByPath(fromObject, new string[] { "nextPageToken" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { + Common.SetValueByPath(toObject, new string[] { "files" }, + Common.GetValueByPath(fromObject, new string[] { "files" })); + } + + return toObject; + } + + internal JsonNode RegisterFilesResponseFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "sdkHttpResponse" }, + Common.GetValueByPath(fromObject, new string[] { "sdkHttpResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "files" }) != null) { + Common.SetValueByPath(toObject, new string[] { "files" }, + Common.GetValueByPath(fromObject, new string[] { "files" })); + } + + return toObject; + } + + public Files(ApiClient apiClient) { + { + _apiClient = apiClient; + _uploadClient = new UploadClient(_apiClient); + } + } + + private async Task PrivateListAsync( + ListFilesConfig? config, CancellationToken cancellationToken = default) { + ListFilesParameters parameter = new ListFilesParameters(); + + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse ListFilesParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = ListFilesParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateCreateAsync( + Google.GenAI.Types.File file, CreateFileConfig? config, + CancellationToken cancellationToken = default) { + CreateFileParameters parameter = new CreateFileParameters(); + + if (!Common.IsZero(file)) { + parameter.File = file; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse CreateFileParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = CreateFileParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("upload/v1beta/files", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + + if (config?.ShouldReturnHttpResponse == true) { + var httpHeaders = response.GetHeaders(); + Dictionary? headers = null; + + if (httpHeaders != null) { + headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var header in httpHeaders) { + headers[header.Key] = string.Join(", ", header.Value); + } + } + + return new CreateFileResponse { + SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } + }; + } + + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + public async Task GetAsync( + string name, GetFileConfig? config = null, CancellationToken cancellationToken = default) { + GetFileParameters parameter = new GetFileParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetFileParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = GetFileParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files/{file}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task DeleteAsync( + string name, DeleteFileConfig? config = null, + CancellationToken cancellationToken = default) { + DeleteFileParameters parameter = new DeleteFileParameters(); + + if (!Common.IsZero(name)) { + parameter.Name = name; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse DeleteFileParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = DeleteFileParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files/{file}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = await this._apiClient.RequestAsync( + HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, + cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException("Failed to deserialize Task."); + } + + private async Task PrivateRegisterFilesAsync( + List uris, RegisterFilesConfig? config, + CancellationToken cancellationToken = default) { + InternalRegisterFilesParameters parameter = new InternalRegisterFilesParameters(); + + if (!Common.IsZero(uris)) { + parameter.Uris = uris; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse InternalRegisterFilesParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } else { + body = InternalRegisterFilesParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("files:register", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + + if (config?.ShouldReturnHttpResponse == true) { + var httpHeaders = response.GetHeaders(); + Dictionary? headers = null; + + if (httpHeaders != null) { + headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var header in httpHeaders) { + headers[header.Key] = string.Join(", ", header.Value); + } + } + + return new RegisterFilesResponse { + SdkHttpResponse = new HttpResponse { Headers = headers, Body = contentString } + }; + } + + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + + if (!this._apiClient.VertexAI) { + responseNode = httpContentNode; + } + + return responseNode.Deserialize() ?? + throw new InvalidOperationException( + "Failed to deserialize Task."); + } + + public async Task> ListAsync( + ListFilesConfig? config = null, CancellationToken cancellationToken = default) { + config ??= new ListFilesConfig(); + var initialResponse = await PrivateListAsync(config, cancellationToken); + + return new Pager( + requestFunc: async cfg => await PrivateListAsync(cfg, cancellationToken), + extractItems: response => response.Files, + extractNextPageToken: response => response.NextPageToken, + extractHttpResponse: response => response.SdkHttpResponse, + updateConfigPageToken: (cfg, token) => { + cfg.PageToken = token; + return cfg; + }, initialConfig: config, initialResponse: initialResponse, requestedPageSize: config.PageSize ?? 0); + } + + /// + /// Registers Google Cloud Storage files for use with the API. + /// + /// The list of GCS URIs to register. + /// The to use for + /// authorization. A instance + /// that specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task + /// result contains the . + public async Task RegisterFilesAsync( + IEnumerable uris, GoogleCredential credential, RegisterFilesConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (uris == null) + throw new ArgumentNullException(nameof(uris)); + if (credential == null) + throw new ArgumentNullException(nameof(credential)); + + ICredential cred = (ICredential)credential; + string accessToken = + await cred.GetAccessTokenForRequestAsync(cancellationToken: cancellationToken); + if (string.IsNullOrEmpty(accessToken)) { + throw new InvalidOperationException("Failed to obtain access token from credentials."); + } + + var localConfig = config ?? new RegisterFilesConfig(); + var httpOptions = localConfig.HttpOptions ?? new HttpOptions(); + var headers = httpOptions.Headers != null + ? new Dictionary(httpOptions.Headers, + StringComparer.OrdinalIgnoreCase) + : new Dictionary(StringComparer.OrdinalIgnoreCase); + + headers["Authorization"] = $"Bearer {accessToken}"; + if (!string.IsNullOrEmpty(credential.QuotaProject)) { + headers["x-goog-user-project"] = credential.QuotaProject; + } + + // Create an effective configuration with updated headers without mutating the input object. + var effectiveConfig = + localConfig with { HttpOptions = httpOptions with { Headers = headers } }; + + return await PrivateRegisterFilesAsync(uris.ToList(), effectiveConfig, cancellationToken); + } + + /// + /// Uploads a file from a file path. + /// + /// The path to the file to upload. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains the uploaded + /// metadata. + public async Task UploadAsync( + string filePath, UploadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + var fileInfo = new FileInfo(filePath); + using var stream = fileInfo.OpenRead(); + string mimeType = MimeTypes.GetMimeType(Path.GetExtension(filePath)); + + return await UploadAsync(stream, fileInfo.Length, fileInfo.Name, mimeType, config, + cancellationToken); + } + + /// + /// Uploads a file from a byte array. + /// + /// The file content as a byte array. + /// Optional file name to use. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains the uploaded + /// metadata. + public async Task UploadAsync( + byte[] bytes, string? fileName = null, UploadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + using var stream = new MemoryStream(bytes); + return await UploadAsync(stream, bytes.Length, fileName, null, config, cancellationToken); + } + + /// + /// Uploads a file from a stream. + /// + /// The stream containing the file data. + /// The size of the file in bytes. + /// Optional file name to use. + /// Optional MIME type. If not provided, defaults to + /// application/octet-stream. A + /// instance that specifies the optional configurations. A to cancel the operation. + /// A that represents the asynchronous operation. The task + /// result contains the uploaded metadata. + public async Task UploadAsync( + Stream stream, long size, string? fileName = null, string? mimeType = null, + UploadFileConfig? config = null, CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + string uploadUrl = + await CreateFileInApiAsync(config, mimeType, fileName, size, cancellationToken); + HttpContent responseContent = await _uploadClient.UploadAsync( + uploadUrl, stream, size, config?.HttpOptions, cancellationToken); + return await FileFromUploadResponseBodyAsync(responseContent); + } + + /// + /// Downloads a file and returns it as a . Caller is responsible for + /// disposing the returned stream. + /// + /// The name of the file to download (e.g., "files/abc123" or + /// "abc123"). A instance that + /// specifies the optional configurations. A to cancel the operation. A that represents the asynchronous operation. The task result contains a + /// with the file data. + public async Task DownloadAsync(string fileName, DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + string extractedFileName = Transformers.TFileName(fileName); + return await DownloadStreamAsync(extractedFileName, config, cancellationToken); + } + + /// + /// Downloads a object and returns it as a . Caller is + /// responsible for disposing the returned stream. + /// + /// The object to download. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains a with the file + /// data. + public async Task DownloadAsync(Google.GenAI.Types.File file, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(file.Name)) + throw new ArgumentException("File.Name is required", nameof(file)); + + return await DownloadAsync(file.Name, config, cancellationToken); + } + + /// + /// Downloads a object and returns it as a . Caller is + /// responsible for disposing the returned stream. + /// + /// The object to download. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains a with the file + /// data. + public async Task DownloadAsync(Video video, DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(video.Uri)) + throw new ArgumentException("Video.Uri is required", nameof(video)); + + return await DownloadAsync(video.Uri, config, cancellationToken); + } + + /// + /// Downloads a object and returns it as a . + /// Caller is responsible for disposing the returned stream. + /// + /// The object to download. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the + /// asynchronous operation. The task result contains a with the file + /// data. + public async Task DownloadAsync(GeneratedVideo generatedVideo, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (generatedVideo.Video == null) + throw new ArgumentException("Video is empty", nameof(generatedVideo)); + + return await DownloadAsync(generatedVideo.Video, config, cancellationToken); + } + + /// + /// Downloads a file directly to a file path. + /// + /// The name of the file to download (e.g., "files/abc123" or + /// "abc123"). The path where the file should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(string fileName, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + using var stream = await DownloadAsync(fileName, config, cancellationToken); + using var fileStream = + new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None, + bufferSize: UploadClient.DEFAULT_CHUNK_SIZE, useAsync: true); + +#if NETSTANDARD2_0 + await stream.CopyToAsync(fileStream, bufferSize: 81920, cancellationToken); +#else + await stream.CopyToAsync(fileStream, cancellationToken); +#endif + } + + /// + /// Downloads a object directly to a file path. + /// + /// The object to download. + /// The path where the file should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(Google.GenAI.Types.File file, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(file.Name)) + throw new ArgumentException("Google.GenAI.Types.File.Name is required", nameof(file)); + + await DownloadToFileAsync(file.Name, outputPath, config, cancellationToken); + } + + /// + /// Downloads a object directly to a file path. + /// + /// The object to download. + /// The path where the Video should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(GeneratedVideo generatedVideo, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (generatedVideo.Video == null) + throw new ArgumentException("Video is empty", nameof(generatedVideo)); + + await DownloadToFileAsync(generatedVideo.Video, outputPath, config, cancellationToken); + } + + /// + /// Downloads a object directly to a file path. + /// + /// The object to download. + /// The path where the Video should be saved. + /// A instance that specifies the optional + /// configurations. A to + /// cancel the operation. A that represents the asynchronous + /// operation. + public async Task DownloadToFileAsync(Video video, string outputPath, + DownloadFileConfig? config = null, + CancellationToken cancellationToken = default) { + if (this._apiClient.VertexAI) { + throw new NotSupportedException( + "This method is only supported in the Gemini Developer API client."); + } + if (string.IsNullOrEmpty(video.Uri)) + throw new ArgumentException("Video.Uri is required", nameof(video)); + + await DownloadToFileAsync(video.Uri, outputPath, config, cancellationToken); + } + + private async Task FileFromUploadResponseBodyAsync( + HttpContent responseContent) { + string responseString = await responseContent.ReadAsStringAsync(); + JsonNode? responseNode = JsonNode.Parse(responseString); + + if (responseNode?["file"] is not JsonNode fileNode) { + throw new InvalidOperationException("Upload response does not contain file object"); + } + + return JsonSerializer.Deserialize(fileNode.ToString()) ?? + throw new InvalidOperationException("Failed to deserialize File"); + } + + private async Task CreateFileInApiAsync(UploadFileConfig? config, string? mimeType, + string? fileName, long size, + CancellationToken cancellationToken = default) { + var fileBuilder = new Google.GenAI.Types.File(); + + if (config != null) { + if (!string.IsNullOrEmpty(config.Name)) { + fileBuilder.Name = + config.Name.StartsWith("files/") ? config.Name : $"files/{config.Name}"; + } + + mimeType = config.MimeType ?? mimeType; + + if (!string.IsNullOrEmpty(config.DisplayName)) { + fileBuilder.DisplayName = config.DisplayName; + } + } + + var createFileHttpOptions = UploadClient.BuildResumableUploadHttpOptions( + config?.HttpOptions, mimeType, fileName, size); + + var createFileConfig = new CreateFileConfig { HttpOptions = createFileHttpOptions, + ShouldReturnHttpResponse = true }; + + var createFileResponse = + await PrivateCreateAsync(fileBuilder, createFileConfig, cancellationToken); + + string errorMessage = "Upload URL not found in create file response"; + if (createFileResponse.SdkHttpResponse?.Headers == null) { + throw new InvalidOperationException(errorMessage); + } + var headers = new Dictionary(createFileResponse.SdkHttpResponse.Headers, + StringComparer.OrdinalIgnoreCase); + if (!headers.TryGetValue("x-goog-upload-url", out string? uploadUrl)) { + throw new InvalidOperationException(errorMessage); + } + return uploadUrl; + } + + private async Task DownloadStreamAsync(string fileName, DownloadFileConfig? config, + CancellationToken cancellationToken = default) { + string path = $"files/{fileName}:download?alt=media"; + var response = await _apiClient.RequestAsync(HttpMethod.Get, path, "", config?.HttpOptions, + cancellationToken); +#if NETSTANDARD2_0 + return await response.GetEntity().ReadAsStreamAsync(); +#else + return await response.GetEntity().ReadAsStreamAsync(cancellationToken); +#endif + } + } +} diff --git a/Google.GenAI/Models.cs b/Google.GenAI/Models.cs index c8aad071..8eaae01f 100644 --- a/Google.GenAI/Models.cs +++ b/Google.GenAI/Models.cs @@ -4541,7 +4541,7 @@ private async Task PrivateGenerateContentAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse GenerateContentParameters to JsonNode."); @@ -4569,7 +4569,7 @@ private async Task PrivateGenerateContentAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -4612,7 +4612,7 @@ private async IAsyncEnumerable PrivateGenerateContentSt if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse GenerateContentParameters to JsonNode."); @@ -4640,7 +4640,7 @@ private async IAsyncEnumerable PrivateGenerateContentSt HttpOptions? requestHttpOptions = config?.HttpOptions; await foreach (ApiResponse apiResponse in this._apiClient.RequestStreamAsync( - HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken)) { #if NETSTANDARD2_0 string chunkJson = await apiResponse.GetEntity().ReadAsStringAsync(); @@ -4697,7 +4697,7 @@ private async Task PrivateEmbedContentAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException( @@ -4730,7 +4730,7 @@ private async Task PrivateEmbedContentAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -4773,7 +4773,7 @@ private async Task PrivateGenerateImagesAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse GenerateImagesParameters to JsonNode."); @@ -4801,7 +4801,7 @@ private async Task PrivateGenerateImagesAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -4847,7 +4847,7 @@ private async Task PrivateEditImageAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse EditImageParameters to JsonNode."); @@ -4873,7 +4873,7 @@ private async Task PrivateEditImageAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -4917,7 +4917,7 @@ private async Task PrivateUpscaleImageAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse UpscaleImageAPIParameters to JsonNode."); @@ -4943,7 +4943,7 @@ private async Task PrivateUpscaleImageAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -4985,7 +4985,7 @@ public async Task RecontextImageAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse RecontextImageParameters to JsonNode."); @@ -5011,7 +5011,7 @@ public async Task RecontextImageAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5053,7 +5053,7 @@ public async Task SegmentImageAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse SegmentImageParameters to JsonNode."); @@ -5079,7 +5079,7 @@ public async Task SegmentImageAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5121,7 +5121,7 @@ public async Task GetAsync(string model, GetModelConfig? config = null, if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse GetModelParameters to JsonNode."); @@ -5149,7 +5149,7 @@ public async Task GetAsync(string model, GetModelConfig? config = null, HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5182,7 +5182,7 @@ private async Task PrivateListAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse ListModelsParameters to JsonNode."); @@ -5210,7 +5210,7 @@ private async Task PrivateListAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5258,7 +5258,7 @@ public async Task UpdateAsync(string model, UpdateModelConfig? config = n if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse UpdateModelParameters to JsonNode."); @@ -5286,7 +5286,7 @@ public async Task UpdateAsync(string model, UpdateModelConfig? config = n HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = await this._apiClient.RequestAsync( - new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + new HttpMethod("PATCH"), path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5327,7 +5327,7 @@ public async Task DeleteAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse DeleteModelParameters to JsonNode."); @@ -5355,7 +5355,7 @@ public async Task DeleteAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = await this._apiClient.RequestAsync( - HttpMethod.Delete, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), requestHttpOptions, + HttpMethod.Delete, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5410,7 +5410,7 @@ public async Task CountTokensAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse CountTokensParameters to JsonNode."); @@ -5438,7 +5438,7 @@ public async Task CountTokensAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5494,7 +5494,7 @@ public async Task ComputeTokensAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse ComputeTokensParameters to JsonNode."); @@ -5520,7 +5520,7 @@ public async Task ComputeTokensAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -5571,7 +5571,7 @@ private async Task PrivateGenerateVideosAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse GenerateVideosParameters to JsonNode."); @@ -5599,7 +5599,7 @@ private async Task PrivateGenerateVideosAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 diff --git a/Google.GenAI/Operations.cs b/Google.GenAI/Operations.cs index 1df64148..ef278f28 100644 --- a/Google.GenAI/Operations.cs +++ b/Google.GenAI/Operations.cs @@ -1,426 +1,426 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - - public sealed class Operations { - private readonly ApiClient _apiClient; - - internal JsonNode FetchPredictOperationParametersToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "operationName" }, - Common.GetValueByPath(fromObject, new string[] { "operationName" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "resourceName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "resourceName" }, - Common.GetValueByPath(fromObject, new string[] { "resourceName" })); - } - - return toObject; - } - - internal JsonNode GenerateVideosOperationFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response", "generateVideoResponse" }) != - null) { - Common.SetValueByPath( - toObject, new string[] { "response" }, - GenerateVideosResponseFromMldev( - Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "response", "generateVideoResponse" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosOperationFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { - Common.SetValueByPath(toObject, new string[] { "metadata" }, - Common.GetValueByPath(fromObject, new string[] { "metadata" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { - Common.SetValueByPath(toObject, new string[] { "done" }, - Common.GetValueByPath(fromObject, new string[] { "done" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { - Common.SetValueByPath(toObject, new string[] { "error" }, - Common.GetValueByPath(fromObject, new string[] { "error" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "response" }, - GenerateVideosResponseFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "response" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GenerateVideosResponseFromMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(GeneratedVideoFromMldev(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredCount" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredReasons" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); - } - - return toObject; - } - - internal JsonNode GenerateVideosResponseFromVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "videos" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "videos" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(GeneratedVideoFromVertex(Common.ParseToJsonNode(record), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredCount" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "raiMediaFilteredReasons" }, - Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); - } - - return toObject; - } - - internal JsonNode GeneratedVideoFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { - Common.SetValueByPath(toObject, new string[] { "video" }, - VideoFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "video" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GeneratedVideoFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { - Common.SetValueByPath(toObject, new string[] { "video" }, - VideoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( - fromObject, new string[] { "_self" })), - toObject)); - } - - return toObject; - } - - internal JsonNode GetOperationParametersToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, - Common.GetValueByPath(fromObject, new string[] { "operationName" })); - } - - return toObject; - } - - internal JsonNode GetOperationParametersToVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, - Common.GetValueByPath(fromObject, new string[] { "operationName" })); - } - - return toObject; - } - - internal JsonNode VideoFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uri" }, - Common.GetValueByPath(fromObject, new string[] { "uri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encodedVideo" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "encodedVideo" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "encoding" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "encoding" })); - } - - return toObject; - } - - internal JsonNode VideoFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "uri" }, - Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { - Common.SetValueByPath(toObject, new string[] { "videoBytes" }, - Transformers.TBytes(Common.GetValueByPath( - fromObject, new string[] { "bytesBase64Encoded" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - public Operations(ApiClient apiClient) { - _apiClient = apiClient; - } - - internal async Task PrivateGetVideosOperationAsync( - string operationName, GetOperationConfig? config, - CancellationToken cancellationToken = default) { - GetOperationParameters parameter = new GetOperationParameters(); - - if (!Common.IsZero(operationName)) { - parameter.OperationName = operationName; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException("Failed to parse GetOperationParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = GetOperationParametersToVertex(parameterNode, new JsonObject()); - path = Common.FormatMap("{operationName}", body["_url"]); - } else { - body = GetOperationParametersToMldev(parameterNode, new JsonObject()); - path = Common.FormatMap("{operationName}", body["_url"]); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - return responseNode; - } - - internal async Task PrivateFetchPredictVideosOperationAsync( - string operationName, string resourceName, FetchPredictOperationConfig? config, - CancellationToken cancellationToken = default) { - FetchPredictOperationParameters parameter = new FetchPredictOperationParameters(); - - if (!Common.IsZero(operationName)) { - parameter.OperationName = operationName; - } - if (!Common.IsZero(resourceName)) { - parameter.ResourceName = resourceName; - } - if (!Common.IsZero(config)) { - parameter.Config = config; - } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); - JsonNode? parameterNode = JsonNode.Parse(jsonString); - if (parameterNode == null) { - throw new NotSupportedException( - "Failed to parse FetchPredictOperationParameters to JsonNode."); - } - - JsonNode body; - string path; - if (this._apiClient.VertexAI) { - body = FetchPredictOperationParametersToVertex(parameterNode, new JsonObject()); - path = Common.FormatMap("{resourceName}:fetchPredictOperation", body["_url"]); - } else { - throw new NotSupportedException("This method is only supported in the Vertex AI client."); - } - JsonObject? bodyObj = body?.AsObject(); - bodyObj?.Remove("_url"); - if (bodyObj != null && bodyObj.ContainsKey("_query")) { - path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); - bodyObj.Remove("_query"); - } else { - bodyObj?.Remove("_query"); - } - HttpOptions? requestHttpOptions = config?.HttpOptions; - - ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), - requestHttpOptions, cancellationToken); - HttpContent httpContent = response.GetEntity(); -#if NETSTANDARD2_0 - string contentString = await httpContent.ReadAsStringAsync(); -#else - string contentString = await httpContent.ReadAsStringAsync(cancellationToken); -#endif - JsonNode? httpContentNode = JsonNode.Parse(contentString); - if (httpContentNode == null) { - throw new NotSupportedException("Failed to parse response to JsonNode."); - } - JsonNode responseNode = httpContentNode; - - return responseNode; - } - - /// - /// Gets the status of a long-running operation. - /// - /// The type of the operation. - /// An operation instance to get the status for. - /// A instance that specifies the optional - /// configurations. A - /// that can be used to cancel the operation. A - /// that represents the asynchronous operation. The task result contains the updated - /// instance with the latest status or result. - public async Task GetAsync( - TOperation operation, GetOperationConfig? config, - CancellationToken cancellationToken = default) - where TOperation : Operation { - if (string.IsNullOrEmpty(operation.Name)) { - throw new ArgumentException("Operation name is required.", nameof(operation)); - } - - string operationName = operation.Name; - - if (this._apiClient.VertexAI) { - string resourceName = - operationName.Split(new[] { "/operations/" }, StringSplitOptions.None)[0]; - FetchPredictOperationConfig fetchConfig = new FetchPredictOperationConfig {}; - if (config != null) { - fetchConfig.HttpOptions = config.HttpOptions; - } - JsonNode response = await this.PrivateFetchPredictVideosOperationAsync( - operationName, resourceName, fetchConfig, cancellationToken); - - return operation.FromApiResponse(response, true); - } else { - JsonNode response = - await this.PrivateGetVideosOperationAsync(operationName, config, cancellationToken); - return operation.FromApiResponse(response, false); - } - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + + public sealed class Operations { + private readonly ApiClient _apiClient; + + internal JsonNode FetchPredictOperationParametersToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "operationName" }, + Common.GetValueByPath(fromObject, new string[] { "operationName" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "resourceName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "resourceName" }, + Common.GetValueByPath(fromObject, new string[] { "resourceName" })); + } + + return toObject; + } + + internal JsonNode GenerateVideosOperationFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response", "generateVideoResponse" }) != + null) { + Common.SetValueByPath( + toObject, new string[] { "response" }, + GenerateVideosResponseFromMldev( + Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "response", "generateVideoResponse" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosOperationFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "metadata" }) != null) { + Common.SetValueByPath(toObject, new string[] { "metadata" }, + Common.GetValueByPath(fromObject, new string[] { "metadata" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "done" }) != null) { + Common.SetValueByPath(toObject, new string[] { "done" }, + Common.GetValueByPath(fromObject, new string[] { "done" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "error" }) != null) { + Common.SetValueByPath(toObject, new string[] { "error" }, + Common.GetValueByPath(fromObject, new string[] { "error" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "response" }, + GenerateVideosResponseFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "response" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GenerateVideosResponseFromMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "generatedSamples" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(GeneratedVideoFromMldev(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredCount" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredReasons" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); + } + + return toObject; + } + + internal JsonNode GenerateVideosResponseFromVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "videos" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "videos" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(GeneratedVideoFromVertex(Common.ParseToJsonNode(record), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "generatedVideos" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredCount" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredCount" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "raiMediaFilteredReasons" }, + Common.GetValueByPath(fromObject, new string[] { "raiMediaFilteredReasons" })); + } + + return toObject; + } + + internal JsonNode GeneratedVideoFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "video" }) != null) { + Common.SetValueByPath(toObject, new string[] { "video" }, + VideoFromMldev(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "video" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GeneratedVideoFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "_self" }) != null) { + Common.SetValueByPath(toObject, new string[] { "video" }, + VideoFromVertex(Common.ParseToJsonNode(Common.GetValueByPath( + fromObject, new string[] { "_self" })), + toObject)); + } + + return toObject; + } + + internal JsonNode GetOperationParametersToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, + Common.GetValueByPath(fromObject, new string[] { "operationName" })); + } + + return toObject; + } + + internal JsonNode GetOperationParametersToVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "operationName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "_url", "operationName" }, + Common.GetValueByPath(fromObject, new string[] { "operationName" })); + } + + return toObject; + } + + internal JsonNode VideoFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "uri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uri" }, + Common.GetValueByPath(fromObject, new string[] { "uri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encodedVideo" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "encodedVideo" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "encoding" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "encoding" })); + } + + return toObject; + } + + internal JsonNode VideoFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "gcsUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "uri" }, + Common.GetValueByPath(fromObject, new string[] { "gcsUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "bytesBase64Encoded" }) != null) { + Common.SetValueByPath(toObject, new string[] { "videoBytes" }, + Transformers.TBytes(Common.GetValueByPath( + fromObject, new string[] { "bytesBase64Encoded" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + public Operations(ApiClient apiClient) { + _apiClient = apiClient; + } + + internal async Task PrivateGetVideosOperationAsync( + string operationName, GetOperationConfig? config, + CancellationToken cancellationToken = default) { + GetOperationParameters parameter = new GetOperationParameters(); + + if (!Common.IsZero(operationName)) { + parameter.OperationName = operationName; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException("Failed to parse GetOperationParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = GetOperationParametersToVertex(parameterNode, new JsonObject()); + path = Common.FormatMap("{operationName}", body["_url"]); + } else { + body = GetOperationParametersToMldev(parameterNode, new JsonObject()); + path = Common.FormatMap("{operationName}", body["_url"]); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + return responseNode; + } + + internal async Task PrivateFetchPredictVideosOperationAsync( + string operationName, string resourceName, FetchPredictOperationConfig? config, + CancellationToken cancellationToken = default) { + FetchPredictOperationParameters parameter = new FetchPredictOperationParameters(); + + if (!Common.IsZero(operationName)) { + parameter.OperationName = operationName; + } + if (!Common.IsZero(resourceName)) { + parameter.ResourceName = resourceName; + } + if (!Common.IsZero(config)) { + parameter.Config = config; + } + string jsonString = JsonSerializer.Serialize(parameter); + JsonNode? parameterNode = JsonNode.Parse(jsonString); + if (parameterNode == null) { + throw new NotSupportedException( + "Failed to parse FetchPredictOperationParameters to JsonNode."); + } + + JsonNode body; + string path; + if (this._apiClient.VertexAI) { + body = FetchPredictOperationParametersToVertex(parameterNode, new JsonObject()); + path = Common.FormatMap("{resourceName}:fetchPredictOperation", body["_url"]); + } else { + throw new NotSupportedException("This method is only supported in the Vertex AI client."); + } + JsonObject? bodyObj = body?.AsObject(); + bodyObj?.Remove("_url"); + if (bodyObj != null && bodyObj.ContainsKey("_query")) { + path = path + "?" + Common.FormatQuery((JsonObject)bodyObj["_query"]); + bodyObj.Remove("_query"); + } else { + bodyObj?.Remove("_query"); + } + HttpOptions? requestHttpOptions = config?.HttpOptions; + + ApiResponse response = + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), + requestHttpOptions, cancellationToken); + HttpContent httpContent = response.GetEntity(); +#if NETSTANDARD2_0 + string contentString = await httpContent.ReadAsStringAsync(); +#else + string contentString = await httpContent.ReadAsStringAsync(cancellationToken); +#endif + JsonNode? httpContentNode = JsonNode.Parse(contentString); + if (httpContentNode == null) { + throw new NotSupportedException("Failed to parse response to JsonNode."); + } + JsonNode responseNode = httpContentNode; + + return responseNode; + } + + /// + /// Gets the status of a long-running operation. + /// + /// The type of the operation. + /// An operation instance to get the status for. + /// A instance that specifies the optional + /// configurations. A + /// that can be used to cancel the operation. A + /// that represents the asynchronous operation. The task result contains the updated + /// instance with the latest status or result. + public async Task GetAsync( + TOperation operation, GetOperationConfig? config, + CancellationToken cancellationToken = default) + where TOperation : Operation { + if (string.IsNullOrEmpty(operation.Name)) { + throw new ArgumentException("Operation name is required.", nameof(operation)); + } + + string operationName = operation.Name; + + if (this._apiClient.VertexAI) { + string resourceName = + operationName.Split(new[] { "/operations/" }, StringSplitOptions.None)[0]; + FetchPredictOperationConfig fetchConfig = new FetchPredictOperationConfig {}; + if (config != null) { + fetchConfig.HttpOptions = config.HttpOptions; + } + JsonNode response = await this.PrivateFetchPredictVideosOperationAsync( + operationName, resourceName, fetchConfig, cancellationToken); + + return operation.FromApiResponse(response, true); + } else { + JsonNode response = + await this.PrivateGetVideosOperationAsync(operationName, config, cancellationToken); + return operation.FromApiResponse(response, false); + } + } + } +} diff --git a/Google.GenAI/TokensConverters.cs b/Google.GenAI/TokensConverters.cs index 538b7bbf..19391438 100644 --- a/Google.GenAI/TokensConverters.cs +++ b/Google.GenAI/TokensConverters.cs @@ -1,813 +1,813 @@ -/* - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Auto-generated code. Do not edit. - -using System; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; - -using Google.GenAI.Types; - -namespace Google.GenAI { - class TokensConverters { - private readonly ApiClient _apiClient; - - public TokensConverters(ApiClient apiClient) { - _apiClient = apiClient; - } - - internal JsonNode PrebuiltVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "voiceName" }) != null) { - Common.SetValueByPath(toObject, new string[] { "voiceName" }, - Common.GetValueByPath(fromObject, new string[] { "voiceName" })); - } - - return toObject; - } - - internal JsonNode VoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "prebuiltVoiceConfig" }, - PrebuiltVoiceConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "prebuiltVoiceConfig" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode SpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { - Common.SetValueByPath(toObject, new string[] { "speaker" }, - Common.GetValueByPath(fromObject, new string[] { "speaker" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "voiceConfig" }, - VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode MultiSpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(SpeakerVoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(record, JsonConfig.InternalSerializerOptions)), - toObject)); - } - Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); - } - - return toObject; - } - - internal JsonNode SpeechConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "voiceConfig" }, - VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "voiceConfig" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, - MultiSpeakerVoiceConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "multiSpeakerVoiceConfig" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "languageCode" }, - Common.GetValueByPath(fromObject, new string[] { "languageCode" })); - } - - return toObject; - } - - internal JsonNode VideoMetadataToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "fps" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fps" }, - Common.GetValueByPath(fromObject, new string[] { "fps" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endOffset" }) != null) { - Common.SetValueByPath(toObject, new string[] { "endOffset" }, - Common.GetValueByPath(fromObject, new string[] { "endOffset" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "startOffset" }) != null) { - Common.SetValueByPath(toObject, new string[] { "startOffset" }, - Common.GetValueByPath(fromObject, new string[] { "startOffset" })); - } - - return toObject; - } - - internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { - Common.SetValueByPath(toObject, new string[] { "data" }, - Common.GetValueByPath(fromObject, new string[] { "data" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { - throw new NotSupportedException("displayName parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { - Common.SetValueByPath(toObject, new string[] { "fileUri" }, - Common.GetValueByPath(fromObject, new string[] { "fileUri" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mimeType" }, - Common.GetValueByPath(fromObject, new string[] { "mimeType" })); - } - - return toObject; - } - - internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "videoMetadata" }, - VideoMetadataToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "videoMetadata" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { - Common.SetValueByPath(toObject, new string[] { "thought" }, - Common.GetValueByPath(fromObject, new string[] { "thought" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "inlineData" }, - BlobToMldev(JsonNode.Parse(JsonSerializer.Serialize( - Common.GetValueByPath(fromObject, new string[] { "inlineData" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "fileData" }, - FileDataToMldev(JsonNode.Parse(JsonSerializer.Serialize( - Common.GetValueByPath(fromObject, new string[] { "fileData" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "thoughtSignature" }, - Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "codeExecutionResult" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "executableCode" }, - Common.GetValueByPath(fromObject, new string[] { "executableCode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { - Common.SetValueByPath(toObject, new string[] { "functionCall" }, - Common.GetValueByPath(fromObject, new string[] { "functionCall" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "functionResponse" }, - Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { - Common.SetValueByPath(toObject, new string[] { "text" }, - Common.GetValueByPath(fromObject, new string[] { "text" })); - } - - return toObject; - } - - internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(PartToMldev(JsonNode.Parse(JsonSerializer.Serialize(record, JsonConfig.InternalSerializerOptions)), toObject)); - } - Common.SetValueByPath(toObject, new string[] { "parts" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { - Common.SetValueByPath(toObject, new string[] { "role" }, - Common.GetValueByPath(fromObject, new string[] { "role" })); - } - - return toObject; - } - - internal JsonNode FunctionDeclarationToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "behavior" }) != null) { - Common.SetValueByPath(toObject, new string[] { "behavior" }, - Common.GetValueByPath(fromObject, new string[] { "behavior" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { - Common.SetValueByPath(toObject, new string[] { "description" }, - Common.GetValueByPath(fromObject, new string[] { "description" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { - Common.SetValueByPath(toObject, new string[] { "name" }, - Common.GetValueByPath(fromObject, new string[] { "name" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { - Common.SetValueByPath(toObject, new string[] { "parameters" }, - Common.GetValueByPath(fromObject, new string[] { "parameters" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "parametersJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { - Common.SetValueByPath(toObject, new string[] { "response" }, - Common.GetValueByPath(fromObject, new string[] { "response" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "responseJsonSchema" }, - Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); - } - - return toObject; - } - - internal JsonNode IntervalToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "startTime" }, - Common.GetValueByPath(fromObject, new string[] { "startTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { - Common.SetValueByPath(toObject, new string[] { "endTime" }, - Common.GetValueByPath(fromObject, new string[] { "endTime" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "timeRangeFilter" }, - IntervalToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "timeRangeFilter" }))), - toObject)); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { - throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode DynamicRetrievalConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { - Common.SetValueByPath(toObject, new string[] { "mode" }, - Common.GetValueByPath(fromObject, new string[] { "mode" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "dynamicThreshold" }, - Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" })); - } - - return toObject; - } - - internal JsonNode GoogleSearchRetrievalToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "dynamicRetrievalConfig" }) != null) { - Common.SetValueByPath(toObject, new string[] { "dynamicRetrievalConfig" }, - DynamicRetrievalConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "dynamicRetrievalConfig" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode UrlContextToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode ToolComputerUseToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "environment" }) != null) { - Common.SetValueByPath(toObject, new string[] { "environment" }, - Common.GetValueByPath(fromObject, new string[] { "environment" })); - } - - return toObject; - } - - internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { - JsonArray keyArray = - (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(FunctionDeclarationToMldev(JsonNode.Parse(JsonSerializer.Serialize(record, JsonConfig.InternalSerializerOptions)), - toObject)); - } - Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { - throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "googleSearch" }, - GoogleSearchToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "googleSearch" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { - Common.SetValueByPath(toObject, new string[] { "googleSearchRetrieval" }, - GoogleSearchRetrievalToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "googleSearchRetrieval" }))), - toObject)); - } - - if (!Common.IsZero( - Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { - throw new NotSupportedException( - "enterpriseWebSearch parameter is not supported in Gemini API."); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "googleMaps" }))) { - throw new NotSupportedException("googleMaps parameter is not supported in Gemini API."); - } - - if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "urlContext" }, - UrlContextToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "urlContext" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "computerUse" }, - ToolComputerUseToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "computerUse" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { - Common.SetValueByPath(toObject, new string[] { "codeExecution" }, - Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); - } - - return toObject; - } - - internal JsonNode SessionResumptionConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "handle" }) != null) { - Common.SetValueByPath(toObject, new string[] { "handle" }, - Common.GetValueByPath(fromObject, new string[] { "handle" })); - } - - if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "transparent" }))) { - throw new NotSupportedException("transparent parameter is not supported in Gemini API."); - } - - return toObject; - } - - internal JsonNode AudioTranscriptionConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode AutomaticActivityDetectionToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "disabled" }) != null) { - Common.SetValueByPath(toObject, new string[] { "disabled" }, - Common.GetValueByPath(fromObject, new string[] { "disabled" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "startOfSpeechSensitivity" }, - Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "endOfSpeechSensitivity" }, - Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "prefixPaddingMs" }, - Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "silenceDurationMs" }, - Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" })); - } - - return toObject; - } - - internal JsonNode RealtimeInputConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "automaticActivityDetection" }) != - null) { - Common.SetValueByPath(toObject, new string[] { "automaticActivityDetection" }, - AutomaticActivityDetectionToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "automaticActivityDetection" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "activityHandling" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "activityHandling" }, - Common.GetValueByPath(fromObject, new string[] { "activityHandling" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "turnCoverage" }) != null) { - Common.SetValueByPath(toObject, new string[] { "turnCoverage" }, - Common.GetValueByPath(fromObject, new string[] { "turnCoverage" })); - } - - return toObject; - } - - internal JsonNode SlidingWindowToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "targetTokens" }) != null) { - Common.SetValueByPath(toObject, new string[] { "targetTokens" }, - Common.GetValueByPath(fromObject, new string[] { "targetTokens" })); - } - - return toObject; - } - - internal JsonNode ContextWindowCompressionConfigToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "triggerTokens" }) != null) { - Common.SetValueByPath(toObject, new string[] { "triggerTokens" }, - Common.GetValueByPath(fromObject, new string[] { "triggerTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "slidingWindow" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "slidingWindow" }, - SlidingWindowToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "slidingWindow" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode ProactivityConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" }) != null) { - Common.SetValueByPath(toObject, new string[] { "proactiveAudio" }, - Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" })); - } - - return toObject; - } - - internal JsonNode LiveConnectConfigToMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "generationConfig" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig" }, - Common.GetValueByPath(fromObject, new string[] { "generationConfig" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "responseModalities" }, - Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { - Common.SetValueByPath(parentObject, - new string[] { "setup", "generationConfig", "temperature" }, - Common.GetValueByPath(fromObject, new string[] { "temperature" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topP" }, - Common.GetValueByPath(fromObject, new string[] { "topP" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topK" }, - Common.GetValueByPath(fromObject, new string[] { "topK" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "maxOutputTokens" }, - Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "mediaResolution" }, - Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "seed" }, - Common.GetValueByPath(fromObject, new string[] { "seed" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "speechConfig" }, - SpeechConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Transformers.TLiveSpeechConfig( - Common.GetValueByPath(fromObject, new string[] { "speechConfig" })))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "generationConfig", "enableAffectiveDialog" }, - Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "systemInstruction" }, - ContentToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Transformers.TContent( - Common.GetValueByPath(fromObject, new string[] { "systemInstruction" })))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { - JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); - JsonArray result = new JsonArray(); - - foreach (var record in keyArray) { - result.Add(ToolToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Transformers.TTool(record), JsonConfig.InternalSerializerOptions)), toObject)); - } - Common.SetValueByPath(parentObject, new string[] { "setup", "tools" }, result); - } - - if (Common.GetValueByPath(fromObject, new string[] { "sessionResumption" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "sessionResumption" }, - SessionResumptionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "sessionResumption" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "inputAudioTranscription" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "inputAudioTranscription" }, - AudioTranscriptionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "inputAudioTranscription" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "outputAudioTranscription" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "outputAudioTranscription" }, - AudioTranscriptionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "outputAudioTranscription" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "realtimeInputConfig" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "realtimeInputConfig" }, - RealtimeInputConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "realtimeInputConfig" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "contextWindowCompression" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "setup", "contextWindowCompression" }, - ContextWindowCompressionConfigToMldev( - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "contextWindowCompression" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "proactivity" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "setup", "proactivity" }, - ProactivityConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "proactivity" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode LiveConnectConstraintsToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "setup", "model" }, - Transformers.TModel(this._apiClient, - Common.GetValueByPath(fromObject, new string[] { "model" }))); - } - - if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { - Common.SetValueByPath( - toObject, new string[] { "config" }, - LiveConnectConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "config" }))), - toObject)); - } - - return toObject; - } - - internal JsonNode CreateAuthTokenConfigToMldev(ApiClient apiClient, JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "expireTime" }, - Common.GetValueByPath(fromObject, new string[] { "expireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "newSessionExpireTime" }, - Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "uses" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "uses" }, - Common.GetValueByPath(fromObject, new string[] { "uses" })); - } - - if (Common.GetValueByPath(fromObject, new string[] { "liveConnectConstraints" }) != null) { - Common.SetValueByPath(parentObject, new string[] { "bidiGenerateContentSetup" }, - LiveConnectConstraintsToMldev( - apiClient, - JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( - fromObject, new string[] { "liveConnectConstraints" }))), - toObject)); - } - - if (Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" }) != null) { - Common.SetValueByPath( - parentObject, new string[] { "fieldMask" }, - Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" })); - } - - return toObject; - } - - internal JsonNode CreateAuthTokenParametersToMldev(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode CreateAuthTokenParametersToVertex(JsonNode fromObject, - JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode AuthTokenFromMldev(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - - internal JsonNode AuthTokenFromVertex(JsonNode fromObject, JsonObject parentObject) { - JsonObject toObject = new JsonObject(); - - return toObject; - } - } -} +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Auto-generated code. Do not edit. + +using System; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +using Google.GenAI.Types; + +namespace Google.GenAI { + class TokensConverters { + private readonly ApiClient _apiClient; + + public TokensConverters(ApiClient apiClient) { + _apiClient = apiClient; + } + + internal JsonNode PrebuiltVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "voiceName" }) != null) { + Common.SetValueByPath(toObject, new string[] { "voiceName" }, + Common.GetValueByPath(fromObject, new string[] { "voiceName" })); + } + + return toObject; + } + + internal JsonNode VoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "prebuiltVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "prebuiltVoiceConfig" }, + PrebuiltVoiceConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "prebuiltVoiceConfig" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode SpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speaker" }) != null) { + Common.SetValueByPath(toObject, new string[] { "speaker" }, + Common.GetValueByPath(fromObject, new string[] { "speaker" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "voiceConfig" }, + VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode MultiSpeakerVoiceConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "speakerVoiceConfigs" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(SpeakerVoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(record)), + toObject)); + } + Common.SetValueByPath(toObject, new string[] { "speakerVoiceConfigs" }, result); + } + + return toObject; + } + + internal JsonNode SpeechConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "voiceConfig" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "voiceConfig" }, + VoiceConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "voiceConfig" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "multiSpeakerVoiceConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "multiSpeakerVoiceConfig" }, + MultiSpeakerVoiceConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "multiSpeakerVoiceConfig" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "languageCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "languageCode" }, + Common.GetValueByPath(fromObject, new string[] { "languageCode" })); + } + + return toObject; + } + + internal JsonNode VideoMetadataToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "fps" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fps" }, + Common.GetValueByPath(fromObject, new string[] { "fps" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endOffset" }) != null) { + Common.SetValueByPath(toObject, new string[] { "endOffset" }, + Common.GetValueByPath(fromObject, new string[] { "endOffset" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "startOffset" }) != null) { + Common.SetValueByPath(toObject, new string[] { "startOffset" }, + Common.GetValueByPath(fromObject, new string[] { "startOffset" })); + } + + return toObject; + } + + internal JsonNode BlobToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "data" }) != null) { + Common.SetValueByPath(toObject, new string[] { "data" }, + Common.GetValueByPath(fromObject, new string[] { "data" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode FileDataToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "displayName" }))) { + throw new NotSupportedException("displayName parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileUri" }) != null) { + Common.SetValueByPath(toObject, new string[] { "fileUri" }, + Common.GetValueByPath(fromObject, new string[] { "fileUri" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mimeType" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mimeType" }, + Common.GetValueByPath(fromObject, new string[] { "mimeType" })); + } + + return toObject; + } + + internal JsonNode PartToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "videoMetadata" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "videoMetadata" }, + VideoMetadataToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "videoMetadata" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thought" }) != null) { + Common.SetValueByPath(toObject, new string[] { "thought" }, + Common.GetValueByPath(fromObject, new string[] { "thought" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inlineData" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "inlineData" }, + BlobToMldev(JsonNode.Parse(JsonSerializer.Serialize( + Common.GetValueByPath(fromObject, new string[] { "inlineData" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "fileData" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "fileData" }, + FileDataToMldev(JsonNode.Parse(JsonSerializer.Serialize( + Common.GetValueByPath(fromObject, new string[] { "fileData" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "thoughtSignature" }, + Common.GetValueByPath(fromObject, new string[] { "thoughtSignature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "codeExecutionResult" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecutionResult" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "executableCode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "executableCode" }, + Common.GetValueByPath(fromObject, new string[] { "executableCode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionCall" }) != null) { + Common.SetValueByPath(toObject, new string[] { "functionCall" }, + Common.GetValueByPath(fromObject, new string[] { "functionCall" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "functionResponse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "functionResponse" }, + Common.GetValueByPath(fromObject, new string[] { "functionResponse" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "text" }) != null) { + Common.SetValueByPath(toObject, new string[] { "text" }, + Common.GetValueByPath(fromObject, new string[] { "text" })); + } + + return toObject; + } + + internal JsonNode ContentToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "parts" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "parts" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(PartToMldev(JsonNode.Parse(JsonSerializer.Serialize(record)), toObject)); + } + Common.SetValueByPath(toObject, new string[] { "parts" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "role" }) != null) { + Common.SetValueByPath(toObject, new string[] { "role" }, + Common.GetValueByPath(fromObject, new string[] { "role" })); + } + + return toObject; + } + + internal JsonNode FunctionDeclarationToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "behavior" }) != null) { + Common.SetValueByPath(toObject, new string[] { "behavior" }, + Common.GetValueByPath(fromObject, new string[] { "behavior" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "description" }) != null) { + Common.SetValueByPath(toObject, new string[] { "description" }, + Common.GetValueByPath(fromObject, new string[] { "description" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "name" }) != null) { + Common.SetValueByPath(toObject, new string[] { "name" }, + Common.GetValueByPath(fromObject, new string[] { "name" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parameters" }) != null) { + Common.SetValueByPath(toObject, new string[] { "parameters" }, + Common.GetValueByPath(fromObject, new string[] { "parameters" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "parametersJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "parametersJsonSchema" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "response" }) != null) { + Common.SetValueByPath(toObject, new string[] { "response" }, + Common.GetValueByPath(fromObject, new string[] { "response" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "responseJsonSchema" }, + Common.GetValueByPath(fromObject, new string[] { "responseJsonSchema" })); + } + + return toObject; + } + + internal JsonNode IntervalToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "startTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "startTime" }, + Common.GetValueByPath(fromObject, new string[] { "startTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endTime" }) != null) { + Common.SetValueByPath(toObject, new string[] { "endTime" }, + Common.GetValueByPath(fromObject, new string[] { "endTime" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "timeRangeFilter" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "timeRangeFilter" }, + IntervalToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "timeRangeFilter" }))), + toObject)); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "excludeDomains" }))) { + throw new NotSupportedException("excludeDomains parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode DynamicRetrievalConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "mode" }) != null) { + Common.SetValueByPath(toObject, new string[] { "mode" }, + Common.GetValueByPath(fromObject, new string[] { "mode" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "dynamicThreshold" }, + Common.GetValueByPath(fromObject, new string[] { "dynamicThreshold" })); + } + + return toObject; + } + + internal JsonNode GoogleSearchRetrievalToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "dynamicRetrievalConfig" }) != null) { + Common.SetValueByPath(toObject, new string[] { "dynamicRetrievalConfig" }, + DynamicRetrievalConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "dynamicRetrievalConfig" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode UrlContextToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode ToolComputerUseToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "environment" }) != null) { + Common.SetValueByPath(toObject, new string[] { "environment" }, + Common.GetValueByPath(fromObject, new string[] { "environment" })); + } + + return toObject; + } + + internal JsonNode ToolToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }) != null) { + JsonArray keyArray = + (JsonArray)Common.GetValueByPath(fromObject, new string[] { "functionDeclarations" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(FunctionDeclarationToMldev(JsonNode.Parse(JsonSerializer.Serialize(record)), + toObject)); + } + Common.SetValueByPath(toObject, new string[] { "functionDeclarations" }, result); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "retrieval" }))) { + throw new NotSupportedException("retrieval parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearch" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "googleSearch" }, + GoogleSearchToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "googleSearch" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "googleSearchRetrieval" }) != null) { + Common.SetValueByPath(toObject, new string[] { "googleSearchRetrieval" }, + GoogleSearchRetrievalToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "googleSearchRetrieval" }))), + toObject)); + } + + if (!Common.IsZero( + Common.GetValueByPath(fromObject, new string[] { "enterpriseWebSearch" }))) { + throw new NotSupportedException( + "enterpriseWebSearch parameter is not supported in Gemini API."); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "googleMaps" }))) { + throw new NotSupportedException("googleMaps parameter is not supported in Gemini API."); + } + + if (Common.GetValueByPath(fromObject, new string[] { "urlContext" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "urlContext" }, + UrlContextToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "urlContext" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "computerUse" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "computerUse" }, + ToolComputerUseToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "computerUse" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "codeExecution" }) != null) { + Common.SetValueByPath(toObject, new string[] { "codeExecution" }, + Common.GetValueByPath(fromObject, new string[] { "codeExecution" })); + } + + return toObject; + } + + internal JsonNode SessionResumptionConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "handle" }) != null) { + Common.SetValueByPath(toObject, new string[] { "handle" }, + Common.GetValueByPath(fromObject, new string[] { "handle" })); + } + + if (!Common.IsZero(Common.GetValueByPath(fromObject, new string[] { "transparent" }))) { + throw new NotSupportedException("transparent parameter is not supported in Gemini API."); + } + + return toObject; + } + + internal JsonNode AudioTranscriptionConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode AutomaticActivityDetectionToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "disabled" }) != null) { + Common.SetValueByPath(toObject, new string[] { "disabled" }, + Common.GetValueByPath(fromObject, new string[] { "disabled" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "startOfSpeechSensitivity" }, + Common.GetValueByPath(fromObject, new string[] { "startOfSpeechSensitivity" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "endOfSpeechSensitivity" }, + Common.GetValueByPath(fromObject, new string[] { "endOfSpeechSensitivity" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "prefixPaddingMs" }, + Common.GetValueByPath(fromObject, new string[] { "prefixPaddingMs" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "silenceDurationMs" }, + Common.GetValueByPath(fromObject, new string[] { "silenceDurationMs" })); + } + + return toObject; + } + + internal JsonNode RealtimeInputConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "automaticActivityDetection" }) != + null) { + Common.SetValueByPath(toObject, new string[] { "automaticActivityDetection" }, + AutomaticActivityDetectionToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "automaticActivityDetection" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "activityHandling" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "activityHandling" }, + Common.GetValueByPath(fromObject, new string[] { "activityHandling" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "turnCoverage" }) != null) { + Common.SetValueByPath(toObject, new string[] { "turnCoverage" }, + Common.GetValueByPath(fromObject, new string[] { "turnCoverage" })); + } + + return toObject; + } + + internal JsonNode SlidingWindowToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "targetTokens" }) != null) { + Common.SetValueByPath(toObject, new string[] { "targetTokens" }, + Common.GetValueByPath(fromObject, new string[] { "targetTokens" })); + } + + return toObject; + } + + internal JsonNode ContextWindowCompressionConfigToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "triggerTokens" }) != null) { + Common.SetValueByPath(toObject, new string[] { "triggerTokens" }, + Common.GetValueByPath(fromObject, new string[] { "triggerTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "slidingWindow" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "slidingWindow" }, + SlidingWindowToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "slidingWindow" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode ProactivityConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" }) != null) { + Common.SetValueByPath(toObject, new string[] { "proactiveAudio" }, + Common.GetValueByPath(fromObject, new string[] { "proactiveAudio" })); + } + + return toObject; + } + + internal JsonNode LiveConnectConfigToMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "generationConfig" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig" }, + Common.GetValueByPath(fromObject, new string[] { "generationConfig" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "responseModalities" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "responseModalities" }, + Common.GetValueByPath(fromObject, new string[] { "responseModalities" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "temperature" }) != null) { + Common.SetValueByPath(parentObject, + new string[] { "setup", "generationConfig", "temperature" }, + Common.GetValueByPath(fromObject, new string[] { "temperature" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topP" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topP" }, + Common.GetValueByPath(fromObject, new string[] { "topP" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "topK" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "topK" }, + Common.GetValueByPath(fromObject, new string[] { "topK" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "maxOutputTokens" }, + Common.GetValueByPath(fromObject, new string[] { "maxOutputTokens" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "mediaResolution" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "mediaResolution" }, + Common.GetValueByPath(fromObject, new string[] { "mediaResolution" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "seed" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "generationConfig", "seed" }, + Common.GetValueByPath(fromObject, new string[] { "seed" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "speechConfig" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "speechConfig" }, + SpeechConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Transformers.TLiveSpeechConfig( + Common.GetValueByPath(fromObject, new string[] { "speechConfig" })))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "generationConfig", "enableAffectiveDialog" }, + Common.GetValueByPath(fromObject, new string[] { "enableAffectiveDialog" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "systemInstruction" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "systemInstruction" }, + ContentToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Transformers.TContent( + Common.GetValueByPath(fromObject, new string[] { "systemInstruction" })))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "tools" }) != null) { + JsonArray keyArray = (JsonArray)Common.GetValueByPath(fromObject, new string[] { "tools" }); + JsonArray result = new JsonArray(); + + foreach (var record in keyArray) { + result.Add(ToolToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Transformers.TTool(record))), toObject)); + } + Common.SetValueByPath(parentObject, new string[] { "setup", "tools" }, result); + } + + if (Common.GetValueByPath(fromObject, new string[] { "sessionResumption" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "sessionResumption" }, + SessionResumptionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "sessionResumption" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "inputAudioTranscription" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "inputAudioTranscription" }, + AudioTranscriptionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "inputAudioTranscription" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "outputAudioTranscription" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "outputAudioTranscription" }, + AudioTranscriptionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "outputAudioTranscription" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "realtimeInputConfig" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "realtimeInputConfig" }, + RealtimeInputConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "realtimeInputConfig" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "contextWindowCompression" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "setup", "contextWindowCompression" }, + ContextWindowCompressionConfigToMldev( + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "contextWindowCompression" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "proactivity" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "setup", "proactivity" }, + ProactivityConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "proactivity" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode LiveConnectConstraintsToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "model" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "setup", "model" }, + Transformers.TModel(this._apiClient, + Common.GetValueByPath(fromObject, new string[] { "model" }))); + } + + if (Common.GetValueByPath(fromObject, new string[] { "config" }) != null) { + Common.SetValueByPath( + toObject, new string[] { "config" }, + LiveConnectConfigToMldev(JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "config" }))), + toObject)); + } + + return toObject; + } + + internal JsonNode CreateAuthTokenConfigToMldev(ApiClient apiClient, JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + if (Common.GetValueByPath(fromObject, new string[] { "expireTime" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "expireTime" }, + Common.GetValueByPath(fromObject, new string[] { "expireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "newSessionExpireTime" }, + Common.GetValueByPath(fromObject, new string[] { "newSessionExpireTime" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "uses" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "uses" }, + Common.GetValueByPath(fromObject, new string[] { "uses" })); + } + + if (Common.GetValueByPath(fromObject, new string[] { "liveConnectConstraints" }) != null) { + Common.SetValueByPath(parentObject, new string[] { "bidiGenerateContentSetup" }, + LiveConnectConstraintsToMldev( + apiClient, + JsonNode.Parse(JsonSerializer.Serialize(Common.GetValueByPath( + fromObject, new string[] { "liveConnectConstraints" }))), + toObject)); + } + + if (Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" }) != null) { + Common.SetValueByPath( + parentObject, new string[] { "fieldMask" }, + Common.GetValueByPath(fromObject, new string[] { "lockAdditionalFields" })); + } + + return toObject; + } + + internal JsonNode CreateAuthTokenParametersToMldev(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode CreateAuthTokenParametersToVertex(JsonNode fromObject, + JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode AuthTokenFromMldev(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + + internal JsonNode AuthTokenFromVertex(JsonNode fromObject, JsonObject parentObject) { + JsonObject toObject = new JsonObject(); + + return toObject; + } + } +} diff --git a/Google.GenAI/Tunings.cs b/Google.GenAI/Tunings.cs index 0134ced8..93c09d9a 100644 --- a/Google.GenAI/Tunings.cs +++ b/Google.GenAI/Tunings.cs @@ -1446,7 +1446,7 @@ private async Task PrivateGetAsync(string name, GetTuningJobConfig? c if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse GetTuningJobParameters to JsonNode."); @@ -1472,7 +1472,7 @@ private async Task PrivateGetAsync(string name, GetTuningJobConfig? c HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1505,7 +1505,7 @@ private async Task PrivateListAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse ListTuningJobsParameters to JsonNode."); @@ -1531,7 +1531,7 @@ private async Task PrivateListAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Get, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1579,7 +1579,7 @@ public async Task CancelAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException("Failed to parse CancelTuningJobParameters to JsonNode."); @@ -1605,7 +1605,7 @@ public async Task CancelAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1652,7 +1652,7 @@ private async Task PrivateTuneAsync(string? baseModel, PreTunedModel? if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException( @@ -1679,7 +1679,7 @@ private async Task PrivateTuneAsync(string? baseModel, PreTunedModel? HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0 @@ -1722,7 +1722,7 @@ private async Task PrivateTuneMldevAsync( if (!Common.IsZero(config)) { parameter.Config = config; } - string jsonString = JsonSerializer.Serialize(parameter, JsonConfig.InternalSerializerOptions); + string jsonString = JsonSerializer.Serialize(parameter); JsonNode? parameterNode = JsonNode.Parse(jsonString); if (parameterNode == null) { throw new NotSupportedException( @@ -1750,7 +1750,7 @@ private async Task PrivateTuneMldevAsync( HttpOptions? requestHttpOptions = config?.HttpOptions; ApiResponse response = - await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body, JsonConfig.JsonSerializerOptions), + await this._apiClient.RequestAsync(HttpMethod.Post, path, JsonSerializer.Serialize(body), requestHttpOptions, cancellationToken); HttpContent httpContent = response.GetEntity(); #if NETSTANDARD2_0