From 1883d19d583bcb5af9edfe5a338340c3c1ca55c6 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 17:46:11 +0000 Subject: [PATCH 01/10] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 391cde3..57aa7a5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-6f6bfb81d092f30a5e2005328c97d61b9ea36132bb19e9e79e55294b9534ce20.yml -openapi_spec_hash: f3fc1e3688a38dc2c28f7178f7d534e5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-e629569417ad17cad5c73180109b4c3ae778f38063fc72146fa82f82de145911.yml +openapi_spec_hash: 42e4eedbc0fcc772bb271191a067bce1 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 From 8b3d6b04a2bb9883e434c90e263e723aec3c12b2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 18:23:02 +0000 Subject: [PATCH 02/10] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 57aa7a5..094ac49 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-e629569417ad17cad5c73180109b4c3ae778f38063fc72146fa82f82de145911.yml -openapi_spec_hash: 42e4eedbc0fcc772bb271191a067bce1 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-49b40c7425adba9e67fc102838c5216c45ca1f7ef4c10823c5665fd413538504.yml +openapi_spec_hash: 6880dc029df2e88dfe8943c0dec5a3a5 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 From 37f80f3936fc9a85e6e6079381c41ef4e3793472 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 18:25:46 +0000 Subject: [PATCH 03/10] feat: [feat]: add `ignoreSelectors` to `observe()` --- .stats.yml | 4 +-- .../Sessions/SessionObserveParamsTest.cs | 30 +++++++++++++++++++ .../Models/Sessions/SessionObserveParams.cs | 26 ++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 094ac49..0339c57 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-49b40c7425adba9e67fc102838c5216c45ca1f7ef4c10823c5665fd413538504.yml -openapi_spec_hash: 6880dc029df2e88dfe8943c0dec5a3a5 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-eae8400fade7b2c8329c4148f56de92e147c34c0feecb420c015aab6544a9acc.yml +openapi_spec_hash: 0a9eff1ac1d464e89cbd9db64709b08a config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 diff --git a/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs index e615f51..a86c04a 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs @@ -20,6 +20,7 @@ public void FieldRoundtrip_Works() Instruction = "Find all clickable navigation links", Options = new() { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -51,6 +52,7 @@ public void FieldRoundtrip_Works() string expectedInstruction = "Find all clickable navigation links"; SessionObserveParamsOptions expectedOptions = new() { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -132,6 +134,7 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Instruction = "Find all clickable navigation links", Options = new() { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -171,6 +174,7 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Instruction = "Find all clickable navigation links", Options = new() { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -260,6 +264,7 @@ public void CopyConstructor_Works() Instruction = "Find all clickable navigation links", Options = new() { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -299,6 +304,7 @@ public void FieldRoundtrip_Works() { var model = new SessionObserveParamsOptions { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -323,6 +329,7 @@ public void FieldRoundtrip_Works() }, }; + List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; SessionObserveParamsOptionsModel expectedModel = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -346,6 +353,12 @@ public void FieldRoundtrip_Works() { "rememberMe", true }, }; + Assert.NotNull(model.IgnoreSelectors); + Assert.Equal(expectedIgnoreSelectors.Count, model.IgnoreSelectors.Count); + for (int i = 0; i < expectedIgnoreSelectors.Count; i++) + { + Assert.Equal(expectedIgnoreSelectors[i], model.IgnoreSelectors[i]); + } Assert.Equal(expectedModel, model.Model); Assert.Equal(expectedSelector, model.Selector); Assert.Equal(expectedTimeout, model.Timeout); @@ -364,6 +377,7 @@ public void SerializationRoundtrip_Works() { var model = new SessionObserveParamsOptions { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -402,6 +416,7 @@ public void FieldRoundtripThroughSerialization_Works() { var model = new SessionObserveParamsOptions { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -433,6 +448,7 @@ public void FieldRoundtripThroughSerialization_Works() ); Assert.NotNull(deserialized); + List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; SessionObserveParamsOptionsModel expectedModel = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -456,6 +472,12 @@ public void FieldRoundtripThroughSerialization_Works() { "rememberMe", true }, }; + Assert.NotNull(deserialized.IgnoreSelectors); + Assert.Equal(expectedIgnoreSelectors.Count, deserialized.IgnoreSelectors.Count); + for (int i = 0; i < expectedIgnoreSelectors.Count; i++) + { + Assert.Equal(expectedIgnoreSelectors[i], deserialized.IgnoreSelectors[i]); + } Assert.Equal(expectedModel, deserialized.Model); Assert.Equal(expectedSelector, deserialized.Selector); Assert.Equal(expectedTimeout, deserialized.Timeout); @@ -474,6 +496,7 @@ public void Validation_Works() { var model = new SessionObserveParamsOptions { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", @@ -506,6 +529,8 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() { var model = new SessionObserveParamsOptions { }; + Assert.Null(model.IgnoreSelectors); + Assert.False(model.RawData.ContainsKey("ignoreSelectors")); Assert.Null(model.Model); Assert.False(model.RawData.ContainsKey("model")); Assert.Null(model.Selector); @@ -530,12 +555,15 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() var model = new SessionObserveParamsOptions { // Null should be interpreted as omitted for these properties + IgnoreSelectors = null, Model = null, Selector = null, Timeout = null, Variables = null, }; + Assert.Null(model.IgnoreSelectors); + Assert.False(model.RawData.ContainsKey("ignoreSelectors")); Assert.Null(model.Model); Assert.False(model.RawData.ContainsKey("model")); Assert.Null(model.Selector); @@ -552,6 +580,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() var model = new SessionObserveParamsOptions { // Null should be interpreted as omitted for these properties + IgnoreSelectors = null, Model = null, Selector = null, Timeout = null, @@ -566,6 +595,7 @@ public void CopyConstructor_Works() { var model = new SessionObserveParamsOptions { + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], Model = new ModelConfig() { ModelName = "openai/gpt-5.4-mini", diff --git a/src/Stagehand/Models/Sessions/SessionObserveParams.cs b/src/Stagehand/Models/Sessions/SessionObserveParams.cs index 62a87ed..fcc16f9 100644 --- a/src/Stagehand/Models/Sessions/SessionObserveParams.cs +++ b/src/Stagehand/Models/Sessions/SessionObserveParams.cs @@ -1,5 +1,6 @@ using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Text; @@ -229,6 +230,30 @@ public override int GetHashCode() )] public sealed record class SessionObserveParamsOptions : JsonModel { + /// + /// Selectors for elements and subtrees that should be excluded from observation + /// + public IReadOnlyList? IgnoreSelectors + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct>("ignoreSelectors"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "ignoreSelectors", + value == null ? null : ImmutableArray.ToImmutableArray(value) + ); + } + } + /// /// Model configuration object or model name string (e.g., 'openai/gpt-5-nano') /// @@ -323,6 +348,7 @@ public IReadOnlyDictionary? Variabl /// public override void Validate() { + _ = this.IgnoreSelectors; this.Model?.Validate(); _ = this.Selector; _ = this.Timeout; From c20c6e01fb40ff0ab9547bf733f6c735e0949e4e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 02:04:47 +0000 Subject: [PATCH 04/10] fix(internal): disable default HttpClient timeout as we have our own --- src/Stagehand/Core/ClientOptions.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Stagehand/Core/ClientOptions.cs b/src/Stagehand/Core/ClientOptions.cs index fc9fa76..df853dc 100644 --- a/src/Stagehand/Core/ClientOptions.cs +++ b/src/Stagehand/Core/ClientOptions.cs @@ -21,9 +21,16 @@ public record struct ClientOptions() /// /// The HTTP client to use for making requests in the SDK. + /// + /// Note: The HttpClient has a built-in timeout, which defaults to 100 seconds. + /// When passing a custom HttpClient, this timeout may conflict with the SDK's + /// own timeout handler and cause premature cancellation. /// public HttpClient HttpClient { get; set; } = - new(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.Available }); + new(new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.Available }) + { + Timeout = global::System.Threading.Timeout.InfiniteTimeSpan, + }; Lazy _baseUrl = new(() => Environment.GetEnvironmentVariable("STAGEHAND_API_URL") From d5b8a144a36d2e91701a2e87c0fe714e131ba2c2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 13 May 2026 02:00:49 +0000 Subject: [PATCH 05/10] ci: pin GitHub Actions to commit SHAs Pin all GitHub Actions referenced in generated workflows (both first-party `actions/*` and third-party) to immutable commit SHAs. Updating pinned actions is now a deliberate codegen-side bump rather than implicit on every workflow run. --- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/publish-nuget.yml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c0087c..ca841c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,10 @@ jobs: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@55ec9447dda3d1cf6bd587150f3262f30ee10815 # v3.4.2 with: dotnet-version: '8.0.x' @@ -41,10 +41,10 @@ jobs: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@55ec9447dda3d1cf6bd587150f3262f30ee10815 # v3.4.2 with: dotnet-version: '8.0.x' @@ -57,10 +57,10 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/stagehand-csharp' && 'depot-windows-2022' || 'windows-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 with: dotnet-version: '8.0.x' diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 2ff27c6..7b8f246 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -12,10 +12,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0 with: dotnet-version: '8.0.x' From 8fa49262dca9eda52850f3226279b7ee97228f39 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 19:16:19 +0000 Subject: [PATCH 06/10] feat: STG-1756 add Vertex auth params to Stagehand spec --- .stats.yml | 4 +- .../Models/Sessions/ModelConfigTest.cs | 791 +++++++++++++++++ .../Models/Sessions/SessionActParamsTest.cs | 308 +++++++ .../Sessions/SessionExecuteParamsTest.cs | 792 ++++++++++++++++++ .../Sessions/SessionExtractParamsTest.cs | 308 +++++++ .../Sessions/SessionObserveParamsTest.cs | 308 +++++++ .../Services/SessionServiceTest.cs | 88 ++ src/Stagehand/Core/ModelBase.cs | 1 + src/Stagehand/Models/Sessions/ModelConfig.cs | 733 ++++++++++++++++ 9 files changed, 3331 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0339c57..220445b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-eae8400fade7b2c8329c4148f56de92e147c34c0feecb420c015aab6544a9acc.yml -openapi_spec_hash: 0a9eff1ac1d464e89cbd9db64709b08a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-e77d6b15f0a94b16a54ef87a84d2cabe49eb11cff5ceba76f00dd788ff483eab.yml +openapi_spec_hash: a1dab7fe72a772d188a15305124ebd73 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 diff --git a/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs b/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs index bdfc359..1020652 100644 --- a/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs @@ -16,19 +16,64 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string expectedModelName = "openai/gpt-5.4-mini"; string expectedApiKey = "sk-some-openai-api-key"; string expectedBaseUrl = "https://api.openai.com/v1"; + GoogleAuthOptions expectedGoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; Dictionary expectedHeaders = new() { { "foo", "string" } }; + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; ApiEnum expectedProvider = ModelConfigProvider.OpenAI; Assert.Equal(expectedModelName, model.ModelName); Assert.Equal(expectedApiKey, model.ApiKey); Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.Equal(expectedGoogleAuthOptions, model.GoogleAuthOptions); Assert.NotNull(model.Headers); Assert.Equal(expectedHeaders.Count, model.Headers.Count); foreach (var item in expectedHeaders) @@ -37,6 +82,8 @@ public void FieldRoundtrip_Works() Assert.Equal(value, model.Headers[item.Key]); } + Assert.Equal(expectedLocation, model.Location); + Assert.Equal(expectedProject, model.Project); Assert.Equal(expectedProvider, model.Provider); } @@ -48,7 +95,29 @@ public void SerializationRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; @@ -69,7 +138,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; @@ -83,12 +174,35 @@ public void FieldRoundtripThroughSerialization_Works() string expectedModelName = "openai/gpt-5.4-mini"; string expectedApiKey = "sk-some-openai-api-key"; string expectedBaseUrl = "https://api.openai.com/v1"; + GoogleAuthOptions expectedGoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; Dictionary expectedHeaders = new() { { "foo", "string" } }; + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; ApiEnum expectedProvider = ModelConfigProvider.OpenAI; Assert.Equal(expectedModelName, deserialized.ModelName); Assert.Equal(expectedApiKey, deserialized.ApiKey); Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.Equal(expectedGoogleAuthOptions, deserialized.GoogleAuthOptions); Assert.NotNull(deserialized.Headers); Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); foreach (var item in expectedHeaders) @@ -97,6 +211,8 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(value, deserialized.Headers[item.Key]); } + Assert.Equal(expectedLocation, deserialized.Location); + Assert.Equal(expectedProject, deserialized.Project); Assert.Equal(expectedProvider, deserialized.Provider); } @@ -108,7 +224,29 @@ public void Validation_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; @@ -124,8 +262,14 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() Assert.False(model.RawData.ContainsKey("apiKey")); Assert.Null(model.BaseUrl); Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.GoogleAuthOptions); + Assert.False(model.RawData.ContainsKey("googleAuthOptions")); Assert.Null(model.Headers); Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Location); + Assert.False(model.RawData.ContainsKey("location")); + Assert.Null(model.Project); + Assert.False(model.RawData.ContainsKey("project")); Assert.Null(model.Provider); Assert.False(model.RawData.ContainsKey("provider")); } @@ -148,7 +292,10 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties ApiKey = null, BaseUrl = null, + GoogleAuthOptions = null, Headers = null, + Location = null, + Project = null, Provider = null, }; @@ -156,8 +303,14 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() Assert.False(model.RawData.ContainsKey("apiKey")); Assert.Null(model.BaseUrl); Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.GoogleAuthOptions); + Assert.False(model.RawData.ContainsKey("googleAuthOptions")); Assert.Null(model.Headers); Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Location); + Assert.False(model.RawData.ContainsKey("location")); + Assert.Null(model.Project); + Assert.False(model.RawData.ContainsKey("project")); Assert.Null(model.Provider); Assert.False(model.RawData.ContainsKey("provider")); } @@ -172,7 +325,10 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() // Null should be interpreted as omitted for these properties ApiKey = null, BaseUrl = null, + GoogleAuthOptions = null, Headers = null, + Location = null, + Project = null, Provider = null, }; @@ -187,7 +343,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; @@ -197,6 +375,617 @@ public void CopyConstructor_Works() } } +public class GoogleAuthOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new GoogleAuthOptions + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + Credentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + string expectedProjectID = "projectId"; + Scopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, model.Credentials); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedScopes, model.Scopes); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new GoogleAuthOptions + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new GoogleAuthOptions + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Credentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + string expectedProjectID = "projectId"; + Scopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, deserialized.Credentials); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedScopes, deserialized.Scopes); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new GoogleAuthOptions + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new GoogleAuthOptions { }; + + Assert.Null(model.Credentials); + Assert.False(model.RawData.ContainsKey("credentials")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new GoogleAuthOptions { }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new GoogleAuthOptions + { + // Null should be interpreted as omitted for these properties + Credentials = null, + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + Assert.Null(model.Credentials); + Assert.False(model.RawData.ContainsKey("credentials")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new GoogleAuthOptions + { + // Null should be interpreted as omitted for these properties + Credentials = null, + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new GoogleAuthOptions + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + GoogleAuthOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class CredentialsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = CredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedPrivateKey, model.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, model.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, model.AuthUri); + Assert.Equal(expectedClientID, model.ClientID); + Assert.Equal(expectedClientX509CertUrl, model.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, model.PrivateKeyID); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedTokenUri, model.TokenUri); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = CredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedPrivateKey, deserialized.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, deserialized.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, deserialized.AuthUri); + Assert.Equal(expectedClientID, deserialized.ClientID); + Assert.Equal(expectedClientX509CertUrl, deserialized.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, deserialized.PrivateKeyID); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedTokenUri, deserialized.TokenUri); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Credentials { ClientEmail = "client_email", PrivateKey = "private_key" }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Credentials { ClientEmail = "client_email", PrivateKey = "private_key" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + Credentials copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class CredentialsTypeTest : TestBase +{ + [Theory] + [InlineData(CredentialsType.ServiceAccount)] + public void Validation_Works(CredentialsType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(CredentialsType.ServiceAccount)] + public void SerializationRoundtrip_Works(CredentialsType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ScopesTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + Scopes value = "string"; + value.Validate(); + } + + [Fact] + public void StringsValidationWorks() + { + Scopes value = new(["string"]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Scopes value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + Scopes value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + public class ModelConfigProviderTest : TestBase { [Theory] @@ -205,6 +994,7 @@ public class ModelConfigProviderTest : TestBase [InlineData(ModelConfigProvider.Google)] [InlineData(ModelConfigProvider.Microsoft)] [InlineData(ModelConfigProvider.Bedrock)] + [InlineData(ModelConfigProvider.Vertex)] public void Validation_Works(ModelConfigProvider rawValue) { // force implicit conversion because Theory can't do that for us @@ -230,6 +1020,7 @@ public void InvalidEnumValidationThrows_Works() [InlineData(ModelConfigProvider.Google)] [InlineData(ModelConfigProvider.Microsoft)] [InlineData(ModelConfigProvider.Bedrock)] + [InlineData(ModelConfigProvider.Vertex)] public void SerializationRoundtrip_Works(ModelConfigProvider rawValue) { // force implicit conversion because Theory can't do that for us diff --git a/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs index 9982b63..05e126d 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs @@ -25,7 +25,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -55,7 +77,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -132,7 +176,29 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -170,7 +236,29 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -263,7 +351,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -358,7 +468,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -381,7 +513,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }; double expectedTimeout = 30000; @@ -420,7 +574,29 @@ public void SerializationRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -457,7 +633,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -487,7 +685,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }; double expectedTimeout = 30000; @@ -526,7 +746,29 @@ public void Validation_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -611,7 +853,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, @@ -645,7 +909,29 @@ public void ConfigValidationWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }; value.Validate(); @@ -666,7 +952,29 @@ public void ConfigSerializationRoundtripWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = Sessions::ModelConfigProvider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); diff --git a/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs index 20aaaca..116df0d 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs @@ -24,7 +24,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -33,7 +55,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -66,7 +110,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -75,7 +141,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -118,7 +206,29 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -127,7 +237,29 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -169,7 +301,29 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -178,7 +332,29 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -224,7 +400,29 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -233,7 +431,29 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -274,7 +494,29 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -283,7 +525,29 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -326,7 +590,29 @@ public void Url_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -335,7 +621,29 @@ public void Url_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -390,7 +698,29 @@ public void AddHeadersToRequest_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -399,7 +729,29 @@ public void AddHeadersToRequest_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -448,7 +800,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -457,7 +831,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -500,7 +896,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -509,7 +927,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -522,7 +962,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; ApiEnum expectedMode = Mode.Cua; @@ -531,7 +993,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; ApiEnum expectedProvider = Provider.OpenAI; @@ -556,7 +1040,29 @@ public void SerializationRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -565,7 +1071,29 @@ public void SerializationRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -592,7 +1120,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -601,7 +1151,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -621,7 +1193,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; ApiEnum expectedMode = Mode.Cua; @@ -630,7 +1224,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; ApiEnum expectedProvider = Provider.OpenAI; @@ -655,7 +1271,29 @@ public void Validation_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -664,7 +1302,29 @@ public void Validation_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -757,7 +1417,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -766,7 +1448,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -789,7 +1493,29 @@ public void ModelConfigValidationWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; value.Validate(); @@ -810,7 +1536,29 @@ public void ModelConfigSerializationRoundtripWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); @@ -906,7 +1654,29 @@ public void ConfigValidationWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; value.Validate(); @@ -927,7 +1697,29 @@ public void ConfigSerializationRoundtripWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); diff --git a/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs index acbc7a0..a888c3b 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs @@ -26,7 +26,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -50,7 +72,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -137,7 +181,29 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -169,7 +235,29 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -251,7 +339,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -283,7 +393,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -296,7 +428,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string expectedSelector = "#main-content"; @@ -324,7 +478,29 @@ public void SerializationRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -351,7 +527,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -371,7 +569,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string expectedSelector = "#main-content"; @@ -399,7 +619,29 @@ public void Validation_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -480,7 +722,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "#main-content", @@ -503,7 +767,29 @@ public void ConfigValidationWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; value.Validate(); @@ -524,7 +810,29 @@ public void ConfigSerializationRoundtripWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); diff --git a/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs index a86c04a..d180d7f 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs @@ -26,7 +26,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -58,7 +80,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -140,7 +184,29 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -180,7 +246,29 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -270,7 +358,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -310,7 +420,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -335,7 +467,29 @@ public void FieldRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string expectedSelector = "nav"; @@ -383,7 +537,29 @@ public void SerializationRoundtrip_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -422,7 +598,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -454,7 +652,29 @@ public void FieldRoundtripThroughSerialization_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string expectedSelector = "nav"; @@ -502,7 +722,29 @@ public void Validation_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -601,7 +843,29 @@ public void CopyConstructor_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", @@ -636,7 +900,29 @@ public void ConfigValidationWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; value.Validate(); @@ -657,7 +943,29 @@ public void ConfigSerializationRoundtripWorks() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); diff --git a/src/Stagehand.Tests/Services/SessionServiceTest.cs b/src/Stagehand.Tests/Services/SessionServiceTest.cs index bf5d2b0..902d715 100644 --- a/src/Stagehand.Tests/Services/SessionServiceTest.cs +++ b/src/Stagehand.Tests/Services/SessionServiceTest.cs @@ -58,7 +58,29 @@ public async Task Execute_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -67,7 +89,29 @@ public async Task Execute_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, @@ -107,7 +151,29 @@ public async Task ExecuteStreaming_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, @@ -116,7 +182,29 @@ public async Task ExecuteStreaming_Works() ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", + GoogleAuthOptions = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = CredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, Headers = new Dictionary() { { "foo", "string" } }, + Location = "us-central1", + Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, Provider = Provider.OpenAI, diff --git a/src/Stagehand/Core/ModelBase.cs b/src/Stagehand/Core/ModelBase.cs index 08c436c..652df35 100644 --- a/src/Stagehand/Core/ModelBase.cs +++ b/src/Stagehand/Core/ModelBase.cs @@ -21,6 +21,7 @@ protected ModelBase(ModelBase modelBase) Converters = { new FrozenDictionaryConverterFactory(), + new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), diff --git a/src/Stagehand/Models/Sessions/ModelConfig.cs b/src/Stagehand/Models/Sessions/ModelConfig.cs index 1fcacce..60bb520 100644 --- a/src/Stagehand/Models/Sessions/ModelConfig.cs +++ b/src/Stagehand/Models/Sessions/ModelConfig.cs @@ -1,5 +1,6 @@ using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; @@ -67,6 +68,27 @@ public string? BaseUrl } } + /// + /// google-auth-library options used to authenticate Vertex AI models + /// + public GoogleAuthOptions? GoogleAuthOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("googleAuthOptions"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("googleAuthOptions", value); + } + } + /// /// Custom headers sent with every request to the model provider /// @@ -91,6 +113,48 @@ public IReadOnlyDictionary? Headers } } + /// + /// Google Cloud location for Vertex AI models + /// + public string? Location + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("location"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("location", value); + } + } + + /// + /// Google Cloud project ID for Vertex AI models + /// + public string? Project + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("project"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("project", value); + } + } + /// /// AI provider for the model (or provide a baseURL endpoint instead) /// @@ -118,7 +182,10 @@ public override void Validate() _ = this.ModelName; _ = this.ApiKey; _ = this.BaseUrl; + this.GoogleAuthOptions?.Validate(); _ = this.Headers; + _ = this.Location; + _ = this.Project; this.Provider?.Validate(); } @@ -164,6 +231,669 @@ public ModelConfig FromRawUnchecked(IReadOnlyDictionary raw ModelConfig.FromRawUnchecked(rawData); } +/// +/// google-auth-library options used to authenticate Vertex AI models +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class GoogleAuthOptions : JsonModel +{ + /// + /// Google Cloud service account credentials + /// + public Credentials? Credentials + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("credentials"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("credentials", value); + } + } + + /// + /// Google Cloud project ID used by google-auth-library + /// + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("projectId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("projectId", value); + } + } + + /// + /// Google auth scopes for the desired API request + /// + public Scopes? Scopes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("scopes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("scopes", value); + } + } + + /// + /// Google Cloud universe domain + /// + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universeDomain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universeDomain", value); + } + } + + /// + public override void Validate() + { + this.Credentials?.Validate(); + _ = this.ProjectID; + this.Scopes?.Validate(); + _ = this.UniverseDomain; + } + + public GoogleAuthOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public GoogleAuthOptions(GoogleAuthOptions googleAuthOptions) + : base(googleAuthOptions) { } +#pragma warning restore CS8618 + + public GoogleAuthOptions(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + GoogleAuthOptions(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static GoogleAuthOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class GoogleAuthOptionsFromRaw : IFromRawJson +{ + /// + public GoogleAuthOptions FromRawUnchecked(IReadOnlyDictionary rawData) => + GoogleAuthOptions.FromRawUnchecked(rawData); +} + +/// +/// Google Cloud service account credentials +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Credentials : JsonModel +{ + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("client_email"); + } + init { this._rawData.Set("client_email", value); } + } + + public required string PrivateKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("private_key"); + } + init { this._rawData.Set("private_key", value); } + } + + public string? AuthProviderX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_provider_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_provider_x509_cert_url", value); + } + } + + public string? AuthUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_uri", value); + } + } + + public string? ClientID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_id", value); + } + } + + public string? ClientX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_x509_cert_url", value); + } + } + + public string? PrivateKeyID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("private_key_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("private_key_id", value); + } + } + + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("project_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("project_id", value); + } + } + + public string? TokenUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("token_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("token_uri", value); + } + } + + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universe_domain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universe_domain", value); + } + } + + /// + public override void Validate() + { + _ = this.ClientEmail; + _ = this.PrivateKey; + _ = this.AuthProviderX509CertUrl; + _ = this.AuthUri; + _ = this.ClientID; + _ = this.ClientX509CertUrl; + _ = this.PrivateKeyID; + _ = this.ProjectID; + _ = this.TokenUri; + this.Type?.Validate(); + _ = this.UniverseDomain; + } + + public Credentials() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Credentials(Credentials credentials) + : base(credentials) { } +#pragma warning restore CS8618 + + public Credentials(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Credentials(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Credentials FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class CredentialsFromRaw : IFromRawJson +{ + /// + public Credentials FromRawUnchecked(IReadOnlyDictionary rawData) => + Credentials.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(CredentialsTypeConverter))] +public enum CredentialsType +{ + ServiceAccount, +} + +sealed class CredentialsTypeConverter : JsonConverter +{ + public override CredentialsType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "service_account" => CredentialsType.ServiceAccount, + _ => (CredentialsType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + CredentialsType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + CredentialsType.ServiceAccount => "service_account", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Google auth scopes for the desired API request +/// +[JsonConverter(typeof(ScopesConverter))] +public record class Scopes : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Scopes(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Scopes(IReadOnlyList value, JsonElement? element = null) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public Scopes(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a string. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickStrings(out var value)) { + /// // `value` is of type `IReadOnlyList<string>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickStrings([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action> strings + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case IReadOnlyList value: + strings(value); + break; + default: + throw new StagehandInvalidDataException("Data did not match any variant of Scopes"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func, T> strings + ) + { + return this.Value switch + { + string value => @string(value), + IReadOnlyList value => strings(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of Scopes" + ), + }; + } + + public static implicit operator Scopes(string value) => new(value); + + public static implicit operator Scopes(List value) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new StagehandInvalidDataException("Data did not match any variant of Scopes"); + } + } + + public virtual bool Equals(Scopes? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + IReadOnlyList _ => 1, + _ => -1, + }; + } +} + +sealed class ScopesConverter : JsonConverter +{ + public override Scopes? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Scopes value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + /// /// AI provider for the model (or provide a baseURL endpoint instead) /// @@ -175,6 +905,7 @@ public enum ModelConfigProvider Google, Microsoft, Bedrock, + Vertex, } sealed class ModelConfigProviderConverter : JsonConverter @@ -192,6 +923,7 @@ JsonSerializerOptions options "google" => ModelConfigProvider.Google, "microsoft" => ModelConfigProvider.Microsoft, "bedrock" => ModelConfigProvider.Bedrock, + "vertex" => ModelConfigProvider.Vertex, _ => (ModelConfigProvider)(-1), }; } @@ -211,6 +943,7 @@ JsonSerializerOptions options ModelConfigProvider.Google => "google", ModelConfigProvider.Microsoft => "microsoft", ModelConfigProvider.Bedrock => "bedrock", + ModelConfigProvider.Vertex => "vertex", _ => throw new StagehandInvalidDataException( string.Format("Invalid value '{0}' in {1}", value, nameof(value)) ), From 1dae7c22aa40af570d387fa871ee10ea4c0b9084 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 21:00:22 +0000 Subject: [PATCH 07/10] feat: Add `screenshot` option to Extract --- .stats.yml | 4 ++-- .../Sessions/SessionExtractParamsTest.cs | 20 ++++++++++++++++ .../Models/Sessions/SessionExtractParams.cs | 23 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 220445b..9043f09 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-e77d6b15f0a94b16a54ef87a84d2cabe49eb11cff5ceba76f00dd788ff483eab.yml -openapi_spec_hash: a1dab7fe72a772d188a15305124ebd73 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-80502d74c1be605e77d45ff2b54297fe34ce85dbad1e8f2dfa30ba6d09601219.yml +openapi_spec_hash: fd62f768756a400c3ecd695bfcf3845a config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 diff --git a/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs index a888c3b..db68005 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs @@ -51,6 +51,7 @@ public void FieldRoundtrip_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -97,6 +98,7 @@ public void FieldRoundtrip_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -206,6 +208,7 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -260,6 +263,7 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -364,6 +368,7 @@ public void CopyConstructor_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -418,6 +423,7 @@ public void FieldRoundtrip_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -453,6 +459,7 @@ public void FieldRoundtrip_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; + bool expectedScreenshot = false; string expectedSelector = "#main-content"; double expectedTimeout = 30000; @@ -463,6 +470,7 @@ public void FieldRoundtrip_Works() Assert.Equal(expectedIgnoreSelectors[i], model.IgnoreSelectors[i]); } Assert.Equal(expectedModel, model.Model); + Assert.Equal(expectedScreenshot, model.Screenshot); Assert.Equal(expectedSelector, model.Selector); Assert.Equal(expectedTimeout, model.Timeout); } @@ -503,6 +511,7 @@ public void SerializationRoundtrip_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -552,6 +561,7 @@ public void FieldRoundtripThroughSerialization_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -594,6 +604,7 @@ public void FieldRoundtripThroughSerialization_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }; + bool expectedScreenshot = false; string expectedSelector = "#main-content"; double expectedTimeout = 30000; @@ -604,6 +615,7 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(expectedIgnoreSelectors[i], deserialized.IgnoreSelectors[i]); } Assert.Equal(expectedModel, deserialized.Model); + Assert.Equal(expectedScreenshot, deserialized.Screenshot); Assert.Equal(expectedSelector, deserialized.Selector); Assert.Equal(expectedTimeout, deserialized.Timeout); } @@ -644,6 +656,7 @@ public void Validation_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -660,6 +673,8 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() Assert.False(model.RawData.ContainsKey("ignoreSelectors")); Assert.Null(model.Model); Assert.False(model.RawData.ContainsKey("model")); + Assert.Null(model.Screenshot); + Assert.False(model.RawData.ContainsKey("screenshot")); Assert.Null(model.Selector); Assert.False(model.RawData.ContainsKey("selector")); Assert.Null(model.Timeout); @@ -682,6 +697,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties IgnoreSelectors = null, Model = null, + Screenshot = null, Selector = null, Timeout = null, }; @@ -690,6 +706,8 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() Assert.False(model.RawData.ContainsKey("ignoreSelectors")); Assert.Null(model.Model); Assert.False(model.RawData.ContainsKey("model")); + Assert.Null(model.Screenshot); + Assert.False(model.RawData.ContainsKey("screenshot")); Assert.Null(model.Selector); Assert.False(model.RawData.ContainsKey("selector")); Assert.Null(model.Timeout); @@ -704,6 +722,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() // Null should be interpreted as omitted for these properties IgnoreSelectors = null, Model = null, + Screenshot = null, Selector = null, Timeout = null, }; @@ -747,6 +766,7 @@ public void CopyConstructor_Works() Project = "my-gcp-project", Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; diff --git a/src/Stagehand/Models/Sessions/SessionExtractParams.cs b/src/Stagehand/Models/Sessions/SessionExtractParams.cs index a549d13..953fcd0 100644 --- a/src/Stagehand/Models/Sessions/SessionExtractParams.cs +++ b/src/Stagehand/Models/Sessions/SessionExtractParams.cs @@ -301,6 +301,28 @@ public SessionExtractParamsOptionsModel? Model } } + /// + /// When true, include a screenshot of the current viewport in the extraction + /// LLM call. Defaults to false. + /// + public bool? Screenshot + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableStruct("screenshot"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("screenshot", value); + } + } + /// /// CSS selector to scope extraction to a specific element /// @@ -348,6 +370,7 @@ public override void Validate() { _ = this.IgnoreSelectors; this.Model?.Validate(); + _ = this.Screenshot; _ = this.Selector; _ = this.Timeout; } From 491fbd8190a86a1a8e9ba41e849753207aa8574c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 May 2026 19:42:41 +0000 Subject: [PATCH 08/10] feat: [STG-1756] forward Vertex model config --- .stats.yml | 4 +- .../Models/Sessions/ModelConfigTest.cs | 1273 ++++- .../Models/Sessions/SessionActParamsTest.cs | 2114 +++++++- .../Sessions/SessionExecuteParamsTest.cs | 4709 +++++++++++++++-- .../Sessions/SessionExtractParamsTest.cs | 2383 ++++++++- .../Sessions/SessionObserveParamsTest.cs | 2371 ++++++++- .../Models/Sessions/SessionStartParamsTest.cs | 46 +- .../Services/SessionServiceTest.cs | 104 +- src/Stagehand/Core/ModelBase.cs | 36 +- src/Stagehand/Models/Sessions/ModelConfig.cs | 1193 ++++- .../Models/Sessions/SessionActParams.cs | 1413 ++++- .../Models/Sessions/SessionExecuteParams.cs | 3454 +++++++++++- .../Models/Sessions/SessionExtractParams.cs | 1588 +++++- .../Models/Sessions/SessionObserveParams.cs | 1588 +++++- .../Models/Sessions/SessionStartParams.cs | 26 +- 15 files changed, 20535 insertions(+), 1767 deletions(-) diff --git a/.stats.yml b/.stats.yml index 9043f09..15099ca 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 8 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-80502d74c1be605e77d45ff2b54297fe34ce85dbad1e8f2dfa30ba6d09601219.yml -openapi_spec_hash: fd62f768756a400c3ecd695bfcf3845a +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase/stagehand-c7910965e66e73ad8b65b6cc391d431094b2a6c6577c3e9d82feaa8138e74cff.yml +openapi_spec_hash: 37748bb69c22a9ce721d9b5a5861f964 config_hash: 1fb12ae9b478488bc1e56bfbdc210b01 diff --git a/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs b/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs index 1020652..e459154 100644 --- a/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs @@ -9,14 +9,67 @@ namespace Stagehand.Tests.Models.Sessions; public class ModelConfigTest : TestBase { [Fact] - public void FieldRoundtrip_Works() + public void VertexModelConfigObjectValidationWorks() + { + ModelConfig value = new ModelConfigVertexModelConfigObject() + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() { - var model = new ModelConfig + ModelConfig value = new ModelConfigGenericModelConfigObject() { ModelName = "openai/gpt-5.4-mini", ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, + }; + value.Validate(); + } + + [Fact] + public void VertexModelConfigObjectSerializationRoundtripWorks() + { + ModelConfig value = new ModelConfigVertexModelConfigObject() + { + Auth = new() { Credentials = new() { @@ -29,23 +82,100 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); - string expectedModelName = "openai/gpt-5.4-mini"; - string expectedApiKey = "sk-some-openai-api-key"; - string expectedBaseUrl = "https://api.openai.com/v1"; - GoogleAuthOptions expectedGoogleAuthOptions = new() + Assert.Equal(value, deserialized); + } + + [Fact] + public void GenericModelConfigObjectSerializationRoundtripWorks() + { + ModelConfig value = new ModelConfigGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ModelConfigVertexModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ModelConfigVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + ModelConfigVertexModelConfigObjectAuth expectedAuth = new() { Credentials = new() { @@ -58,22 +188,34 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + ModelConfigVertexModelConfigObjectProviderOptions expectedProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; Dictionary expectedHeaders = new() { { "foo", "string" } }; - string expectedLocation = "us-central1"; - string expectedProject = "my-gcp-project"; - ApiEnum expectedProvider = ModelConfigProvider.OpenAI; + Assert.Equal(expectedAuth, model.Auth); Assert.Equal(expectedModelName, model.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, model.Provider)); + Assert.Equal(expectedProviderOptions, model.ProviderOptions); Assert.Equal(expectedApiKey, model.ApiKey); Assert.Equal(expectedBaseUrl, model.BaseUrl); - Assert.Equal(expectedGoogleAuthOptions, model.GoogleAuthOptions); Assert.NotNull(model.Headers); Assert.Equal(expectedHeaders.Count, model.Headers.Count); foreach (var item in expectedHeaders) @@ -82,20 +224,14 @@ public void FieldRoundtrip_Works() Assert.Equal(value, model.Headers[item.Key]); } - Assert.Equal(expectedLocation, model.Location); - Assert.Equal(expectedProject, model.Project); - Assert.Equal(expectedProvider, model.Provider); } [Fact] public void SerializationRoundtrip_Works() { - var model = new ModelConfig + var model = new ModelConfigVertexModelConfigObject { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -108,21 +244,30 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + var deserialized = JsonSerializer.Deserialize( json, ModelBase.SerializerOptions ); @@ -133,12 +278,9 @@ public void SerializationRoundtrip_Works() [Fact] public void FieldRoundtripThroughSerialization_Works() { - var model = new ModelConfig + var model = new ModelConfigVertexModelConfigObject { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -151,30 +293,36 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + var deserialized = JsonSerializer.Deserialize( element, ModelBase.SerializerOptions ); Assert.NotNull(deserialized); - string expectedModelName = "openai/gpt-5.4-mini"; - string expectedApiKey = "sk-some-openai-api-key"; - string expectedBaseUrl = "https://api.openai.com/v1"; - GoogleAuthOptions expectedGoogleAuthOptions = new() + ModelConfigVertexModelConfigObjectAuth expectedAuth = new() { Credentials = new() { @@ -187,22 +335,34 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + ModelConfigVertexModelConfigObjectProviderOptions expectedProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; Dictionary expectedHeaders = new() { { "foo", "string" } }; - string expectedLocation = "us-central1"; - string expectedProject = "my-gcp-project"; - ApiEnum expectedProvider = ModelConfigProvider.OpenAI; + Assert.Equal(expectedAuth, deserialized.Auth); Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, deserialized.Provider)); + Assert.Equal(expectedProviderOptions, deserialized.ProviderOptions); Assert.Equal(expectedApiKey, deserialized.ApiKey); Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); - Assert.Equal(expectedGoogleAuthOptions, deserialized.GoogleAuthOptions); Assert.NotNull(deserialized.Headers); Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); foreach (var item in expectedHeaders) @@ -211,20 +371,14 @@ public void FieldRoundtripThroughSerialization_Works() Assert.Equal(value, deserialized.Headers[item.Key]); } - Assert.Equal(expectedLocation, deserialized.Location); - Assert.Equal(expectedProject, deserialized.Project); - Assert.Equal(expectedProvider, deserialized.Provider); } [Fact] public void Validation_Works() { - var model = new ModelConfig + var model = new ModelConfigVertexModelConfigObject { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -237,17 +391,26 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; model.Validate(); @@ -256,28 +419,84 @@ public void Validation_Works() [Fact] public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() { - var model = new ModelConfig { ModelName = "openai/gpt-5.4-mini" }; + var model = new ModelConfigVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; Assert.Null(model.ApiKey); Assert.False(model.RawData.ContainsKey("apiKey")); Assert.Null(model.BaseUrl); Assert.False(model.RawData.ContainsKey("baseURL")); - Assert.Null(model.GoogleAuthOptions); - Assert.False(model.RawData.ContainsKey("googleAuthOptions")); Assert.Null(model.Headers); Assert.False(model.RawData.ContainsKey("headers")); - Assert.Null(model.Location); - Assert.False(model.RawData.ContainsKey("location")); - Assert.Null(model.Project); - Assert.False(model.RawData.ContainsKey("project")); - Assert.Null(model.Provider); - Assert.False(model.RawData.ContainsKey("provider")); } [Fact] public void OptionalNonNullablePropertiesUnsetValidation_Works() { - var model = new ModelConfig { ModelName = "openai/gpt-5.4-mini" }; + var model = new ModelConfigVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; model.Validate(); } @@ -285,51 +504,93 @@ public void OptionalNonNullablePropertiesUnsetValidation_Works() [Fact] public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() { - var model = new ModelConfig + var model = new ModelConfigVertexModelConfigObject { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), // Null should be interpreted as omitted for these properties ApiKey = null, BaseUrl = null, - GoogleAuthOptions = null, Headers = null, - Location = null, - Project = null, - Provider = null, }; Assert.Null(model.ApiKey); Assert.False(model.RawData.ContainsKey("apiKey")); Assert.Null(model.BaseUrl); Assert.False(model.RawData.ContainsKey("baseURL")); - Assert.Null(model.GoogleAuthOptions); - Assert.False(model.RawData.ContainsKey("googleAuthOptions")); Assert.Null(model.Headers); Assert.False(model.RawData.ContainsKey("headers")); - Assert.Null(model.Location); - Assert.False(model.RawData.ContainsKey("location")); - Assert.Null(model.Project); - Assert.False(model.RawData.ContainsKey("project")); - Assert.Null(model.Provider); - Assert.False(model.RawData.ContainsKey("provider")); } [Fact] public void OptionalNonNullablePropertiesSetToNullValidation_Works() { - var model = new ModelConfig + var model = new ModelConfigVertexModelConfigObject { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), // Null should be interpreted as omitted for these properties ApiKey = null, BaseUrl = null, - GoogleAuthOptions = null, Headers = null, - Location = null, - Project = null, - Provider = null, }; model.Validate(); @@ -338,12 +599,9 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() [Fact] public void CopyConstructor_Works() { - var model = new ModelConfig + var model = new ModelConfigVertexModelConfigObject { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -356,31 +614,40 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ModelConfigVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; - ModelConfig copied = new(model); + ModelConfigVertexModelConfigObject copied = new(model); Assert.Equal(model, copied); } } -public class GoogleAuthOptionsTest : TestBase +public class ModelConfigVertexModelConfigObjectAuthTest : TestBase { [Fact] public void FieldRoundtrip_Works() { - var model = new GoogleAuthOptions + var model = new ModelConfigVertexModelConfigObjectAuth { Credentials = new() { @@ -393,7 +660,7 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", @@ -401,7 +668,7 @@ public void FieldRoundtrip_Works() UniverseDomain = "universeDomain", }; - Credentials expectedCredentials = new() + ModelConfigVertexModelConfigObjectAuthCredentials expectedCredentials = new() { ClientEmail = "client_email", PrivateKey = "private_key", @@ -412,14 +679,16 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); string expectedProjectID = "projectId"; - Scopes expectedScopes = "string"; + ModelConfigVertexModelConfigObjectAuthScopes expectedScopes = "string"; string expectedUniverseDomain = "universeDomain"; Assert.Equal(expectedCredentials, model.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); Assert.Equal(expectedProjectID, model.ProjectID); Assert.Equal(expectedScopes, model.Scopes); Assert.Equal(expectedUniverseDomain, model.UniverseDomain); @@ -428,7 +697,7 @@ public void FieldRoundtrip_Works() [Fact] public void SerializationRoundtrip_Works() { - var model = new GoogleAuthOptions + var model = new ModelConfigVertexModelConfigObjectAuth { Credentials = new() { @@ -441,7 +710,7 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", @@ -450,7 +719,7 @@ public void SerializationRoundtrip_Works() }; string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + var deserialized = JsonSerializer.Deserialize( json, ModelBase.SerializerOptions ); @@ -461,7 +730,7 @@ public void SerializationRoundtrip_Works() [Fact] public void FieldRoundtripThroughSerialization_Works() { - var model = new GoogleAuthOptions + var model = new ModelConfigVertexModelConfigObjectAuth { Credentials = new() { @@ -474,7 +743,7 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", @@ -483,13 +752,13 @@ public void FieldRoundtripThroughSerialization_Works() }; string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + var deserialized = JsonSerializer.Deserialize( element, ModelBase.SerializerOptions ); Assert.NotNull(deserialized); - Credentials expectedCredentials = new() + ModelConfigVertexModelConfigObjectAuthCredentials expectedCredentials = new() { ClientEmail = "client_email", PrivateKey = "private_key", @@ -500,14 +769,16 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); string expectedProjectID = "projectId"; - Scopes expectedScopes = "string"; + ModelConfigVertexModelConfigObjectAuthScopes expectedScopes = "string"; string expectedUniverseDomain = "universeDomain"; Assert.Equal(expectedCredentials, deserialized.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); Assert.Equal(expectedProjectID, deserialized.ProjectID); Assert.Equal(expectedScopes, deserialized.Scopes); Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); @@ -516,7 +787,7 @@ public void FieldRoundtripThroughSerialization_Works() [Fact] public void Validation_Works() { - var model = new GoogleAuthOptions + var model = new ModelConfigVertexModelConfigObjectAuth { Credentials = new() { @@ -529,7 +800,7 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", @@ -543,12 +814,26 @@ public void Validation_Works() [Fact] public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() { - var model = new GoogleAuthOptions { }; - - Assert.Null(model.Credentials); - Assert.False(model.RawData.ContainsKey("credentials")); - Assert.Null(model.ProjectID); - Assert.False(model.RawData.ContainsKey("projectId")); + var model = new ModelConfigVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); Assert.Null(model.Scopes); Assert.False(model.RawData.ContainsKey("scopes")); Assert.Null(model.UniverseDomain); @@ -558,7 +843,23 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() [Fact] public void OptionalNonNullablePropertiesUnsetValidation_Works() { - var model = new GoogleAuthOptions { }; + var model = new ModelConfigVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; model.Validate(); } @@ -566,17 +867,29 @@ public void OptionalNonNullablePropertiesUnsetValidation_Works() [Fact] public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() { - var model = new GoogleAuthOptions + var model = new ModelConfigVertexModelConfigObjectAuth { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + // Null should be interpreted as omitted for these properties - Credentials = null, ProjectID = null, Scopes = null, UniverseDomain = null, }; - Assert.Null(model.Credentials); - Assert.False(model.RawData.ContainsKey("credentials")); Assert.Null(model.ProjectID); Assert.False(model.RawData.ContainsKey("projectId")); Assert.Null(model.Scopes); @@ -588,10 +901,24 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() [Fact] public void OptionalNonNullablePropertiesSetToNullValidation_Works() { - var model = new GoogleAuthOptions + var model = new ModelConfigVertexModelConfigObjectAuth { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + // Null should be interpreted as omitted for these properties - Credentials = null, ProjectID = null, Scopes = null, UniverseDomain = null, @@ -603,7 +930,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() [Fact] public void CopyConstructor_Works() { - var model = new GoogleAuthOptions + var model = new ModelConfigVertexModelConfigObjectAuth { Credentials = new() { @@ -616,7 +943,7 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", @@ -624,18 +951,18 @@ public void CopyConstructor_Works() UniverseDomain = "universeDomain", }; - GoogleAuthOptions copied = new(model); + ModelConfigVertexModelConfigObjectAuth copied = new(model); Assert.Equal(model, copied); } } -public class CredentialsTest : TestBase +public class ModelConfigVertexModelConfigObjectAuthCredentialsTest : TestBase { [Fact] public void FieldRoundtrip_Works() { - var model = new Credentials + var model = new ModelConfigVertexModelConfigObjectAuthCredentials { ClientEmail = "client_email", PrivateKey = "private_key", @@ -646,7 +973,7 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }; @@ -659,7 +986,8 @@ public void FieldRoundtrip_Works() string expectedPrivateKeyID = "private_key_id"; string expectedProjectID = "project_id"; string expectedTokenUri = "https://example.com"; - ApiEnum expectedType = CredentialsType.ServiceAccount; + ApiEnum expectedType = + ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount; string expectedUniverseDomain = "universe_domain"; Assert.Equal(expectedClientEmail, model.ClientEmail); @@ -678,7 +1006,7 @@ public void FieldRoundtrip_Works() [Fact] public void SerializationRoundtrip_Works() { - var model = new Credentials + var model = new ModelConfigVertexModelConfigObjectAuthCredentials { ClientEmail = "client_email", PrivateKey = "private_key", @@ -689,15 +1017,16 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }; string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( - json, - ModelBase.SerializerOptions - ); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); Assert.Equal(model, deserialized); } @@ -705,7 +1034,7 @@ public void SerializationRoundtrip_Works() [Fact] public void FieldRoundtripThroughSerialization_Works() { - var model = new Credentials + var model = new ModelConfigVertexModelConfigObjectAuthCredentials { ClientEmail = "client_email", PrivateKey = "private_key", @@ -716,15 +1045,16 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }; string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( - element, - ModelBase.SerializerOptions - ); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); Assert.NotNull(deserialized); string expectedClientEmail = "client_email"; @@ -736,7 +1066,8 @@ public void FieldRoundtripThroughSerialization_Works() string expectedPrivateKeyID = "private_key_id"; string expectedProjectID = "project_id"; string expectedTokenUri = "https://example.com"; - ApiEnum expectedType = CredentialsType.ServiceAccount; + ApiEnum expectedType = + ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount; string expectedUniverseDomain = "universe_domain"; Assert.Equal(expectedClientEmail, deserialized.ClientEmail); @@ -755,7 +1086,7 @@ public void FieldRoundtripThroughSerialization_Works() [Fact] public void Validation_Works() { - var model = new Credentials + var model = new ModelConfigVertexModelConfigObjectAuthCredentials { ClientEmail = "client_email", PrivateKey = "private_key", @@ -766,7 +1097,7 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }; @@ -776,7 +1107,11 @@ public void Validation_Works() [Fact] public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() { - var model = new Credentials { ClientEmail = "client_email", PrivateKey = "private_key" }; + var model = new ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; Assert.Null(model.AuthProviderX509CertUrl); Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); @@ -801,7 +1136,11 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() [Fact] public void OptionalNonNullablePropertiesUnsetValidation_Works() { - var model = new Credentials { ClientEmail = "client_email", PrivateKey = "private_key" }; + var model = new ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; model.Validate(); } @@ -809,7 +1148,7 @@ public void OptionalNonNullablePropertiesUnsetValidation_Works() [Fact] public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() { - var model = new Credentials + var model = new ModelConfigVertexModelConfigObjectAuthCredentials { ClientEmail = "client_email", PrivateKey = "private_key", @@ -849,7 +1188,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() [Fact] public void OptionalNonNullablePropertiesSetToNullValidation_Works() { - var model = new Credentials + var model = new ModelConfigVertexModelConfigObjectAuthCredentials { ClientEmail = "client_email", PrivateKey = "private_key", @@ -872,7 +1211,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() [Fact] public void CopyConstructor_Works() { - var model = new Credentials + var model = new ModelConfigVertexModelConfigObjectAuthCredentials { ClientEmail = "client_email", PrivateKey = "private_key", @@ -883,51 +1222,51 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }; - Credentials copied = new(model); + ModelConfigVertexModelConfigObjectAuthCredentials copied = new(model); Assert.Equal(model, copied); } } -public class CredentialsTypeTest : TestBase +public class ModelConfigVertexModelConfigObjectAuthCredentialsTypeTest : TestBase { [Theory] - [InlineData(CredentialsType.ServiceAccount)] - public void Validation_Works(CredentialsType rawValue) + [InlineData(ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void Validation_Works(ModelConfigVertexModelConfigObjectAuthCredentialsType rawValue) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; value.Validate(); } [Fact] public void InvalidEnumValidationThrows_Works() { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), - ModelBase.SerializerOptions - ); + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); Assert.NotNull(value); Assert.Throws(() => value.Validate()); } [Theory] - [InlineData(CredentialsType.ServiceAccount)] - public void SerializationRoundtrip_Works(CredentialsType rawValue) + [InlineData(ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void SerializationRoundtrip_Works( + ModelConfigVertexModelConfigObjectAuthCredentialsType rawValue + ) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( - json, - ModelBase.SerializerOptions - ); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); Assert.Equal(value, deserialized); } @@ -935,42 +1274,43 @@ public void SerializationRoundtrip_Works(CredentialsType rawValue) [Fact] public void InvalidEnumSerializationRoundtrip_Works() { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), - ModelBase.SerializerOptions - ); + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( - json, - ModelBase.SerializerOptions - ); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); Assert.Equal(value, deserialized); } } -public class ScopesTest : TestBase +public class ModelConfigVertexModelConfigObjectAuthScopesTest : TestBase { [Fact] public void StringValidationWorks() { - Scopes value = "string"; + ModelConfigVertexModelConfigObjectAuthScopes value = "string"; value.Validate(); } [Fact] public void StringsValidationWorks() { - Scopes value = new(["string"]); + ModelConfigVertexModelConfigObjectAuthScopes value = new(["string"]); value.Validate(); } [Fact] public void StringSerializationRoundtripWorks() { - Scopes value = "string"; + ModelConfigVertexModelConfigObjectAuthScopes value = "string"; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); Assert.Equal(value, deserialized); } @@ -978,59 +1318,554 @@ public void StringSerializationRoundtripWorks() [Fact] public void StringsSerializationRoundtripWorks() { - Scopes value = new(["string"]); + ModelConfigVertexModelConfigObjectAuthScopes value = new(["string"]); string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize(element, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); Assert.Equal(value, deserialized); } } -public class ModelConfigProviderTest : TestBase +public class ModelConfigVertexModelConfigObjectProviderOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + ModelConfigVertexModelConfigObjectProviderOptionsVertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, model.Vertex); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ModelConfigVertexModelConfigObjectProviderOptionsVertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, deserialized.Vertex); + } + + [Fact] + public void Validation_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + ModelConfigVertexModelConfigObjectProviderOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ModelConfigVertexModelConfigObjectProviderOptionsVertexTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, model.Location); + Assert.Equal(expectedProject, model.Project); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, deserialized.Location); + Assert.Equal(expectedProject, deserialized.Project); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ModelConfigVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + ModelConfigVertexModelConfigObjectProviderOptionsVertex copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ModelConfigGenericModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ModelConfigGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, + }; + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = + ModelConfigGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, model.ModelName); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + Assert.Equal(expectedProvider, model.Provider); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ModelConfigGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ModelConfigGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = + ModelConfigGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + Assert.Equal(expectedProvider, deserialized.Provider); + } + + [Fact] + public void Validation_Works() + { + var model = new ModelConfigGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ModelConfigGenericModelConfigObject { ModelName = "openai/gpt-5.4-mini" }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ModelConfigGenericModelConfigObject { ModelName = "openai/gpt-5.4-mini" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ModelConfigGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ModelConfigGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ModelConfigGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, + }; + + ModelConfigGenericModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ModelConfigGenericModelConfigObjectProviderTest : TestBase { [Theory] - [InlineData(ModelConfigProvider.OpenAI)] - [InlineData(ModelConfigProvider.Anthropic)] - [InlineData(ModelConfigProvider.Google)] - [InlineData(ModelConfigProvider.Microsoft)] - [InlineData(ModelConfigProvider.Bedrock)] - [InlineData(ModelConfigProvider.Vertex)] - public void Validation_Works(ModelConfigProvider rawValue) + [InlineData(ModelConfigGenericModelConfigObjectProvider.OpenAI)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Anthropic)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Google)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Microsoft)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Bedrock)] + public void Validation_Works(ModelConfigGenericModelConfigObjectProvider rawValue) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; value.Validate(); } [Fact] public void InvalidEnumValidationThrows_Works() { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), - ModelBase.SerializerOptions - ); + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); Assert.NotNull(value); Assert.Throws(() => value.Validate()); } [Theory] - [InlineData(ModelConfigProvider.OpenAI)] - [InlineData(ModelConfigProvider.Anthropic)] - [InlineData(ModelConfigProvider.Google)] - [InlineData(ModelConfigProvider.Microsoft)] - [InlineData(ModelConfigProvider.Bedrock)] - [InlineData(ModelConfigProvider.Vertex)] - public void SerializationRoundtrip_Works(ModelConfigProvider rawValue) + [InlineData(ModelConfigGenericModelConfigObjectProvider.OpenAI)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Anthropic)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Google)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Microsoft)] + [InlineData(ModelConfigGenericModelConfigObjectProvider.Bedrock)] + public void SerializationRoundtrip_Works(ModelConfigGenericModelConfigObjectProvider rawValue) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( - json, - ModelBase.SerializerOptions - ); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); Assert.Equal(value, deserialized); } @@ -1038,15 +1873,13 @@ public void SerializationRoundtrip_Works(ModelConfigProvider rawValue) [Fact] public void InvalidEnumSerializationRoundtrip_Works() { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), - ModelBase.SerializerOptions - ); + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( - json, - ModelBase.SerializerOptions - ); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); Assert.Equal(value, deserialized); } diff --git a/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs index 05e126d..c6a187c 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs @@ -20,12 +20,9 @@ public void FieldRoundtrip_Works() FrameID = "frameId", Options = new() { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -38,17 +35,26 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -72,12 +78,9 @@ public void FieldRoundtrip_Works() string expectedFrameID = "frameId"; Sessions::Options expectedOptions = new() { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -90,17 +93,26 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -171,12 +183,9 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Input = "Click the login button", Options = new() { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -189,17 +198,26 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -231,12 +249,9 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Input = "Click the login button", Options = new() { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -249,17 +264,26 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -346,12 +370,9 @@ public void CopyConstructor_Works() FrameID = "frameId", Options = new() { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -364,17 +385,26 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -463,12 +493,9 @@ public void FieldRoundtrip_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -481,17 +508,26 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -508,12 +544,9 @@ public void FieldRoundtrip_Works() }, }; - Sessions::Model expectedModel = new Sessions::ModelConfig() + Sessions::Model expectedModel = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -526,17 +559,26 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -569,12 +611,9 @@ public void SerializationRoundtrip_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -587,17 +626,26 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -628,12 +676,9 @@ public void FieldRoundtripThroughSerialization_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -646,17 +691,26 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -680,12 +734,9 @@ public void FieldRoundtripThroughSerialization_Works() ); Assert.NotNull(deserialized); - Sessions::Model expectedModel = new Sessions::ModelConfig() + Sessions::Model expectedModel = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -698,17 +749,26 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -741,12 +801,9 @@ public void Validation_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -759,17 +816,26 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -848,12 +914,9 @@ public void CopyConstructor_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + Model = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -866,17 +929,26 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -902,14 +974,11 @@ public void CopyConstructor_Works() public class ModelTest : TestBase { [Fact] - public void ConfigValidationWorks() + public void VertexModelConfigObjectValidationWorks() { - Sessions::Model value = new Sessions::ModelConfig() + Sessions::Model value = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -922,17 +991,40 @@ public void ConfigValidationWorks() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() + { + Sessions::Model value = new Sessions::GenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = Sessions::Provider.OpenAI, }; value.Validate(); } @@ -945,14 +1037,11 @@ public void StringValidationWorks() } [Fact] - public void ConfigSerializationRoundtripWorks() + public void VertexModelConfigObjectSerializationRoundtripWorks() { - Sessions::Model value = new Sessions::ModelConfig() + Sessions::Model value = new Sessions::VertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -965,17 +1054,46 @@ public void ConfigSerializationRoundtripWorks() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = Sessions::CredentialsType.ServiceAccount, + Type = Sessions::Type.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = Sessions::ModelConfigProvider.OpenAI, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GenericModelConfigObjectSerializationRoundtripWorks() + { + Sessions::Model value = new Sessions::GenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = Sessions::Provider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); var deserialized = JsonSerializer.Deserialize( @@ -1000,6 +1118,1746 @@ public void StringSerializationRoundtripWorks() } } +public class VertexModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Sessions::Auth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + Sessions::ProviderOptions expectedProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, model.Auth); + Assert.Equal(expectedModelName, model.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, model.Provider)); + Assert.Equal(expectedProviderOptions, model.ProviderOptions); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Sessions::Auth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + Sessions::ProviderOptions expectedProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, deserialized.Auth); + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, deserialized.Provider)); + Assert.Equal(expectedProviderOptions, deserialized.ProviderOptions); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Sessions::VertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new Sessions::Vertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Sessions::VertexModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AuthTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + Sessions::Credentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + Sessions::Scopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, model.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedScopes, model.Scopes); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Sessions::Credentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + Sessions::Scopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, deserialized.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedScopes, deserialized.Scopes); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Sessions::Auth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + Sessions::Auth copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class CredentialsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = Sessions::Type.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedPrivateKey, model.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, model.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, model.AuthUri); + Assert.Equal(expectedClientID, model.ClientID); + Assert.Equal(expectedClientX509CertUrl, model.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, model.PrivateKeyID); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedTokenUri, model.TokenUri); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = Sessions::Type.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedPrivateKey, deserialized.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, deserialized.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, deserialized.AuthUri); + Assert.Equal(expectedClientID, deserialized.ClientID); + Assert.Equal(expectedClientX509CertUrl, deserialized.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, deserialized.PrivateKeyID); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedTokenUri, deserialized.TokenUri); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Sessions::Credentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = Sessions::Type.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + Sessions::Credentials copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class TypeTest : TestBase +{ + [Theory] + [InlineData(Sessions::Type.ServiceAccount)] + public void Validation_Works(Sessions::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Sessions::Type.ServiceAccount)] + public void SerializationRoundtrip_Works(Sessions::Type rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ScopesTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + Sessions::Scopes value = "string"; + value.Validate(); + } + + [Fact] + public void StringsValidationWorks() + { + Sessions::Scopes value = new(["string"]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + Sessions::Scopes value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + Sessions::Scopes value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ProviderOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Sessions::ProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + Sessions::Vertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, model.Vertex); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Sessions::ProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Sessions::ProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + Sessions::Vertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, deserialized.Vertex); + } + + [Fact] + public void Validation_Works() + { + var model = new Sessions::ProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Sessions::ProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + Sessions::ProviderOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class VertexTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Sessions::Vertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, model.Location); + Assert.Equal(expectedProject, model.Project); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Sessions::Vertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Sessions::Vertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, deserialized.Location); + Assert.Equal(expectedProject, deserialized.Project); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new Sessions::Vertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Sessions::Vertex { Location = "us-central1", Project = "my-gcp-project" }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Sessions::Vertex { Location = "us-central1", Project = "my-gcp-project" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Sessions::Vertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Sessions::Vertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Sessions::Vertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Sessions::Vertex copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class GenericModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new Sessions::GenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = Sessions::Provider.OpenAI, + }; + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = Sessions::Provider.OpenAI; + + Assert.Equal(expectedModelName, model.ModelName); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + Assert.Equal(expectedProvider, model.Provider); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new Sessions::GenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = Sessions::Provider.OpenAI, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new Sessions::GenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = Sessions::Provider.OpenAI, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = Sessions::Provider.OpenAI; + + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + Assert.Equal(expectedProvider, deserialized.Provider); + } + + [Fact] + public void Validation_Works() + { + var model = new Sessions::GenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = Sessions::Provider.OpenAI, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new Sessions::GenericModelConfigObject { ModelName = "openai/gpt-5.4-mini" }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new Sessions::GenericModelConfigObject { ModelName = "openai/gpt-5.4-mini" }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new Sessions::GenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new Sessions::GenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new Sessions::GenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = Sessions::Provider.OpenAI, + }; + + Sessions::GenericModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ProviderTest : TestBase +{ + [Theory] + [InlineData(Sessions::Provider.OpenAI)] + [InlineData(Sessions::Provider.Anthropic)] + [InlineData(Sessions::Provider.Google)] + [InlineData(Sessions::Provider.Microsoft)] + [InlineData(Sessions::Provider.Bedrock)] + public void Validation_Works(Sessions::Provider rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Sessions::Provider.OpenAI)] + [InlineData(Sessions::Provider.Anthropic)] + [InlineData(Sessions::Provider.Google)] + [InlineData(Sessions::Provider.Microsoft)] + [InlineData(Sessions::Provider.Bedrock)] + public void SerializationRoundtrip_Works(Sessions::Provider rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + public class VariableTest : TestBase { [Fact] diff --git a/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs index 116df0d..1de9022 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs @@ -19,12 +19,9 @@ public void FieldRoundtrip_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -37,25 +34,32 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -68,19 +72,29 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -105,12 +119,9 @@ public void FieldRoundtrip_Works() AgentConfig expectedAgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -123,25 +134,32 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -154,19 +172,29 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; ExecuteOptions expectedExecuteOptions = new() @@ -201,12 +229,9 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -219,25 +244,32 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -250,19 +282,29 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -296,12 +338,9 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -314,25 +353,32 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -345,19 +391,29 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -395,12 +451,9 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -413,25 +466,32 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -444,19 +504,29 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -489,12 +559,9 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -507,25 +574,32 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -538,19 +612,29 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -585,12 +669,9 @@ public void Url_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -603,25 +684,32 @@ public void Url_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -634,19 +722,29 @@ public void Url_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -693,12 +791,9 @@ public void AddHeadersToRequest_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -711,25 +806,32 @@ public void AddHeadersToRequest_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -742,19 +844,29 @@ public void AddHeadersToRequest_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -795,12 +907,9 @@ public void CopyConstructor_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -813,25 +922,32 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -844,19 +960,29 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -891,12 +1017,9 @@ public void FieldRoundtrip_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -909,25 +1032,32 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -940,29 +1070,36 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; bool expectedCua = true; - ExecutionModel expectedExecutionModel = new ModelConfig() + ExecutionModel expectedExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -975,25 +1112,31 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; ApiEnum expectedMode = Mode.Cua; - AgentConfigModel expectedModel = new ModelConfig() + AgentConfigModel expectedModel = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1006,19 +1149,29 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; - ApiEnum expectedProvider = Provider.OpenAI; + ApiEnum expectedProvider = AgentConfigProvider.OpenAI; string expectedSystemPrompt = "systemPrompt"; Assert.Equal(expectedCua, model.Cua); @@ -1035,12 +1188,9 @@ public void SerializationRoundtrip_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1053,25 +1203,32 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1084,19 +1241,29 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -1115,12 +1282,9 @@ public void FieldRoundtripThroughSerialization_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1133,25 +1297,32 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1164,19 +1335,29 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -1188,12 +1369,9 @@ public void FieldRoundtripThroughSerialization_Works() Assert.NotNull(deserialized); bool expectedCua = true; - ExecutionModel expectedExecutionModel = new ModelConfig() + ExecutionModel expectedExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1206,25 +1384,31 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; - ApiEnum expectedMode = Mode.Cua; - AgentConfigModel expectedModel = new ModelConfig() - { ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Headers = new Dictionary() { { "foo", "string" } }, + }; + ApiEnum expectedMode = Mode.Cua; + AgentConfigModel expectedModel = new AgentConfigModelVertexModelConfigObject() + { + Auth = new() { Credentials = new() { @@ -1237,19 +1421,29 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; - ApiEnum expectedProvider = Provider.OpenAI; + ApiEnum expectedProvider = AgentConfigProvider.OpenAI; string expectedSystemPrompt = "systemPrompt"; Assert.Equal(expectedCua, deserialized.Cua); @@ -1266,12 +1460,9 @@ public void Validation_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1284,25 +1475,32 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1315,19 +1513,29 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -1412,12 +1620,9 @@ public void CopyConstructor_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1430,25 +1635,32 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1461,19 +1673,29 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -1486,14 +1708,11 @@ public void CopyConstructor_Works() public class ExecutionModelTest : TestBase { [Fact] - public void ModelConfigValidationWorks() + public void VertexModelConfigObjectValidationWorks() { - ExecutionModel value = new ModelConfig() + ExecutionModel value = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1506,17 +1725,40 @@ public void ModelConfigValidationWorks() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() + { + ExecutionModel value = new ExecutionModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, }; value.Validate(); } @@ -1529,14 +1771,11 @@ public void StringValidationWorks() } [Fact] - public void ModelConfigSerializationRoundtripWorks() + public void VertexModelConfigObjectSerializationRoundtripWorks() { - ExecutionModel value = new ModelConfig() + ExecutionModel value = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1549,17 +1788,26 @@ public void ModelConfigSerializationRoundtripWorks() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); var deserialized = JsonSerializer.Deserialize( @@ -1571,9 +1819,16 @@ public void ModelConfigSerializationRoundtripWorks() } [Fact] - public void StringSerializationRoundtripWorks() + public void GenericModelConfigObjectSerializationRoundtripWorks() { - ExecutionModel value = "string"; + ExecutionModel value = new ExecutionModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, + }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); var deserialized = JsonSerializer.Deserialize( element, @@ -1582,79 +1837,120 @@ public void StringSerializationRoundtripWorks() Assert.Equal(value, deserialized); } -} - -public class ModeTest : TestBase -{ - [Theory] - [InlineData(Mode.Dom)] - [InlineData(Mode.Hybrid)] - [InlineData(Mode.Cua)] - public void Validation_Works(Mode rawValue) - { - // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; - value.Validate(); - } [Fact] - public void InvalidEnumValidationThrows_Works() - { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), - ModelBase.SerializerOptions - ); - - Assert.NotNull(value); - Assert.Throws(() => value.Validate()); - } - - [Theory] - [InlineData(Mode.Dom)] - [InlineData(Mode.Hybrid)] - [InlineData(Mode.Cua)] - public void SerializationRoundtrip_Works(Mode rawValue) + public void StringSerializationRoundtripWorks() { - // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; - - string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( - json, + ExecutionModel value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, ModelBase.SerializerOptions ); Assert.Equal(value, deserialized); } +} +public class ExecutionModelVertexModelConfigObjectTest : TestBase +{ [Fact] - public void InvalidEnumSerializationRoundtrip_Works() + public void FieldRoundtrip_Works() { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), - ModelBase.SerializerOptions - ); - string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( - json, - ModelBase.SerializerOptions + var model = new ExecutionModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + ExecutionModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + ExecutionModelVertexModelConfigObjectProviderOptions expectedProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, model.Auth); + Assert.Equal(expectedModelName, model.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, model.Provider)); + Assert.Equal(expectedProviderOptions, model.ProviderOptions); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); - Assert.Equal(value, deserialized); + Assert.Equal(value, model.Headers[item.Key]); + } } -} -public class AgentConfigModelTest : TestBase -{ [Fact] - public void ConfigValidationWorks() + public void SerializationRoundtrip_Works() { - AgentConfigModel value = new ModelConfig() + var model = new ExecutionModelVertexModelConfigObject { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1667,37 +1963,43 @@ public void ConfigValidationWorks() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; - value.Validate(); - } - [Fact] - public void StringValidationWorks() - { - AgentConfigModel value = "string"; - value.Validate(); + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); } [Fact] - public void ConfigSerializationRoundtripWorks() + public void FieldRoundtripThroughSerialization_Works() { - AgentConfigModel value = new ModelConfig() + var model = new ExecutionModelVertexModelConfigObject { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -1710,60 +2012,3611 @@ public void ConfigSerializationRoundtripWorks() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; - string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( element, ModelBase.SerializerOptions ); + Assert.NotNull(deserialized); - Assert.Equal(value, deserialized); + ExecutionModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + ExecutionModelVertexModelConfigObjectProviderOptions expectedProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, deserialized.Auth); + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, deserialized.Provider)); + Assert.Equal(expectedProviderOptions, deserialized.ProviderOptions); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExecutionModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExecutionModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + ExecutionModelVertexModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExecutionModelVertexModelConfigObjectAuthTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + ExecutionModelVertexModelConfigObjectAuthCredentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + ExecutionModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, model.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedScopes, model.Scopes); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ExecutionModelVertexModelConfigObjectAuthCredentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + ExecutionModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, deserialized.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedScopes, deserialized.Scopes); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + ExecutionModelVertexModelConfigObjectAuth copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExecutionModelVertexModelConfigObjectAuthCredentialsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedPrivateKey, model.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, model.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, model.AuthUri); + Assert.Equal(expectedClientID, model.ClientID); + Assert.Equal(expectedClientX509CertUrl, model.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, model.PrivateKeyID); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedTokenUri, model.TokenUri); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedPrivateKey, deserialized.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, deserialized.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, deserialized.AuthUri); + Assert.Equal(expectedClientID, deserialized.ClientID); + Assert.Equal(expectedClientX509CertUrl, deserialized.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, deserialized.PrivateKeyID); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedTokenUri, deserialized.TokenUri); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExecutionModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + ExecutionModelVertexModelConfigObjectAuthCredentials copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExecutionModelVertexModelConfigObjectAuthCredentialsTypeTest : TestBase +{ + [Theory] + [InlineData(ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void Validation_Works(ExecutionModelVertexModelConfigObjectAuthCredentialsType rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void SerializationRoundtrip_Works( + ExecutionModelVertexModelConfigObjectAuthCredentialsType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class ExecutionModelVertexModelConfigObjectAuthScopesTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ExecutionModelVertexModelConfigObjectAuthScopes value = "string"; + value.Validate(); + } + + [Fact] + public void StringsValidationWorks() + { + ExecutionModelVertexModelConfigObjectAuthScopes value = new(["string"]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ExecutionModelVertexModelConfigObjectAuthScopes value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + ExecutionModelVertexModelConfigObjectAuthScopes value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class ExecutionModelVertexModelConfigObjectProviderOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + ExecutionModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, model.Vertex); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ExecutionModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, deserialized.Vertex); + } + + [Fact] + public void Validation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + ExecutionModelVertexModelConfigObjectProviderOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExecutionModelVertexModelConfigObjectProviderOptionsVertexTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, model.Location); + Assert.Equal(expectedProject, model.Project); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, deserialized.Location); + Assert.Equal(expectedProject, deserialized.Project); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExecutionModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + ExecutionModelVertexModelConfigObjectProviderOptionsVertex copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExecutionModelGenericModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, + }; + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = + ExecutionModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, model.ModelName); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + Assert.Equal(expectedProvider, model.Provider); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = + ExecutionModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + Assert.Equal(expectedProvider, deserialized.Provider); + } + + [Fact] + public void Validation_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new ExecutionModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, + }; + + ExecutionModelGenericModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ExecutionModelGenericModelConfigObjectProviderTest : TestBase +{ + [Theory] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Google)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Bedrock)] + public void Validation_Works(ExecutionModelGenericModelConfigObjectProvider rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Google)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(ExecutionModelGenericModelConfigObjectProvider.Bedrock)] + public void SerializationRoundtrip_Works( + ExecutionModelGenericModelConfigObjectProvider rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class ModeTest : TestBase +{ + [Theory] + [InlineData(Mode.Dom)] + [InlineData(Mode.Hybrid)] + [InlineData(Mode.Cua)] + public void Validation_Works(Mode rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(Mode.Dom)] + [InlineData(Mode.Hybrid)] + [InlineData(Mode.Cua)] + public void SerializationRoundtrip_Works(Mode rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize>( + JsonSerializer.SerializeToElement("invalid value"), + ModelBase.SerializerOptions + ); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize>( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AgentConfigModelTest : TestBase +{ + [Fact] + public void VertexModelConfigObjectValidationWorks() + { + AgentConfigModel value = new AgentConfigModelVertexModelConfigObject() + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() + { + AgentConfigModel value = new AgentConfigModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + }; + value.Validate(); + } + + [Fact] + public void StringValidationWorks() + { + AgentConfigModel value = "string"; + value.Validate(); + } + + [Fact] + public void VertexModelConfigObjectSerializationRoundtripWorks() + { + AgentConfigModel value = new AgentConfigModelVertexModelConfigObject() + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GenericModelConfigObjectSerializationRoundtripWorks() + { + AgentConfigModel value = new AgentConfigModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + AgentConfigModel value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AgentConfigModelVertexModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + AgentConfigModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + AgentConfigModelVertexModelConfigObjectProviderOptions expectedProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, model.Auth); + Assert.Equal(expectedModelName, model.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, model.Provider)); + Assert.Equal(expectedProviderOptions, model.ProviderOptions); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + AgentConfigModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + AgentConfigModelVertexModelConfigObjectProviderOptions expectedProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, deserialized.Auth); + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, deserialized.Provider)); + Assert.Equal(expectedProviderOptions, deserialized.ProviderOptions); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentConfigModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + AgentConfigModelVertexModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AgentConfigModelVertexModelConfigObjectAuthTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + AgentConfigModelVertexModelConfigObjectAuthCredentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + AgentConfigModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, model.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedScopes, model.Scopes); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + AgentConfigModelVertexModelConfigObjectAuthCredentials expectedCredentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + AgentConfigModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, deserialized.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedScopes, deserialized.Scopes); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + AgentConfigModelVertexModelConfigObjectAuth copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AgentConfigModelVertexModelConfigObjectAuthCredentialsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedPrivateKey, model.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, model.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, model.AuthUri); + Assert.Equal(expectedClientID, model.ClientID); + Assert.Equal(expectedClientX509CertUrl, model.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, model.PrivateKeyID); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedTokenUri, model.TokenUri); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum expectedType = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedPrivateKey, deserialized.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, deserialized.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, deserialized.AuthUri); + Assert.Equal(expectedClientID, deserialized.ClientID); + Assert.Equal(expectedClientX509CertUrl, deserialized.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, deserialized.PrivateKeyID); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedTokenUri, deserialized.TokenUri); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + AgentConfigModelVertexModelConfigObjectAuthCredentials copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AgentConfigModelVertexModelConfigObjectAuthCredentialsTypeTest : TestBase +{ + [Theory] + [InlineData(AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void Validation_Works( + AgentConfigModelVertexModelConfigObjectAuthCredentialsType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = + rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void SerializationRoundtrip_Works( + AgentConfigModelVertexModelConfigObjectAuthCredentialsType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = + rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class AgentConfigModelVertexModelConfigObjectAuthScopesTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + AgentConfigModelVertexModelConfigObjectAuthScopes value = "string"; + value.Validate(); + } + + [Fact] + public void StringsValidationWorks() + { + AgentConfigModelVertexModelConfigObjectAuthScopes value = new(["string"]); + value.Validate(); } [Fact] public void StringSerializationRoundtripWorks() { - AgentConfigModel value = "string"; - string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + AgentConfigModelVertexModelConfigObjectAuthScopes value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + AgentConfigModelVertexModelConfigObjectAuthScopes value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class AgentConfigModelVertexModelConfigObjectProviderOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + AgentConfigModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, model.Vertex); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + AgentConfigModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, deserialized.Vertex); + } + + [Fact] + public void Validation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + AgentConfigModelVertexModelConfigObjectProviderOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AgentConfigModelVertexModelConfigObjectProviderOptionsVertexTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, model.Location); + Assert.Equal(expectedProject, model.Project); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, deserialized.Location); + Assert.Equal(expectedProject, deserialized.Project); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + AgentConfigModelVertexModelConfigObjectProviderOptionsVertex copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AgentConfigModelGenericModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + }; + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = + AgentConfigModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, model.ModelName); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + Assert.Equal(expectedProvider, model.Provider); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( element, ModelBase.SerializerOptions ); + Assert.NotNull(deserialized); + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum expectedProvider = + AgentConfigModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + Assert.Equal(expectedProvider, deserialized.Provider); + } + + [Fact] + public void Validation_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new AgentConfigModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + }; + + AgentConfigModelGenericModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class AgentConfigModelGenericModelConfigObjectProviderTest : TestBase +{ + [Theory] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Google)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Bedrock)] + public void Validation_Works(AgentConfigModelGenericModelConfigObjectProvider rawValue) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Google)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(AgentConfigModelGenericModelConfigObjectProvider.Bedrock)] + public void SerializationRoundtrip_Works( + AgentConfigModelGenericModelConfigObjectProvider rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); Assert.Equal(value, deserialized); } } -public class ProviderTest : TestBase +public class AgentConfigProviderTest : TestBase { [Theory] - [InlineData(Provider.OpenAI)] - [InlineData(Provider.Anthropic)] - [InlineData(Provider.Google)] - [InlineData(Provider.Microsoft)] - [InlineData(Provider.Bedrock)] - public void Validation_Works(Provider rawValue) + [InlineData(AgentConfigProvider.OpenAI)] + [InlineData(AgentConfigProvider.Anthropic)] + [InlineData(AgentConfigProvider.Google)] + [InlineData(AgentConfigProvider.Microsoft)] + [InlineData(AgentConfigProvider.Bedrock)] + public void Validation_Works(AgentConfigProvider rawValue) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; value.Validate(); } [Fact] public void InvalidEnumValidationThrows_Works() { - var value = JsonSerializer.Deserialize>( + var value = JsonSerializer.Deserialize>( JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions ); @@ -1773,18 +5626,18 @@ public void InvalidEnumValidationThrows_Works() } [Theory] - [InlineData(Provider.OpenAI)] - [InlineData(Provider.Anthropic)] - [InlineData(Provider.Google)] - [InlineData(Provider.Microsoft)] - [InlineData(Provider.Bedrock)] - public void SerializationRoundtrip_Works(Provider rawValue) + [InlineData(AgentConfigProvider.OpenAI)] + [InlineData(AgentConfigProvider.Anthropic)] + [InlineData(AgentConfigProvider.Google)] + [InlineData(AgentConfigProvider.Microsoft)] + [InlineData(AgentConfigProvider.Bedrock)] + public void SerializationRoundtrip_Works(AgentConfigProvider rawValue) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( + var deserialized = JsonSerializer.Deserialize>( json, ModelBase.SerializerOptions ); @@ -1795,12 +5648,12 @@ public void SerializationRoundtrip_Works(Provider rawValue) [Fact] public void InvalidEnumSerializationRoundtrip_Works() { - var value = JsonSerializer.Deserialize>( + var value = JsonSerializer.Deserialize>( JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions ); string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( + var deserialized = JsonSerializer.Deserialize>( json, ModelBase.SerializerOptions ); diff --git a/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs index db68005..3c3947f 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs @@ -21,12 +21,9 @@ public void FieldRoundtrip_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -39,17 +36,27 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -68,12 +75,9 @@ public void FieldRoundtrip_Works() SessionExtractParamsOptions expectedOptions = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -86,17 +90,27 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -178,12 +192,9 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -196,17 +207,27 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -233,12 +254,9 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -251,17 +269,27 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -338,12 +366,9 @@ public void CopyConstructor_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -356,17 +381,27 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -393,12 +428,9 @@ public void FieldRoundtrip_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -411,17 +443,27 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -429,36 +471,44 @@ public void FieldRoundtrip_Works() }; List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; - SessionExtractParamsOptionsModel expectedModel = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + SessionExtractParamsOptionsModel expectedModel = + new SessionExtractParamsOptionsModelVertexModelConfigObject() { - Credentials = new() + Auth = new() { - ClientEmail = "client_email", - PrivateKey = "private_key", - AuthProviderX509CertUrl = "https://example.com", - AuthUri = "https://example.com", - ClientID = "client_id", - ClientX509CertUrl = "https://example.com", - PrivateKeyID = "private_key_id", - ProjectID = "project_id", - TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, - UniverseDomain = "universe_domain", + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", }, - ProjectID = "projectId", - Scopes = "string", - UniverseDomain = "universeDomain", - }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; bool expectedScreenshot = false; string expectedSelector = "#main-content"; double expectedTimeout = 30000; @@ -481,12 +531,9 @@ public void SerializationRoundtrip_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -499,17 +546,27 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -531,12 +588,9 @@ public void FieldRoundtripThroughSerialization_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -549,17 +603,27 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -574,36 +638,44 @@ public void FieldRoundtripThroughSerialization_Works() Assert.NotNull(deserialized); List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; - SessionExtractParamsOptionsModel expectedModel = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + SessionExtractParamsOptionsModel expectedModel = + new SessionExtractParamsOptionsModelVertexModelConfigObject() { - Credentials = new() + Auth = new() { - ClientEmail = "client_email", - PrivateKey = "private_key", - AuthProviderX509CertUrl = "https://example.com", - AuthUri = "https://example.com", - ClientID = "client_id", - ClientX509CertUrl = "https://example.com", - PrivateKeyID = "private_key_id", - ProjectID = "project_id", - TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, - UniverseDomain = "universe_domain", + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", }, - ProjectID = "projectId", - Scopes = "string", - UniverseDomain = "universeDomain", - }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; bool expectedScreenshot = false; string expectedSelector = "#main-content"; double expectedTimeout = 30000; @@ -626,12 +698,9 @@ public void Validation_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -644,17 +713,27 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -736,12 +815,9 @@ public void CopyConstructor_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionExtractParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -754,17 +830,27 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Screenshot = false, Selector = "#main-content", @@ -780,38 +866,61 @@ public void CopyConstructor_Works() public class SessionExtractParamsOptionsModelTest : TestBase { [Fact] - public void ConfigValidationWorks() + public void VertexModelConfigObjectValidationWorks() { - SessionExtractParamsOptionsModel value = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + SessionExtractParamsOptionsModel value = + new SessionExtractParamsOptionsModelVertexModelConfigObject() { - Credentials = new() + Auth = new() { - ClientEmail = "client_email", - PrivateKey = "private_key", - AuthProviderX509CertUrl = "https://example.com", - AuthUri = "https://example.com", - ClientID = "client_id", - ClientX509CertUrl = "https://example.com", - PrivateKeyID = "private_key_id", - ProjectID = "project_id", - TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, - UniverseDomain = "universe_domain", + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", }, - ProjectID = "projectId", - Scopes = "string", - UniverseDomain = "universeDomain", - }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() + { + SessionExtractParamsOptionsModel value = + new SessionExtractParamsOptionsModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; value.Validate(); } @@ -823,38 +932,67 @@ public void StringValidationWorks() } [Fact] - public void ConfigSerializationRoundtripWorks() + public void VertexModelConfigObjectSerializationRoundtripWorks() { - SessionExtractParamsOptionsModel value = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + SessionExtractParamsOptionsModel value = + new SessionExtractParamsOptionsModelVertexModelConfigObject() { - Credentials = new() + Auth = new() { - ClientEmail = "client_email", - PrivateKey = "private_key", - AuthProviderX509CertUrl = "https://example.com", - AuthUri = "https://example.com", - ClientID = "client_id", - ClientX509CertUrl = "https://example.com", - PrivateKeyID = "private_key_id", - ProjectID = "project_id", - TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, - UniverseDomain = "universe_domain", + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", }, - ProjectID = "projectId", - Scopes = "string", - UniverseDomain = "universeDomain", - }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GenericModelConfigObjectSerializationRoundtripWorks() + { + SessionExtractParamsOptionsModel value = + new SessionExtractParamsOptionsModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); var deserialized = JsonSerializer.Deserialize( element, @@ -878,6 +1016,1851 @@ public void StringSerializationRoundtripWorks() } } +public class SessionExtractParamsOptionsModelVertexModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + SessionExtractParamsOptionsModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions expectedProviderOptions = + new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, model.Auth); + Assert.Equal(expectedModelName, model.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, model.Provider)); + Assert.Equal(expectedProviderOptions, model.ProviderOptions); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + SessionExtractParamsOptionsModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions expectedProviderOptions = + new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, deserialized.Auth); + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, deserialized.Provider)); + Assert.Equal(expectedProviderOptions, deserialized.ProviderOptions); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + SessionExtractParamsOptionsModelVertexModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials expectedCredentials = + new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, model.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedScopes, model.Scopes); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials expectedCredentials = + new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, deserialized.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedScopes, deserialized.Scopes); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + SessionExtractParamsOptionsModelVertexModelConfigObjectAuth copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > expectedType = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedPrivateKey, model.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, model.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, model.AuthUri); + Assert.Equal(expectedClientID, model.ClientID); + Assert.Equal(expectedClientX509CertUrl, model.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, model.PrivateKeyID); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedTokenUri, model.TokenUri); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > expectedType = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedPrivateKey, deserialized.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, deserialized.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, deserialized.AuthUri); + Assert.Equal(expectedClientID, deserialized.ClientID); + Assert.Equal(expectedClientX509CertUrl, deserialized.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, deserialized.PrivateKeyID); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedTokenUri, deserialized.TokenUri); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsTypeTest + : TestBase +{ + [Theory] + [InlineData( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount + )] + public void Validation_Works( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount + )] + public void SerializationRoundtrip_Works( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopesTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes value = "string"; + value.Validate(); + } + + [Fact] + public void StringsValidationWorks() + { + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes value = new(["string"]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = + new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, model.Vertex); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = + new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, deserialized.Vertex); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertexTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, model.Location); + Assert.Equal(expectedProject, model.Project); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, deserialized.Location); + Assert.Equal(expectedProject, deserialized.Project); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex copied = new( + model + ); + + Assert.Equal(model, copied); + } +} + +public class SessionExtractParamsOptionsModelGenericModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum< + string, + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider + > expectedProvider = + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, model.ModelName); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + Assert.Equal(expectedProvider, model.Provider); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum< + string, + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider + > expectedProvider = + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + Assert.Equal(expectedProvider, deserialized.Provider); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionExtractParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + SessionExtractParamsOptionsModelGenericModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionExtractParamsOptionsModelGenericModelConfigObjectProviderTest : TestBase +{ + [Theory] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Google)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Bedrock)] + public void Validation_Works( + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = + rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Google)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Bedrock)] + public void SerializationRoundtrip_Works( + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = + rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + public class SessionExtractParamsXStreamResponseTest : TestBase { [Theory] diff --git a/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs index d180d7f..b5a6a9a 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs @@ -21,12 +21,9 @@ public void FieldRoundtrip_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -39,17 +36,27 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -75,12 +82,9 @@ public void FieldRoundtrip_Works() SessionObserveParamsOptions expectedOptions = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -93,17 +97,27 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -179,12 +193,9 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -197,17 +208,27 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -241,12 +262,9 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -259,17 +277,27 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -353,12 +381,9 @@ public void CopyConstructor_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -371,17 +396,27 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -415,12 +450,9 @@ public void FieldRoundtrip_Works() var model = new SessionObserveParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -433,17 +465,27 @@ public void FieldRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -462,36 +504,44 @@ public void FieldRoundtrip_Works() }; List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; - SessionObserveParamsOptionsModel expectedModel = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + SessionObserveParamsOptionsModel expectedModel = + new SessionObserveParamsOptionsModelVertexModelConfigObject() { - Credentials = new() + Auth = new() { - ClientEmail = "client_email", - PrivateKey = "private_key", - AuthProviderX509CertUrl = "https://example.com", - AuthUri = "https://example.com", - ClientID = "client_id", - ClientX509CertUrl = "https://example.com", - PrivateKeyID = "private_key_id", - ProjectID = "project_id", - TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, - UniverseDomain = "universe_domain", + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", }, - ProjectID = "projectId", - Scopes = "string", - UniverseDomain = "universeDomain", - }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; string expectedSelector = "nav"; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -532,12 +582,9 @@ public void SerializationRoundtrip_Works() var model = new SessionObserveParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -550,17 +597,27 @@ public void SerializationRoundtrip_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -593,12 +650,9 @@ public void FieldRoundtripThroughSerialization_Works() var model = new SessionObserveParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -611,17 +665,27 @@ public void FieldRoundtripThroughSerialization_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -647,36 +711,44 @@ public void FieldRoundtripThroughSerialization_Works() Assert.NotNull(deserialized); List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; - SessionObserveParamsOptionsModel expectedModel = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + SessionObserveParamsOptionsModel expectedModel = + new SessionObserveParamsOptionsModelVertexModelConfigObject() { - Credentials = new() + Auth = new() { - ClientEmail = "client_email", - PrivateKey = "private_key", - AuthProviderX509CertUrl = "https://example.com", - AuthUri = "https://example.com", - ClientID = "client_id", - ClientX509CertUrl = "https://example.com", - PrivateKeyID = "private_key_id", - ProjectID = "project_id", - TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, - UniverseDomain = "universe_domain", + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", }, - ProjectID = "projectId", - Scopes = "string", - UniverseDomain = "universeDomain", - }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; string expectedSelector = "nav"; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -717,12 +789,9 @@ public void Validation_Works() var model = new SessionObserveParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -735,17 +804,27 @@ public void Validation_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -838,12 +917,9 @@ public void CopyConstructor_Works() var model = new SessionObserveParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + Model = new SessionObserveParamsOptionsModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -856,17 +932,27 @@ public void CopyConstructor_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -893,57 +979,411 @@ public void CopyConstructor_Works() public class SessionObserveParamsOptionsModelTest : TestBase { [Fact] - public void ConfigValidationWorks() + public void VertexModelConfigObjectValidationWorks() { - SessionObserveParamsOptionsModel value = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + SessionObserveParamsOptionsModel value = + new SessionObserveParamsOptionsModelVertexModelConfigObject() { - Credentials = new() + Auth = new() { - ClientEmail = "client_email", - PrivateKey = "private_key", - AuthProviderX509CertUrl = "https://example.com", - AuthUri = "https://example.com", - ClientID = "client_id", - ClientX509CertUrl = "https://example.com", - PrivateKeyID = "private_key_id", - ProjectID = "project_id", - TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, - UniverseDomain = "universe_domain", + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", }, - ProjectID = "projectId", - Scopes = "string", - UniverseDomain = "universeDomain", - }, - Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, - }; + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; value.Validate(); } [Fact] - public void StringValidationWorks() + public void GenericModelConfigObjectValidationWorks() { - SessionObserveParamsOptionsModel value = "string"; + SessionObserveParamsOptionsModel value = + new SessionObserveParamsOptionsModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; value.Validate(); } [Fact] - public void ConfigSerializationRoundtripWorks() + public void StringValidationWorks() { - SessionObserveParamsOptionsModel value = new ModelConfig() + SessionObserveParamsOptionsModel value = "string"; + value.Validate(); + } + + [Fact] + public void VertexModelConfigObjectSerializationRoundtripWorks() + { + SessionObserveParamsOptionsModel value = + new SessionObserveParamsOptionsModelVertexModelConfigObject() + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GenericModelConfigObjectSerializationRoundtripWorks() + { + SessionObserveParamsOptionsModel value = + new SessionObserveParamsOptionsModelGenericModelConfigObject() + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + SessionObserveParamsOptionsModel value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class SessionObserveParamsOptionsModelVertexModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + SessionObserveParamsOptionsModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions expectedProviderOptions = + new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, model.Auth); + Assert.Equal(expectedModelName, model.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, model.Provider)); + Assert.Equal(expectedProviderOptions, model.ProviderOptions); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), ApiKey = "sk-some-openai-api-key", BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + SessionObserveParamsOptionsModelVertexModelConfigObjectAuth expectedAuth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + string expectedModelName = "openai/gpt-5.4-mini"; + JsonElement expectedProvider = JsonSerializer.SerializeToElement("vertex"); + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions expectedProviderOptions = + new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ); + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedAuth, deserialized.Auth); + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.True(JsonElement.DeepEquals(expectedProvider, deserialized.Provider)); + Assert.Equal(expectedProviderOptions, deserialized.ProviderOptions); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() { Credentials = new() { @@ -956,36 +1396,1579 @@ public void ConfigSerializationRoundtripWorks() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }; - string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( - element, - ModelBase.SerializerOptions - ); - Assert.Equal(value, deserialized); + model.Validate(); } [Fact] - public void StringSerializationRoundtripWorks() + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() { - SessionObserveParamsOptionsModel value = "string"; - string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( - element, - ModelBase.SerializerOptions - ); + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObject + { + Auth = new() + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + SessionObserveParamsOptionsModelVertexModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials expectedCredentials = + new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, model.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, model.Type)); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedScopes, model.Scopes); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials expectedCredentials = + new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes expectedScopes = "string"; + string expectedUniverseDomain = "universeDomain"; + + Assert.Equal(expectedCredentials, deserialized.Credentials); + Assert.True(JsonElement.DeepEquals(expectedType, deserialized.Type)); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedScopes, deserialized.Scopes); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("projectId")); + Assert.Null(model.Scopes); + Assert.False(model.RawData.ContainsKey("scopes")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + + // Null should be interpreted as omitted for these properties + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuth + { + Credentials = new() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + SessionObserveParamsOptionsModelVertexModelConfigObjectAuth copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > expectedType = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, model.ClientEmail); + Assert.Equal(expectedPrivateKey, model.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, model.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, model.AuthUri); + Assert.Equal(expectedClientID, model.ClientID); + Assert.Equal(expectedClientX509CertUrl, model.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, model.PrivateKeyID); + Assert.Equal(expectedProjectID, model.ProjectID); + Assert.Equal(expectedTokenUri, model.TokenUri); + Assert.Equal(expectedType, model.Type); + Assert.Equal(expectedUniverseDomain, model.UniverseDomain); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedClientEmail = "client_email"; + string expectedPrivateKey = "private_key"; + string expectedAuthProviderX509CertUrl = "https://example.com"; + string expectedAuthUri = "https://example.com"; + string expectedClientID = "client_id"; + string expectedClientX509CertUrl = "https://example.com"; + string expectedPrivateKeyID = "private_key_id"; + string expectedProjectID = "project_id"; + string expectedTokenUri = "https://example.com"; + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > expectedType = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount; + string expectedUniverseDomain = "universe_domain"; + + Assert.Equal(expectedClientEmail, deserialized.ClientEmail); + Assert.Equal(expectedPrivateKey, deserialized.PrivateKey); + Assert.Equal(expectedAuthProviderX509CertUrl, deserialized.AuthProviderX509CertUrl); + Assert.Equal(expectedAuthUri, deserialized.AuthUri); + Assert.Equal(expectedClientID, deserialized.ClientID); + Assert.Equal(expectedClientX509CertUrl, deserialized.ClientX509CertUrl); + Assert.Equal(expectedPrivateKeyID, deserialized.PrivateKeyID); + Assert.Equal(expectedProjectID, deserialized.ProjectID); + Assert.Equal(expectedTokenUri, deserialized.TokenUri); + Assert.Equal(expectedType, deserialized.Type); + Assert.Equal(expectedUniverseDomain, deserialized.UniverseDomain); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + Assert.Null(model.AuthProviderX509CertUrl); + Assert.False(model.RawData.ContainsKey("auth_provider_x509_cert_url")); + Assert.Null(model.AuthUri); + Assert.False(model.RawData.ContainsKey("auth_uri")); + Assert.Null(model.ClientID); + Assert.False(model.RawData.ContainsKey("client_id")); + Assert.Null(model.ClientX509CertUrl); + Assert.False(model.RawData.ContainsKey("client_x509_cert_url")); + Assert.Null(model.PrivateKeyID); + Assert.False(model.RawData.ContainsKey("private_key_id")); + Assert.Null(model.ProjectID); + Assert.False(model.RawData.ContainsKey("project_id")); + Assert.Null(model.TokenUri); + Assert.False(model.RawData.ContainsKey("token_uri")); + Assert.Null(model.Type); + Assert.False(model.RawData.ContainsKey("type")); + Assert.Null(model.UniverseDomain); + Assert.False(model.RawData.ContainsKey("universe_domain")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + + // Null should be interpreted as omitted for these properties + AuthProviderX509CertUrl = null, + AuthUri = null, + ClientID = null, + ClientX509CertUrl = null, + PrivateKeyID = null, + ProjectID = null, + TokenUri = null, + Type = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.com", + AuthUri = "https://example.com", + ClientID = "client_id", + ClientX509CertUrl = "https://example.com", + PrivateKeyID = "private_key_id", + ProjectID = "project_id", + TokenUri = "https://example.com", + Type = + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsTypeTest + : TestBase +{ + [Theory] + [InlineData( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount + )] + public void Validation_Works( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > value = rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount + )] + public void SerializationRoundtrip_Works( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > value = rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } +} + +public class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopesTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes value = "string"; + value.Validate(); + } + + [Fact] + public void StringsValidationWorks() + { + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes value = new(["string"]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +public class SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = + new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, model.Vertex); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex expectedVertex = + new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + Assert.Equal(expectedVertex, deserialized.Vertex); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions + { + Vertex = new() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }, + }; + + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertexTest + : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, model.Location); + Assert.Equal(expectedProject, model.Project); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedLocation = "us-central1"; + string expectedProject = "my-gcp-project"; + string expectedBaseUrl = "https://example.com"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + + Assert.Equal(expectedLocation, deserialized.Location); + Assert.Equal(expectedProject, deserialized.Project); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + } + + [Fact] + public void Validation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + + // Null should be interpreted as omitted for these properties + BaseUrl = null, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + }; + + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex copied = new( + model + ); + + Assert.Equal(model, copied); + } +} + +public class SessionObserveParamsOptionsModelGenericModelConfigObjectTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum< + string, + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider + > expectedProvider = + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, model.ModelName); + Assert.Equal(expectedApiKey, model.ApiKey); + Assert.Equal(expectedBaseUrl, model.BaseUrl); + Assert.NotNull(model.Headers); + Assert.Equal(expectedHeaders.Count, model.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(model.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, model.Headers[item.Key]); + } + Assert.Equal(expectedProvider, model.Provider); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + json, + ModelBase.SerializerOptions + ); + + Assert.Equal(model, deserialized); + } + + [Fact] + public void FieldRoundtripThroughSerialization_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = + JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + string expectedModelName = "openai/gpt-5.4-mini"; + string expectedApiKey = "sk-some-openai-api-key"; + string expectedBaseUrl = "https://api.openai.com/v1"; + Dictionary expectedHeaders = new() { { "foo", "string" } }; + ApiEnum< + string, + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider + > expectedProvider = + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI; + + Assert.Equal(expectedModelName, deserialized.ModelName); + Assert.Equal(expectedApiKey, deserialized.ApiKey); + Assert.Equal(expectedBaseUrl, deserialized.BaseUrl); + Assert.NotNull(deserialized.Headers); + Assert.Equal(expectedHeaders.Count, deserialized.Headers.Count); + foreach (var item in expectedHeaders) + { + Assert.True(deserialized.Headers.TryGetValue(item.Key, out var value)); + + Assert.Equal(value, deserialized.Headers[item.Key]); + } + Assert.Equal(expectedProvider, deserialized.Provider); + } + + [Fact] + public void Validation_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + Assert.Null(model.ApiKey); + Assert.False(model.RawData.ContainsKey("apiKey")); + Assert.Null(model.BaseUrl); + Assert.False(model.RawData.ContainsKey("baseURL")); + Assert.Null(model.Headers); + Assert.False(model.RawData.ContainsKey("headers")); + Assert.Null(model.Provider); + Assert.False(model.RawData.ContainsKey("provider")); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullValidation_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + + // Null should be interpreted as omitted for these properties + ApiKey = null, + BaseUrl = null, + Headers = null, + Provider = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + var model = new SessionObserveParamsOptionsModelGenericModelConfigObject + { + ModelName = "openai/gpt-5.4-mini", + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", + Headers = new Dictionary() { { "foo", "string" } }, + Provider = SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + }; + + SessionObserveParamsOptionsModelGenericModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class SessionObserveParamsOptionsModelGenericModelConfigObjectProviderTest : TestBase +{ + [Theory] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Google)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Bedrock)] + public void Validation_Works( + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = + rawValue; + value.Validate(); + } + + [Fact] + public void InvalidEnumValidationThrows_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + + Assert.NotNull(value); + Assert.Throws(() => value.Validate()); + } + + [Theory] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Anthropic)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Google)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Microsoft)] + [InlineData(SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Bedrock)] + public void SerializationRoundtrip_Works( + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider rawValue + ) + { + // force implicit conversion because Theory can't do that for us + ApiEnum value = + rawValue; + + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void InvalidEnumSerializationRoundtrip_Works() + { + var value = JsonSerializer.Deserialize< + ApiEnum + >(JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions); + string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize< + ApiEnum + >(json, ModelBase.SerializerOptions); Assert.Equal(value, deserialized); } diff --git a/src/Stagehand.Tests/Models/Sessions/SessionStartParamsTest.cs b/src/Stagehand.Tests/Models/Sessions/SessionStartParamsTest.cs index 06ee465..b3fa3e6 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionStartParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionStartParamsTest.cs @@ -49,7 +49,7 @@ public void FieldRoundtrip_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }, BrowserbaseSessionCreateParams = new() { @@ -138,7 +138,7 @@ public void FieldRoundtrip_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }; Sessions::BrowserbaseSessionCreateParams expectedBrowserbaseSessionCreateParams = new() { @@ -368,7 +368,7 @@ public void CopyConstructor_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }, BrowserbaseSessionCreateParams = new() { @@ -466,7 +466,7 @@ public void FieldRoundtrip_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }; string expectedCdpUrl = "ws://localhost:9222"; @@ -499,7 +499,7 @@ public void FieldRoundtrip_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }; - ApiEnum expectedType = Sessions::Type.Local; + ApiEnum expectedType = Sessions::BrowserType.Local; Assert.Equal(expectedCdpUrl, model.CdpUrl); Assert.Equal(expectedLaunchOptions, model.LaunchOptions); @@ -541,7 +541,7 @@ public void SerializationRoundtrip_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }; string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); @@ -588,7 +588,7 @@ public void FieldRoundtripThroughSerialization_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }; string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); @@ -628,7 +628,7 @@ public void FieldRoundtripThroughSerialization_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }; - ApiEnum expectedType = Sessions::Type.Local; + ApiEnum expectedType = Sessions::BrowserType.Local; Assert.Equal(expectedCdpUrl, deserialized.CdpUrl); Assert.Equal(expectedLaunchOptions, deserialized.LaunchOptions); @@ -670,7 +670,7 @@ public void Validation_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }; model.Validate(); @@ -765,7 +765,7 @@ public void CopyConstructor_Works() UserDataDir = "userDataDir", Viewport = new() { Height = 0, Width = 0 }, }, - Type = Sessions::Type.Local, + Type = Sessions::BrowserType.Local, }; Sessions::Browser copied = new(model); @@ -1515,22 +1515,22 @@ public void CopyConstructor_Works() } } -public class TypeTest : TestBase +public class BrowserTypeTest : TestBase { [Theory] - [InlineData(Sessions::Type.Local)] - [InlineData(Sessions::Type.Browserbase)] - public void Validation_Works(Sessions::Type rawValue) + [InlineData(Sessions::BrowserType.Local)] + [InlineData(Sessions::BrowserType.Browserbase)] + public void Validation_Works(Sessions::BrowserType rawValue) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; value.Validate(); } [Fact] public void InvalidEnumValidationThrows_Works() { - var value = JsonSerializer.Deserialize>( + var value = JsonSerializer.Deserialize>( JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions ); @@ -1540,15 +1540,15 @@ public void InvalidEnumValidationThrows_Works() } [Theory] - [InlineData(Sessions::Type.Local)] - [InlineData(Sessions::Type.Browserbase)] - public void SerializationRoundtrip_Works(Sessions::Type rawValue) + [InlineData(Sessions::BrowserType.Local)] + [InlineData(Sessions::BrowserType.Browserbase)] + public void SerializationRoundtrip_Works(Sessions::BrowserType rawValue) { // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + ApiEnum value = rawValue; string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( + var deserialized = JsonSerializer.Deserialize>( json, ModelBase.SerializerOptions ); @@ -1559,12 +1559,12 @@ public void SerializationRoundtrip_Works(Sessions::Type rawValue) [Fact] public void InvalidEnumSerializationRoundtrip_Works() { - var value = JsonSerializer.Deserialize>( + var value = JsonSerializer.Deserialize>( JsonSerializer.SerializeToElement("invalid value"), ModelBase.SerializerOptions ); string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( + var deserialized = JsonSerializer.Deserialize>( json, ModelBase.SerializerOptions ); diff --git a/src/Stagehand.Tests/Services/SessionServiceTest.cs b/src/Stagehand.Tests/Services/SessionServiceTest.cs index 902d715..5389ffc 100644 --- a/src/Stagehand.Tests/Services/SessionServiceTest.cs +++ b/src/Stagehand.Tests/Services/SessionServiceTest.cs @@ -53,12 +53,9 @@ public async Task Execute_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -71,25 +68,32 @@ public async Task Execute_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -102,19 +106,29 @@ public async Task Execute_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -146,12 +160,9 @@ public async Task ExecuteStreaming_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = new ExecutionModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -164,25 +175,32 @@ public async Task ExecuteStreaming_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + Model = new AgentConfigModelVertexModelConfigObject() { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - GoogleAuthOptions = new() + Auth = new() { Credentials = new() { @@ -195,19 +213,29 @@ public async Task ExecuteStreaming_Works() PrivateKeyID = "private_key_id", ProjectID = "project_id", TokenUri = "https://example.com", - Type = CredentialsType.ServiceAccount, + Type = + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, UniverseDomain = "universe_domain", }, ProjectID = "projectId", Scopes = "string", UniverseDomain = "universeDomain", }, + ModelName = "openai/gpt-5.4-mini", + ProviderOptions = new( + new AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), + ApiKey = "sk-some-openai-api-key", + BaseUrl = "https://api.openai.com/v1", Headers = new Dictionary() { { "foo", "string" } }, - Location = "us-central1", - Project = "my-gcp-project", - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() diff --git a/src/Stagehand/Core/ModelBase.cs b/src/Stagehand/Core/ModelBase.cs index 652df35..9307f4d 100644 --- a/src/Stagehand/Core/ModelBase.cs +++ b/src/Stagehand/Core/ModelBase.cs @@ -21,21 +21,49 @@ protected ModelBase(ModelBase modelBase) Converters = { new FrozenDictionaryConverterFactory(), - new ApiEnumConverter(), - new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), + new ApiEnumConverter(), + new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), + new ApiEnumConverter< + string, + ExecutionModelVertexModelConfigObjectAuthCredentialsType + >(), + new ApiEnumConverter(), new ApiEnumConverter(), - new ApiEnumConverter(), + new ApiEnumConverter< + string, + AgentConfigModelVertexModelConfigObjectAuthCredentialsType + >(), + new ApiEnumConverter(), + new ApiEnumConverter(), new ApiEnumConverter(), + new ApiEnumConverter< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + >(), + new ApiEnumConverter< + string, + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider + >(), new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), + new ApiEnumConverter< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + >(), + new ApiEnumConverter< + string, + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider + >(), new ApiEnumConverter(), new ApiEnumConverter(), - new ApiEnumConverter(), + new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), new ApiEnumConverter(), diff --git a/src/Stagehand/Models/Sessions/ModelConfig.cs b/src/Stagehand/Models/Sessions/ModelConfig.cs index 60bb520..da083aa 100644 --- a/src/Stagehand/Models/Sessions/ModelConfig.cs +++ b/src/Stagehand/Models/Sessions/ModelConfig.cs @@ -10,242 +10,422 @@ namespace Stagehand.Models.Sessions; -[JsonConverter(typeof(JsonModelConverter))] -public sealed record class ModelConfig : JsonModel +[JsonConverter(typeof(ModelConfigConverter))] +public record class ModelConfig : ModelBase { - /// - /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') - /// - public required string ModelName + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json { get { - this._rawData.Freeze(); - return this._rawData.GetNotNullClass("modelName"); + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); } - init { this._rawData.Set("modelName", value); } } - /// - /// API key for the model provider - /// - public string? ApiKey + public string ModelName { get { - this._rawData.Freeze(); - return this._rawData.GetNullableClass("apiKey"); - } - init - { - if (value == null) - { - return; - } - - this._rawData.Set("apiKey", value); + return Match( + vertexModelConfigObject: (x) => x.ModelName, + genericModelConfigObject: (x) => x.ModelName + ); } } - /// - /// Base URL for the model provider - /// - public string? BaseUrl + public string? ApiKey { get { - this._rawData.Freeze(); - return this._rawData.GetNullableClass("baseURL"); - } - init - { - if (value == null) - { - return; - } - - this._rawData.Set("baseURL", value); + return Match( + vertexModelConfigObject: (x) => x.ApiKey, + genericModelConfigObject: (x) => x.ApiKey + ); } } - /// - /// google-auth-library options used to authenticate Vertex AI models - /// - public GoogleAuthOptions? GoogleAuthOptions + public string? BaseUrl { get { - this._rawData.Freeze(); - return this._rawData.GetNullableClass("googleAuthOptions"); + return Match( + vertexModelConfigObject: (x) => x.BaseUrl, + genericModelConfigObject: (x) => x.BaseUrl + ); } - init - { - if (value == null) - { - return; - } + } - this._rawData.Set("googleAuthOptions", value); - } + public ModelConfig(ModelConfigVertexModelConfigObject value, JsonElement? element = null) + { + this.Value = value; + this._element = element; } - /// - /// Custom headers sent with every request to the model provider - /// - public IReadOnlyDictionary? Headers + public ModelConfig(ModelConfigGenericModelConfigObject value, JsonElement? element = null) { - get - { - this._rawData.Freeze(); - return this._rawData.GetNullableClass>("headers"); - } - init - { - if (value == null) - { - return; - } + this.Value = value; + this._element = element; + } - this._rawData.Set?>( - "headers", - value == null ? null : FrozenDictionary.ToFrozenDictionary(value) - ); - } + public ModelConfig(JsonElement element) + { + this._element = element; } /// - /// Google Cloud location for Vertex AI models + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVertexModelConfigObject(out var value)) { + /// // `value` is of type `ModelConfigVertexModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// /// - public string? Location + public bool TryPickVertexModelConfigObject( + [NotNullWhen(true)] out ModelConfigVertexModelConfigObject? value + ) { - get - { - this._rawData.Freeze(); - return this._rawData.GetNullableClass("location"); - } - init - { - if (value == null) - { - return; - } - - this._rawData.Set("location", value); - } + value = this.Value as ModelConfigVertexModelConfigObject; + return value != null; } /// - /// Google Cloud project ID for Vertex AI models + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickGenericModelConfigObject(out var value)) { + /// // `value` is of type `ModelConfigGenericModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// /// - public string? Project + public bool TryPickGenericModelConfigObject( + [NotNullWhen(true)] out ModelConfigGenericModelConfigObject? value + ) { - get - { - this._rawData.Freeze(); - return this._rawData.GetNullableClass("project"); - } - init - { - if (value == null) - { - return; - } - - this._rawData.Set("project", value); - } + value = this.Value as ModelConfigGenericModelConfigObject; + return value != null; } /// - /// AI provider for the model (or provide a baseURL endpoint instead) + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (ModelConfigVertexModelConfigObject value) => {...}, + /// (ModelConfigGenericModelConfigObject value) => {...} + /// ); + /// + /// /// - public ApiEnum? Provider + public void Switch( + System::Action vertexModelConfigObject, + System::Action genericModelConfigObject + ) { - get - { - this._rawData.Freeze(); - return this._rawData.GetNullableClass>("provider"); - } - init + switch (this.Value) { - if (value == null) - { - return; - } - - this._rawData.Set("provider", value); + case ModelConfigVertexModelConfigObject value: + vertexModelConfigObject(value); + break; + case ModelConfigGenericModelConfigObject value: + genericModelConfigObject(value); + break; + default: + throw new StagehandInvalidDataException( + "Data did not match any variant of ModelConfig" + ); } } - /// - public override void Validate() + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (ModelConfigVertexModelConfigObject value) => {...}, + /// (ModelConfigGenericModelConfigObject value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func vertexModelConfigObject, + System::Func genericModelConfigObject + ) { - _ = this.ModelName; - _ = this.ApiKey; - _ = this.BaseUrl; - this.GoogleAuthOptions?.Validate(); - _ = this.Headers; - _ = this.Location; - _ = this.Project; - this.Provider?.Validate(); + return this.Value switch + { + ModelConfigVertexModelConfigObject value => vertexModelConfigObject(value), + ModelConfigGenericModelConfigObject value => genericModelConfigObject(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of ModelConfig" + ), + }; } - public ModelConfig() { } + public static implicit operator ModelConfig(ModelConfigVertexModelConfigObject value) => + new(value); -#pragma warning disable CS8618 - [SetsRequiredMembers] - public ModelConfig(ModelConfig modelConfig) - : base(modelConfig) { } -#pragma warning restore CS8618 + public static implicit operator ModelConfig(ModelConfigGenericModelConfigObject value) => + new(value); - public ModelConfig(IReadOnlyDictionary rawData) + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() { - this._rawData = new(rawData); + if (this.Value == null) + { + throw new StagehandInvalidDataException( + "Data did not match any variant of ModelConfig" + ); + } + this.Switch( + (vertexModelConfigObject) => vertexModelConfigObject.Validate(), + (genericModelConfigObject) => genericModelConfigObject.Validate() + ); } -#pragma warning disable CS8618 - [SetsRequiredMembers] - ModelConfig(FrozenDictionary rawData) - { - this._rawData = new(rawData); - } -#pragma warning restore CS8618 + public virtual bool Equals(ModelConfig? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); - /// - public static ModelConfig FromRawUnchecked(IReadOnlyDictionary rawData) + public override int GetHashCode() { - return new(FrozenDictionary.ToFrozenDictionary(rawData)); + return 0; } - [SetsRequiredMembers] - public ModelConfig(string modelName) - : this() + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() { - this.ModelName = modelName; + return this.Value switch + { + ModelConfigVertexModelConfigObject _ => 0, + ModelConfigGenericModelConfigObject _ => 1, + _ => -1, + }; } } -class ModelConfigFromRaw : IFromRawJson +sealed class ModelConfigConverter : JsonConverter { - /// - public ModelConfig FromRawUnchecked(IReadOnlyDictionary rawData) => - ModelConfig.FromRawUnchecked(rawData); + public override ModelConfig? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ModelConfig value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } } -/// -/// google-auth-library options used to authenticate Vertex AI models -/// -[JsonConverter(typeof(JsonModelConverter))] -public sealed record class GoogleAuthOptions : JsonModel +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigVertexModelConfigObject, + ModelConfigVertexModelConfigObjectFromRaw + >) +)] +public sealed record class ModelConfigVertexModelConfigObject : JsonModel { /// - /// Google Cloud service account credentials + /// Vertex provider authentication configuration + /// + public required ModelConfigVertexModelConfigObjectAuth Auth + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("auth"); + } + init { this._rawData.Set("auth", value); } + } + + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// Vertex AI model provider + /// + public JsonElement Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("provider"); + } + init { this._rawData.Set("provider", value); } + } + + /// + /// Vertex provider-specific model configuration + /// + public required ModelConfigVertexModelConfigObjectProviderOptions ProviderOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "providerOptions" + ); + } + init { this._rawData.Set("providerOptions", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider /// - public Credentials? Credentials + public IReadOnlyDictionary? Headers { get { this._rawData.Freeze(); - return this._rawData.GetNullableClass("credentials"); + return this._rawData.GetNullableClass>("headers"); } init { @@ -254,8 +434,110 @@ public Credentials? Credentials return; } - this._rawData.Set("credentials", value); + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + this.Auth.Validate(); + _ = this.ModelName; + if (!JsonElement.DeepEquals(this.Provider, JsonSerializer.SerializeToElement("vertex"))) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + this.ProviderOptions.Validate(); + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + } + + public ModelConfigVertexModelConfigObject() + { + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObject( + ModelConfigVertexModelConfigObject modelConfigVertexModelConfigObject + ) + : base(modelConfigVertexModelConfigObject) { } +#pragma warning restore CS8618 + + public ModelConfigVertexModelConfigObject(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ModelConfigVertexModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ModelConfigVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ModelConfigVertexModelConfigObjectFromRaw : IFromRawJson +{ + /// + public ModelConfigVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigVertexModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// Vertex provider authentication configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigVertexModelConfigObjectAuth, + ModelConfigVertexModelConfigObjectAuthFromRaw + >) +)] +public sealed record class ModelConfigVertexModelConfigObjectAuth : JsonModel +{ + /// + /// Google Cloud service account credentials + /// + public required ModelConfigVertexModelConfigObjectAuthCredentials Credentials + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "credentials" + ); + } + init { this._rawData.Set("credentials", value); } + } + + /// + /// Use inline Google Cloud service account credentials for provider authentication + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); } + init { this._rawData.Set("type", value); } } /// @@ -282,12 +564,14 @@ public string? ProjectID /// /// Google auth scopes for the desired API request /// - public Scopes? Scopes + public ModelConfigVertexModelConfigObjectAuthScopes? Scopes { get { this._rawData.Freeze(); - return this._rawData.GetNullableClass("scopes"); + return this._rawData.GetNullableClass( + "scopes" + ); } init { @@ -324,54 +608,86 @@ public string? UniverseDomain /// public override void Validate() { - this.Credentials?.Validate(); + this.Credentials.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("googleServiceAccount") + ) + ) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } _ = this.ProjectID; this.Scopes?.Validate(); _ = this.UniverseDomain; } - public GoogleAuthOptions() { } + public ModelConfigVertexModelConfigObjectAuth() + { + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } #pragma warning disable CS8618 [SetsRequiredMembers] - public GoogleAuthOptions(GoogleAuthOptions googleAuthOptions) - : base(googleAuthOptions) { } + public ModelConfigVertexModelConfigObjectAuth( + ModelConfigVertexModelConfigObjectAuth modelConfigVertexModelConfigObjectAuth + ) + : base(modelConfigVertexModelConfigObjectAuth) { } #pragma warning restore CS8618 - public GoogleAuthOptions(IReadOnlyDictionary rawData) + public ModelConfigVertexModelConfigObjectAuth(IReadOnlyDictionary rawData) { this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); } #pragma warning disable CS8618 [SetsRequiredMembers] - GoogleAuthOptions(FrozenDictionary rawData) + ModelConfigVertexModelConfigObjectAuth(FrozenDictionary rawData) { this._rawData = new(rawData); } #pragma warning restore CS8618 - /// - public static GoogleAuthOptions FromRawUnchecked( + /// + public static ModelConfigVertexModelConfigObjectAuth FromRawUnchecked( IReadOnlyDictionary rawData ) { return new(FrozenDictionary.ToFrozenDictionary(rawData)); } + + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObjectAuth( + ModelConfigVertexModelConfigObjectAuthCredentials credentials + ) + : this() + { + this.Credentials = credentials; + } } -class GoogleAuthOptionsFromRaw : IFromRawJson +class ModelConfigVertexModelConfigObjectAuthFromRaw + : IFromRawJson { /// - public GoogleAuthOptions FromRawUnchecked(IReadOnlyDictionary rawData) => - GoogleAuthOptions.FromRawUnchecked(rawData); + public ModelConfigVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigVertexModelConfigObjectAuth.FromRawUnchecked(rawData); } /// /// Google Cloud service account credentials /// -[JsonConverter(typeof(JsonModelConverter))] -public sealed record class Credentials : JsonModel +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigVertexModelConfigObjectAuthCredentials, + ModelConfigVertexModelConfigObjectAuthCredentialsFromRaw + >) +)] +public sealed record class ModelConfigVertexModelConfigObjectAuthCredentials : JsonModel { public required string ClientEmail { @@ -519,12 +835,14 @@ public string? TokenUri } } - public ApiEnum? Type + public ApiEnum? Type { get { this._rawData.Freeze(); - return this._rawData.GetNullableClass>("type"); + return this._rawData.GetNullableClass< + ApiEnum + >("type"); } init { @@ -571,50 +889,59 @@ public override void Validate() _ = this.UniverseDomain; } - public Credentials() { } + public ModelConfigVertexModelConfigObjectAuthCredentials() { } #pragma warning disable CS8618 [SetsRequiredMembers] - public Credentials(Credentials credentials) - : base(credentials) { } + public ModelConfigVertexModelConfigObjectAuthCredentials( + ModelConfigVertexModelConfigObjectAuthCredentials modelConfigVertexModelConfigObjectAuthCredentials + ) + : base(modelConfigVertexModelConfigObjectAuthCredentials) { } #pragma warning restore CS8618 - public Credentials(IReadOnlyDictionary rawData) + public ModelConfigVertexModelConfigObjectAuthCredentials( + IReadOnlyDictionary rawData + ) { this._rawData = new(rawData); } #pragma warning disable CS8618 [SetsRequiredMembers] - Credentials(FrozenDictionary rawData) + ModelConfigVertexModelConfigObjectAuthCredentials(FrozenDictionary rawData) { this._rawData = new(rawData); } #pragma warning restore CS8618 - /// - public static Credentials FromRawUnchecked(IReadOnlyDictionary rawData) + /// + public static ModelConfigVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) { return new(FrozenDictionary.ToFrozenDictionary(rawData)); } } -class CredentialsFromRaw : IFromRawJson +class ModelConfigVertexModelConfigObjectAuthCredentialsFromRaw + : IFromRawJson { /// - public Credentials FromRawUnchecked(IReadOnlyDictionary rawData) => - Credentials.FromRawUnchecked(rawData); + public ModelConfigVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigVertexModelConfigObjectAuthCredentials.FromRawUnchecked(rawData); } -[JsonConverter(typeof(CredentialsTypeConverter))] -public enum CredentialsType +[JsonConverter(typeof(ModelConfigVertexModelConfigObjectAuthCredentialsTypeConverter))] +public enum ModelConfigVertexModelConfigObjectAuthCredentialsType { ServiceAccount, } -sealed class CredentialsTypeConverter : JsonConverter +sealed class ModelConfigVertexModelConfigObjectAuthCredentialsTypeConverter + : JsonConverter { - public override CredentialsType Read( + public override ModelConfigVertexModelConfigObjectAuthCredentialsType Read( ref Utf8JsonReader reader, System::Type typeToConvert, JsonSerializerOptions options @@ -622,14 +949,15 @@ JsonSerializerOptions options { return JsonSerializer.Deserialize(ref reader, options) switch { - "service_account" => CredentialsType.ServiceAccount, - _ => (CredentialsType)(-1), + "service_account" => + ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + _ => (ModelConfigVertexModelConfigObjectAuthCredentialsType)(-1), }; } public override void Write( Utf8JsonWriter writer, - CredentialsType value, + ModelConfigVertexModelConfigObjectAuthCredentialsType value, JsonSerializerOptions options ) { @@ -637,7 +965,8 @@ JsonSerializerOptions options writer, value switch { - CredentialsType.ServiceAccount => "service_account", + ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount => + "service_account", _ => throw new StagehandInvalidDataException( string.Format("Invalid value '{0}' in {1}", value, nameof(value)) ), @@ -650,8 +979,8 @@ JsonSerializerOptions options /// /// Google auth scopes for the desired API request /// -[JsonConverter(typeof(ScopesConverter))] -public record class Scopes : ModelBase +[JsonConverter(typeof(ModelConfigVertexModelConfigObjectAuthScopesConverter))] +public record class ModelConfigVertexModelConfigObjectAuthScopes : ModelBase { public object? Value { get; } = null; @@ -668,19 +997,22 @@ public JsonElement Json } } - public Scopes(string value, JsonElement? element = null) + public ModelConfigVertexModelConfigObjectAuthScopes(string value, JsonElement? element = null) { this.Value = value; this._element = element; } - public Scopes(IReadOnlyList value, JsonElement? element = null) + public ModelConfigVertexModelConfigObjectAuthScopes( + IReadOnlyList value, + JsonElement? element = null + ) { this.Value = ImmutableArray.ToImmutableArray(value); this._element = element; } - public Scopes(JsonElement element) + public ModelConfigVertexModelConfigObjectAuthScopes(JsonElement element) { this._element = element; } @@ -761,7 +1093,9 @@ public void Switch( strings(value); break; default: - throw new StagehandInvalidDataException("Data did not match any variant of Scopes"); + throw new StagehandInvalidDataException( + "Data did not match any variant of ModelConfigVertexModelConfigObjectAuthScopes" + ); } } @@ -796,14 +1130,17 @@ public T Match( string value => @string(value), IReadOnlyList value => strings(value), _ => throw new StagehandInvalidDataException( - "Data did not match any variant of Scopes" + "Data did not match any variant of ModelConfigVertexModelConfigObjectAuthScopes" ), }; } - public static implicit operator Scopes(string value) => new(value); + public static implicit operator ModelConfigVertexModelConfigObjectAuthScopes(string value) => + new(value); - public static implicit operator Scopes(List value) => new((IReadOnlyList)value); + public static implicit operator ModelConfigVertexModelConfigObjectAuthScopes( + List value + ) => new((IReadOnlyList)value); /// /// Validates that the instance was constructed with a known variant and that this variant is valid @@ -819,11 +1156,13 @@ public override void Validate() { if (this.Value == null) { - throw new StagehandInvalidDataException("Data did not match any variant of Scopes"); + throw new StagehandInvalidDataException( + "Data did not match any variant of ModelConfigVertexModelConfigObjectAuthScopes" + ); } } - public virtual bool Equals(Scopes? other) => + public virtual bool Equals(ModelConfigVertexModelConfigObjectAuthScopes? other) => other != null && this.VariantIndex() == other.VariantIndex() && JsonElement.DeepEquals(this.Json, other.Json); @@ -850,9 +1189,10 @@ int VariantIndex() } } -sealed class ScopesConverter : JsonConverter +sealed class ModelConfigVertexModelConfigObjectAuthScopesConverter + : JsonConverter { - public override Scopes? Read( + public override ModelConfigVertexModelConfigObjectAuthScopes? Read( ref Utf8JsonReader reader, System::Type typeToConvert, JsonSerializerOptions options @@ -888,29 +1228,420 @@ JsonSerializerOptions options return new(element); } - public override void Write(Utf8JsonWriter writer, Scopes value, JsonSerializerOptions options) + public override void Write( + Utf8JsonWriter writer, + ModelConfigVertexModelConfigObjectAuthScopes value, + JsonSerializerOptions options + ) { JsonSerializer.Serialize(writer, value.Json, options); } } +/// +/// Vertex provider-specific model configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigVertexModelConfigObjectProviderOptions, + ModelConfigVertexModelConfigObjectProviderOptionsFromRaw + >) +)] +public sealed record class ModelConfigVertexModelConfigObjectProviderOptions : JsonModel +{ + /// + /// Vertex AI provider-specific settings + /// + public required ModelConfigVertexModelConfigObjectProviderOptionsVertex Vertex + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "vertex" + ); + } + init { this._rawData.Set("vertex", value); } + } + + /// + public override void Validate() + { + this.Vertex.Validate(); + } + + public ModelConfigVertexModelConfigObjectProviderOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObjectProviderOptions( + ModelConfigVertexModelConfigObjectProviderOptions modelConfigVertexModelConfigObjectProviderOptions + ) + : base(modelConfigVertexModelConfigObjectProviderOptions) { } +#pragma warning restore CS8618 + + public ModelConfigVertexModelConfigObjectProviderOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ModelConfigVertexModelConfigObjectProviderOptions(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ModelConfigVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObjectProviderOptions( + ModelConfigVertexModelConfigObjectProviderOptionsVertex vertex + ) + : this() + { + this.Vertex = vertex; + } +} + +class ModelConfigVertexModelConfigObjectProviderOptionsFromRaw + : IFromRawJson +{ + /// + public ModelConfigVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigVertexModelConfigObjectProviderOptions.FromRawUnchecked(rawData); +} + +/// +/// Vertex AI provider-specific settings +/// +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigVertexModelConfigObjectProviderOptionsVertex, + ModelConfigVertexModelConfigObjectProviderOptionsVertexFromRaw + >) +)] +public sealed record class ModelConfigVertexModelConfigObjectProviderOptionsVertex : JsonModel +{ + /// + /// Google Cloud location for Vertex AI models + /// + public required string Location + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("location"); + } + init { this._rawData.Set("location", value); } + } + + /// + /// Google Cloud project ID for Vertex AI models + /// + public required string Project + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("project"); + } + init { this._rawData.Set("project", value); } + } + + /// + /// Base URL for the Vertex AI provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the Vertex AI provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Location; + _ = this.Project; + _ = this.BaseUrl; + _ = this.Headers; + } + + public ModelConfigVertexModelConfigObjectProviderOptionsVertex() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObjectProviderOptionsVertex( + ModelConfigVertexModelConfigObjectProviderOptionsVertex modelConfigVertexModelConfigObjectProviderOptionsVertex + ) + : base(modelConfigVertexModelConfigObjectProviderOptionsVertex) { } +#pragma warning restore CS8618 + + public ModelConfigVertexModelConfigObjectProviderOptionsVertex( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ModelConfigVertexModelConfigObjectProviderOptionsVertex( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ModelConfigVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ModelConfigVertexModelConfigObjectProviderOptionsVertexFromRaw + : IFromRawJson +{ + /// + public ModelConfigVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigVertexModelConfigObjectProviderOptionsVertex.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigGenericModelConfigObject, + ModelConfigGenericModelConfigObjectFromRaw + >) +)] +public sealed record class ModelConfigGenericModelConfigObject : JsonModel +{ + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// AI provider for the model (or provide a baseURL endpoint instead) + /// + public ApiEnum? Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("provider"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("provider", value); + } + } + + /// + public override void Validate() + { + _ = this.ModelName; + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + this.Provider?.Validate(); + } + + public ModelConfigGenericModelConfigObject() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ModelConfigGenericModelConfigObject( + ModelConfigGenericModelConfigObject modelConfigGenericModelConfigObject + ) + : base(modelConfigGenericModelConfigObject) { } +#pragma warning restore CS8618 + + public ModelConfigGenericModelConfigObject(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ModelConfigGenericModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ModelConfigGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ModelConfigGenericModelConfigObject(string modelName) + : this() + { + this.ModelName = modelName; + } +} + +class ModelConfigGenericModelConfigObjectFromRaw : IFromRawJson +{ + /// + public ModelConfigGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigGenericModelConfigObject.FromRawUnchecked(rawData); +} + /// /// AI provider for the model (or provide a baseURL endpoint instead) /// -[JsonConverter(typeof(ModelConfigProviderConverter))] -public enum ModelConfigProvider +[JsonConverter(typeof(ModelConfigGenericModelConfigObjectProviderConverter))] +public enum ModelConfigGenericModelConfigObjectProvider { OpenAI, Anthropic, Google, Microsoft, Bedrock, - Vertex, } -sealed class ModelConfigProviderConverter : JsonConverter +sealed class ModelConfigGenericModelConfigObjectProviderConverter + : JsonConverter { - public override ModelConfigProvider Read( + public override ModelConfigGenericModelConfigObjectProvider Read( ref Utf8JsonReader reader, System::Type typeToConvert, JsonSerializerOptions options @@ -918,19 +1649,18 @@ JsonSerializerOptions options { return JsonSerializer.Deserialize(ref reader, options) switch { - "openai" => ModelConfigProvider.OpenAI, - "anthropic" => ModelConfigProvider.Anthropic, - "google" => ModelConfigProvider.Google, - "microsoft" => ModelConfigProvider.Microsoft, - "bedrock" => ModelConfigProvider.Bedrock, - "vertex" => ModelConfigProvider.Vertex, - _ => (ModelConfigProvider)(-1), + "openai" => ModelConfigGenericModelConfigObjectProvider.OpenAI, + "anthropic" => ModelConfigGenericModelConfigObjectProvider.Anthropic, + "google" => ModelConfigGenericModelConfigObjectProvider.Google, + "microsoft" => ModelConfigGenericModelConfigObjectProvider.Microsoft, + "bedrock" => ModelConfigGenericModelConfigObjectProvider.Bedrock, + _ => (ModelConfigGenericModelConfigObjectProvider)(-1), }; } public override void Write( Utf8JsonWriter writer, - ModelConfigProvider value, + ModelConfigGenericModelConfigObjectProvider value, JsonSerializerOptions options ) { @@ -938,12 +1668,11 @@ JsonSerializerOptions options writer, value switch { - ModelConfigProvider.OpenAI => "openai", - ModelConfigProvider.Anthropic => "anthropic", - ModelConfigProvider.Google => "google", - ModelConfigProvider.Microsoft => "microsoft", - ModelConfigProvider.Bedrock => "bedrock", - ModelConfigProvider.Vertex => "vertex", + ModelConfigGenericModelConfigObjectProvider.OpenAI => "openai", + ModelConfigGenericModelConfigObjectProvider.Anthropic => "anthropic", + ModelConfigGenericModelConfigObjectProvider.Google => "google", + ModelConfigGenericModelConfigObjectProvider.Microsoft => "microsoft", + ModelConfigGenericModelConfigObjectProvider.Bedrock => "bedrock", _ => throw new StagehandInvalidDataException( string.Format("Invalid value '{0}' in {1}", value, nameof(value)) ), diff --git a/src/Stagehand/Models/Sessions/SessionActParams.cs b/src/Stagehand/Models/Sessions/SessionActParams.cs index ccf6932..b3d92a2 100644 --- a/src/Stagehand/Models/Sessions/SessionActParams.cs +++ b/src/Stagehand/Models/Sessions/SessionActParams.cs @@ -1,5 +1,6 @@ using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Text; @@ -597,7 +598,49 @@ public JsonElement Json } } - public Model(ModelConfig value, JsonElement? element = null) + public string? ModelName + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ModelName, + genericModelConfigObject: (x) => x.ModelName, + @string: (_) => null + ); + } + } + + public string? ApiKey + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ApiKey, + genericModelConfigObject: (x) => x.ApiKey, + @string: (_) => null + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + vertexModelConfigObject: (x) => x.BaseUrl, + genericModelConfigObject: (x) => x.BaseUrl, + @string: (_) => null + ); + } + } + + public Model(VertexModelConfigObject value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Model(GenericModelConfigObject value, JsonElement? element = null) { this.Value = value; this._element = element; @@ -616,22 +659,47 @@ public Model(JsonElement element) /// /// Returns true and sets the out parameter if the instance was constructed with a variant of - /// type . + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVertexModelConfigObject(out var value)) { + /// // `value` is of type `VertexModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVertexModelConfigObject( + [NotNullWhen(true)] out VertexModelConfigObject? value + ) + { + value = this.Value as VertexModelConfigObject; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . /// /// Consider using or if you need to handle every variant. /// /// /// - /// if (instance.TryPickConfig(out var value)) { - /// // `value` is of type `ModelConfig` + /// if (instance.TryPickGenericModelConfigObject(out var value)) { + /// // `value` is of type `GenericModelConfigObject` /// Console.WriteLine(value); /// } /// /// /// - public bool TryPickConfig([NotNullWhen(true)] out ModelConfig? value) + public bool TryPickGenericModelConfigObject( + [NotNullWhen(true)] out GenericModelConfigObject? value + ) { - value = this.Value as ModelConfig; + value = this.Value as GenericModelConfigObject; return value != null; } @@ -670,18 +738,26 @@ public bool TryPickString([NotNullWhen(true)] out string? value) /// /// /// instance.Switch( - /// (ModelConfig value) => {...}, + /// (VertexModelConfigObject value) => {...}, + /// (GenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public void Switch(System::Action config, System::Action @string) + public void Switch( + System::Action vertexModelConfigObject, + System::Action genericModelConfigObject, + System::Action @string + ) { switch (this.Value) { - case ModelConfig value: - config(value); + case VertexModelConfigObject value: + vertexModelConfigObject(value); + break; + case GenericModelConfigObject value: + genericModelConfigObject(value); break; case string value: @string(value); @@ -706,23 +782,31 @@ public void Switch(System::Action config, System::Action @s /// /// /// var result = instance.Match( - /// (ModelConfig value) => {...}, + /// (VertexModelConfigObject value) => {...}, + /// (GenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public T Match(System::Func config, System::Func @string) + public T Match( + System::Func vertexModelConfigObject, + System::Func genericModelConfigObject, + System::Func @string + ) { return this.Value switch { - ModelConfig value => config(value), + VertexModelConfigObject value => vertexModelConfigObject(value), + GenericModelConfigObject value => genericModelConfigObject(value), string value => @string(value), _ => throw new StagehandInvalidDataException("Data did not match any variant of Model"), }; } - public static implicit operator Model(ModelConfig value) => new(value); + public static implicit operator Model(VertexModelConfigObject value) => new(value); + + public static implicit operator Model(GenericModelConfigObject value) => new(value); public static implicit operator Model(string value) => new(value); @@ -742,7 +826,11 @@ public override void Validate() { throw new StagehandInvalidDataException("Data did not match any variant of Model"); } - this.Switch((config) => config.Validate(), (_) => { }); + this.Switch( + (vertexModelConfigObject) => vertexModelConfigObject.Validate(), + (genericModelConfigObject) => genericModelConfigObject.Validate(), + (_) => { } + ); } public virtual bool Equals(Model? other) => @@ -765,8 +853,9 @@ int VariantIndex() { return this.Value switch { - ModelConfig _ => 0, - string _ => 1, + VertexModelConfigObject _ => 0, + GenericModelConfigObject _ => 1, + string _ => 2, _ => -1, }; } @@ -783,7 +872,27 @@ JsonSerializerOptions options var element = JsonSerializer.Deserialize(ref reader, options); try { - var deserialized = JsonSerializer.Deserialize(element, options); + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); if (deserialized != null) { deserialized.Validate(); @@ -817,6 +926,1274 @@ public override void Write(Utf8JsonWriter writer, Model value, JsonSerializerOpt } } +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class VertexModelConfigObject : JsonModel +{ + /// + /// Vertex provider authentication configuration + /// + public required Auth Auth + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("auth"); + } + init { this._rawData.Set("auth", value); } + } + + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// Vertex AI model provider + /// + public JsonElement Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("provider"); + } + init { this._rawData.Set("provider", value); } + } + + /// + /// Vertex provider-specific model configuration + /// + public required ProviderOptions ProviderOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("providerOptions"); + } + init { this._rawData.Set("providerOptions", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + this.Auth.Validate(); + _ = this.ModelName; + if (!JsonElement.DeepEquals(this.Provider, JsonSerializer.SerializeToElement("vertex"))) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + this.ProviderOptions.Validate(); + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + } + + public VertexModelConfigObject() + { + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public VertexModelConfigObject(VertexModelConfigObject vertexModelConfigObject) + : base(vertexModelConfigObject) { } +#pragma warning restore CS8618 + + public VertexModelConfigObject(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + VertexModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static VertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VertexModelConfigObjectFromRaw : IFromRawJson +{ + /// + public VertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => VertexModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// Vertex provider authentication configuration +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Auth : JsonModel +{ + /// + /// Google Cloud service account credentials + /// + public required Credentials Credentials + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("credentials"); + } + init { this._rawData.Set("credentials", value); } + } + + /// + /// Use inline Google Cloud service account credentials for provider authentication + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Google Cloud project ID used by google-auth-library + /// + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("projectId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("projectId", value); + } + } + + /// + /// Google auth scopes for the desired API request + /// + public Scopes? Scopes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("scopes"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("scopes", value); + } + } + + /// + /// Google Cloud universe domain + /// + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universeDomain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universeDomain", value); + } + } + + /// + public override void Validate() + { + this.Credentials.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("googleServiceAccount") + ) + ) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + _ = this.ProjectID; + this.Scopes?.Validate(); + _ = this.UniverseDomain; + } + + public Auth() + { + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Auth(Auth auth) + : base(auth) { } +#pragma warning restore CS8618 + + public Auth(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Auth(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Auth FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public Auth(Credentials credentials) + : this() + { + this.Credentials = credentials; + } +} + +class AuthFromRaw : IFromRawJson +{ + /// + public Auth FromRawUnchecked(IReadOnlyDictionary rawData) => + Auth.FromRawUnchecked(rawData); +} + +/// +/// Google Cloud service account credentials +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Credentials : JsonModel +{ + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("client_email"); + } + init { this._rawData.Set("client_email", value); } + } + + public required string PrivateKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("private_key"); + } + init { this._rawData.Set("private_key", value); } + } + + public string? AuthProviderX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_provider_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_provider_x509_cert_url", value); + } + } + + public string? AuthUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_uri", value); + } + } + + public string? ClientID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_id", value); + } + } + + public string? ClientX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_x509_cert_url", value); + } + } + + public string? PrivateKeyID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("private_key_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("private_key_id", value); + } + } + + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("project_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("project_id", value); + } + } + + public string? TokenUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("token_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("token_uri", value); + } + } + + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universe_domain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universe_domain", value); + } + } + + /// + public override void Validate() + { + _ = this.ClientEmail; + _ = this.PrivateKey; + _ = this.AuthProviderX509CertUrl; + _ = this.AuthUri; + _ = this.ClientID; + _ = this.ClientX509CertUrl; + _ = this.PrivateKeyID; + _ = this.ProjectID; + _ = this.TokenUri; + this.Type?.Validate(); + _ = this.UniverseDomain; + } + + public Credentials() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Credentials(Credentials credentials) + : base(credentials) { } +#pragma warning restore CS8618 + + public Credentials(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Credentials(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Credentials FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class CredentialsFromRaw : IFromRawJson +{ + /// + public Credentials FromRawUnchecked(IReadOnlyDictionary rawData) => + Credentials.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(TypeConverter))] +public enum Type +{ + ServiceAccount, +} + +sealed class TypeConverter : JsonConverter +{ + public override global::Stagehand.Models.Sessions.Type Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "service_account" => global::Stagehand.Models.Sessions.Type.ServiceAccount, + _ => (global::Stagehand.Models.Sessions.Type)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + global::Stagehand.Models.Sessions.Type value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + global::Stagehand.Models.Sessions.Type.ServiceAccount => "service_account", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Google auth scopes for the desired API request +/// +[JsonConverter(typeof(ScopesConverter))] +public record class Scopes : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public Scopes(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public Scopes(IReadOnlyList value, JsonElement? element = null) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public Scopes(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a string. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickStrings(out var value)) { + /// // `value` is of type `IReadOnlyList<string>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickStrings([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action> strings + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case IReadOnlyList value: + strings(value); + break; + default: + throw new StagehandInvalidDataException("Data did not match any variant of Scopes"); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func, T> strings + ) + { + return this.Value switch + { + string value => @string(value), + IReadOnlyList value => strings(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of Scopes" + ), + }; + } + + public static implicit operator Scopes(string value) => new(value); + + public static implicit operator Scopes(List value) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new StagehandInvalidDataException("Data did not match any variant of Scopes"); + } + } + + public virtual bool Equals(Scopes? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + IReadOnlyList _ => 1, + _ => -1, + }; + } +} + +sealed class ScopesConverter : JsonConverter +{ + public override Scopes? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write(Utf8JsonWriter writer, Scopes value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Vertex provider-specific model configuration +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class ProviderOptions : JsonModel +{ + /// + /// Vertex AI provider-specific settings + /// + public required Vertex Vertex + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("vertex"); + } + init { this._rawData.Set("vertex", value); } + } + + /// + public override void Validate() + { + this.Vertex.Validate(); + } + + public ProviderOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ProviderOptions(ProviderOptions providerOptions) + : base(providerOptions) { } +#pragma warning restore CS8618 + + public ProviderOptions(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ProviderOptions(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ProviderOptions FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ProviderOptions(Vertex vertex) + : this() + { + this.Vertex = vertex; + } +} + +class ProviderOptionsFromRaw : IFromRawJson +{ + /// + public ProviderOptions FromRawUnchecked(IReadOnlyDictionary rawData) => + ProviderOptions.FromRawUnchecked(rawData); +} + +/// +/// Vertex AI provider-specific settings +/// +[JsonConverter(typeof(JsonModelConverter))] +public sealed record class Vertex : JsonModel +{ + /// + /// Google Cloud location for Vertex AI models + /// + public required string Location + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("location"); + } + init { this._rawData.Set("location", value); } + } + + /// + /// Google Cloud project ID for Vertex AI models + /// + public required string Project + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("project"); + } + init { this._rawData.Set("project", value); } + } + + /// + /// Base URL for the Vertex AI provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the Vertex AI provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Location; + _ = this.Project; + _ = this.BaseUrl; + _ = this.Headers; + } + + public Vertex() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public Vertex(Vertex vertex) + : base(vertex) { } +#pragma warning restore CS8618 + + public Vertex(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + Vertex(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static Vertex FromRawUnchecked(IReadOnlyDictionary rawData) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class VertexFromRaw : IFromRawJson +{ + /// + public Vertex FromRawUnchecked(IReadOnlyDictionary rawData) => + Vertex.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter) +)] +public sealed record class GenericModelConfigObject : JsonModel +{ + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// AI provider for the model (or provide a baseURL endpoint instead) + /// + public ApiEnum? Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("provider"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("provider", value); + } + } + + /// + public override void Validate() + { + _ = this.ModelName; + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + this.Provider?.Validate(); + } + + public GenericModelConfigObject() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public GenericModelConfigObject(GenericModelConfigObject genericModelConfigObject) + : base(genericModelConfigObject) { } +#pragma warning restore CS8618 + + public GenericModelConfigObject(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + GenericModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static GenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public GenericModelConfigObject(string modelName) + : this() + { + this.ModelName = modelName; + } +} + +class GenericModelConfigObjectFromRaw : IFromRawJson +{ + /// + public GenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => GenericModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// AI provider for the model (or provide a baseURL endpoint instead) +/// +[JsonConverter(typeof(ProviderConverter))] +public enum Provider +{ + OpenAI, + Anthropic, + Google, + Microsoft, + Bedrock, +} + +sealed class ProviderConverter : JsonConverter +{ + public override Provider Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "openai" => Provider.OpenAI, + "anthropic" => Provider.Anthropic, + "google" => Provider.Google, + "microsoft" => Provider.Microsoft, + "bedrock" => Provider.Bedrock, + _ => (Provider)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Provider value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Provider.OpenAI => "openai", + Provider.Anthropic => "anthropic", + Provider.Google => "google", + Provider.Microsoft => "microsoft", + Provider.Bedrock => "bedrock", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + [JsonConverter(typeof(VariableConverter))] public record class Variable : ModelBase { diff --git a/src/Stagehand/Models/Sessions/SessionExecuteParams.cs b/src/Stagehand/Models/Sessions/SessionExecuteParams.cs index 7d2c67f..5128db2 100644 --- a/src/Stagehand/Models/Sessions/SessionExecuteParams.cs +++ b/src/Stagehand/Models/Sessions/SessionExecuteParams.cs @@ -1,5 +1,6 @@ using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Text; @@ -318,12 +319,12 @@ public AgentConfigModel? Model /// /// AI provider for the agent (legacy, use model: openai/gpt-5-nano instead) /// - public ApiEnum? Provider + public ApiEnum? Provider { get { this._rawData.Freeze(); - return this._rawData.GetNullableClass>("provider"); + return this._rawData.GetNullableClass>("provider"); } init { @@ -426,7 +427,49 @@ public JsonElement Json } } - public ExecutionModel(ModelConfig value, JsonElement? element = null) + public string? ModelName + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ModelName, + genericModelConfigObject: (x) => x.ModelName, + @string: (_) => null + ); + } + } + + public string? ApiKey + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ApiKey, + genericModelConfigObject: (x) => x.ApiKey, + @string: (_) => null + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + vertexModelConfigObject: (x) => x.BaseUrl, + genericModelConfigObject: (x) => x.BaseUrl, + @string: (_) => null + ); + } + } + + public ExecutionModel(ExecutionModelVertexModelConfigObject value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ExecutionModel(ExecutionModelGenericModelConfigObject value, JsonElement? element = null) { this.Value = value; this._element = element; @@ -445,22 +488,47 @@ public ExecutionModel(JsonElement element) /// /// Returns true and sets the out parameter if the instance was constructed with a variant of - /// type . + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVertexModelConfigObject(out var value)) { + /// // `value` is of type `ExecutionModelVertexModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVertexModelConfigObject( + [NotNullWhen(true)] out ExecutionModelVertexModelConfigObject? value + ) + { + value = this.Value as ExecutionModelVertexModelConfigObject; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . /// /// Consider using or if you need to handle every variant. /// /// /// - /// if (instance.TryPickModelConfig(out var value)) { - /// // `value` is of type `ModelConfig` + /// if (instance.TryPickGenericModelConfigObject(out var value)) { + /// // `value` is of type `ExecutionModelGenericModelConfigObject` /// Console.WriteLine(value); /// } /// /// /// - public bool TryPickModelConfig([NotNullWhen(true)] out ModelConfig? value) + public bool TryPickGenericModelConfigObject( + [NotNullWhen(true)] out ExecutionModelGenericModelConfigObject? value + ) { - value = this.Value as ModelConfig; + value = this.Value as ExecutionModelGenericModelConfigObject; return value != null; } @@ -499,18 +567,26 @@ public bool TryPickString([NotNullWhen(true)] out string? value) /// /// /// instance.Switch( - /// (ModelConfig value) => {...}, + /// (ExecutionModelVertexModelConfigObject value) => {...}, + /// (ExecutionModelGenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public void Switch(System::Action modelConfig, System::Action @string) + public void Switch( + System::Action vertexModelConfigObject, + System::Action genericModelConfigObject, + System::Action @string + ) { switch (this.Value) { - case ModelConfig value: - modelConfig(value); + case ExecutionModelVertexModelConfigObject value: + vertexModelConfigObject(value); + break; + case ExecutionModelGenericModelConfigObject value: + genericModelConfigObject(value); break; case string value: @string(value); @@ -537,17 +613,23 @@ public void Switch(System::Action modelConfig, System::Action /// /// var result = instance.Match( - /// (ModelConfig value) => {...}, + /// (ExecutionModelVertexModelConfigObject value) => {...}, + /// (ExecutionModelGenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public T Match(System::Func modelConfig, System::Func @string) + public T Match( + System::Func vertexModelConfigObject, + System::Func genericModelConfigObject, + System::Func @string + ) { return this.Value switch { - ModelConfig value => modelConfig(value), + ExecutionModelVertexModelConfigObject value => vertexModelConfigObject(value), + ExecutionModelGenericModelConfigObject value => genericModelConfigObject(value), string value => @string(value), _ => throw new StagehandInvalidDataException( "Data did not match any variant of ExecutionModel" @@ -555,7 +637,11 @@ public T Match(System::Func modelConfig, System::Func new(value); + public static implicit operator ExecutionModel(ExecutionModelVertexModelConfigObject value) => + new(value); + + public static implicit operator ExecutionModel(ExecutionModelGenericModelConfigObject value) => + new(value); public static implicit operator ExecutionModel(string value) => new(value); @@ -577,7 +663,11 @@ public override void Validate() "Data did not match any variant of ExecutionModel" ); } - this.Switch((modelConfig) => modelConfig.Validate(), (_) => { }); + this.Switch( + (vertexModelConfigObject) => vertexModelConfigObject.Validate(), + (genericModelConfigObject) => genericModelConfigObject.Validate(), + (_) => { } + ); } public virtual bool Equals(ExecutionModel? other) => @@ -600,8 +690,9 @@ int VariantIndex() { return this.Value switch { - ModelConfig _ => 0, - string _ => 1, + ExecutionModelVertexModelConfigObject _ => 0, + ExecutionModelGenericModelConfigObject _ => 1, + string _ => 2, _ => -1, }; } @@ -618,7 +709,27 @@ JsonSerializerOptions options var element = JsonSerializer.Deserialize(ref reader, options); try { - var deserialized = JsonSerializer.Deserialize(element, options); + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); if (deserialized != null) { deserialized.Validate(); @@ -656,166 +767,2594 @@ JsonSerializerOptions options } } -/// -/// Tool mode for the agent (dom, hybrid, cua). If set, overrides cua. -/// -[JsonConverter(typeof(ModeConverter))] -public enum Mode -{ - Dom, - Hybrid, - Cua, -} - -sealed class ModeConverter : JsonConverter +[JsonConverter( + typeof(JsonModelConverter< + ExecutionModelVertexModelConfigObject, + ExecutionModelVertexModelConfigObjectFromRaw + >) +)] +public sealed record class ExecutionModelVertexModelConfigObject : JsonModel { - public override Mode Read( - ref Utf8JsonReader reader, - System::Type typeToConvert, - JsonSerializerOptions options - ) + /// + /// Vertex provider authentication configuration + /// + public required ExecutionModelVertexModelConfigObjectAuth Auth { - return JsonSerializer.Deserialize(ref reader, options) switch + get { - "dom" => Mode.Dom, - "hybrid" => Mode.Hybrid, - "cua" => Mode.Cua, - _ => (Mode)(-1), - }; + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("auth"); + } + init { this._rawData.Set("auth", value); } } - public override void Write(Utf8JsonWriter writer, Mode value, JsonSerializerOptions options) + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName { - JsonSerializer.Serialize( - writer, - value switch - { - Mode.Dom => "dom", - Mode.Hybrid => "hybrid", - Mode.Cua => "cua", - _ => throw new StagehandInvalidDataException( - string.Format("Invalid value '{0}' in {1}", value, nameof(value)) - ), - }, - options - ); + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } } -} - -/// -/// Model configuration object or model name string (e.g., 'openai/gpt-5-nano') -/// -[JsonConverter(typeof(AgentConfigModelConverter))] -public record class AgentConfigModel : ModelBase -{ - public object? Value { get; } = null; - - JsonElement? _element = null; - public JsonElement Json + /// + /// Vertex AI model provider + /// + public JsonElement Provider { get { - return this._element ??= JsonSerializer.SerializeToElement( - this.Value, - ModelBase.SerializerOptions - ); + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("provider"); } + init { this._rawData.Set("provider", value); } } - public AgentConfigModel(ModelConfig value, JsonElement? element = null) + /// + /// Vertex provider-specific model configuration + /// + public required ExecutionModelVertexModelConfigObjectProviderOptions ProviderOptions { - this.Value = value; - this._element = element; + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "providerOptions" + ); + } + init { this._rawData.Set("providerOptions", value); } } - public AgentConfigModel(string value, JsonElement? element = null) + /// + /// API key for the model provider + /// + public string? ApiKey { - this.Value = value; - this._element = element; - } + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } - public AgentConfigModel(JsonElement element) - { - this._element = element; + this._rawData.Set("apiKey", value); + } } /// - /// Returns true and sets the out parameter if the instance was constructed with a variant of - /// type . - /// - /// Consider using or if you need to handle every variant. - /// - /// - /// - /// if (instance.TryPickConfig(out var value)) { - /// // `value` is of type `ModelConfig` - /// Console.WriteLine(value); - /// } - /// - /// + /// Base URL for the model provider /// - public bool TryPickConfig([NotNullWhen(true)] out ModelConfig? value) + public string? BaseUrl { - value = this.Value as ModelConfig; - return value != null; + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } } /// - /// Returns true and sets the out parameter if the instance was constructed with a variant of - /// type . - /// - /// Consider using or if you need to handle every variant. - /// - /// - /// - /// if (instance.TryPickString(out var value)) { - /// // `value` is of type `string` - /// Console.WriteLine(value); - /// } - /// - /// + /// Custom headers sent with every request to the model provider /// - public bool TryPickString([NotNullWhen(true)] out string? value) + public IReadOnlyDictionary? Headers { - value = this.Value as string; - return value != null; + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } } - /// - /// Calls the function parameter corresponding to the variant the instance was constructed with. - /// - /// Use the TryPick method(s) if you don't need to handle every variant, or - /// if you need your function parameters to return something. - /// - /// - /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data - /// that doesn't match any variant's expected shape). - /// - /// - /// - /// - /// instance.Switch( - /// (ModelConfig value) => {...}, - /// (string value) => {...} - /// ); - /// - /// - /// - public void Switch(System::Action config, System::Action @string) + /// + public override void Validate() { - switch (this.Value) + this.Auth.Validate(); + _ = this.ModelName; + if (!JsonElement.DeepEquals(this.Provider, JsonSerializer.SerializeToElement("vertex"))) { - case ModelConfig value: - config(value); - break; - case string value: - @string(value); - break; - default: - throw new StagehandInvalidDataException( - "Data did not match any variant of AgentConfigModel" - ); + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + this.ProviderOptions.Validate(); + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + } + + public ExecutionModelVertexModelConfigObject() + { + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExecutionModelVertexModelConfigObject( + ExecutionModelVertexModelConfigObject executionModelVertexModelConfigObject + ) + : base(executionModelVertexModelConfigObject) { } +#pragma warning restore CS8618 + + public ExecutionModelVertexModelConfigObject(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExecutionModelVertexModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExecutionModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExecutionModelVertexModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public ExecutionModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExecutionModelVertexModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// Vertex provider authentication configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + ExecutionModelVertexModelConfigObjectAuth, + ExecutionModelVertexModelConfigObjectAuthFromRaw + >) +)] +public sealed record class ExecutionModelVertexModelConfigObjectAuth : JsonModel +{ + /// + /// Google Cloud service account credentials + /// + public required ExecutionModelVertexModelConfigObjectAuthCredentials Credentials + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "credentials" + ); + } + init { this._rawData.Set("credentials", value); } + } + + /// + /// Use inline Google Cloud service account credentials for provider authentication + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Google Cloud project ID used by google-auth-library + /// + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("projectId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("projectId", value); + } + } + + /// + /// Google auth scopes for the desired API request + /// + public ExecutionModelVertexModelConfigObjectAuthScopes? Scopes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "scopes" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("scopes", value); + } + } + + /// + /// Google Cloud universe domain + /// + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universeDomain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universeDomain", value); + } + } + + /// + public override void Validate() + { + this.Credentials.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("googleServiceAccount") + ) + ) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + _ = this.ProjectID; + this.Scopes?.Validate(); + _ = this.UniverseDomain; + } + + public ExecutionModelVertexModelConfigObjectAuth() + { + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExecutionModelVertexModelConfigObjectAuth( + ExecutionModelVertexModelConfigObjectAuth executionModelVertexModelConfigObjectAuth + ) + : base(executionModelVertexModelConfigObjectAuth) { } +#pragma warning restore CS8618 + + public ExecutionModelVertexModelConfigObjectAuth( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExecutionModelVertexModelConfigObjectAuth(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExecutionModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExecutionModelVertexModelConfigObjectAuth( + ExecutionModelVertexModelConfigObjectAuthCredentials credentials + ) + : this() + { + this.Credentials = credentials; + } +} + +class ExecutionModelVertexModelConfigObjectAuthFromRaw + : IFromRawJson +{ + /// + public ExecutionModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExecutionModelVertexModelConfigObjectAuth.FromRawUnchecked(rawData); +} + +/// +/// Google Cloud service account credentials +/// +[JsonConverter( + typeof(JsonModelConverter< + ExecutionModelVertexModelConfigObjectAuthCredentials, + ExecutionModelVertexModelConfigObjectAuthCredentialsFromRaw + >) +)] +public sealed record class ExecutionModelVertexModelConfigObjectAuthCredentials : JsonModel +{ + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("client_email"); + } + init { this._rawData.Set("client_email", value); } + } + + public required string PrivateKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("private_key"); + } + init { this._rawData.Set("private_key", value); } + } + + public string? AuthProviderX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_provider_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_provider_x509_cert_url", value); + } + } + + public string? AuthUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_uri", value); + } + } + + public string? ClientID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_id", value); + } + } + + public string? ClientX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_x509_cert_url", value); + } + } + + public string? PrivateKeyID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("private_key_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("private_key_id", value); + } + } + + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("project_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("project_id", value); + } + } + + public string? TokenUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("token_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("token_uri", value); + } + } + + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universe_domain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universe_domain", value); + } + } + + /// + public override void Validate() + { + _ = this.ClientEmail; + _ = this.PrivateKey; + _ = this.AuthProviderX509CertUrl; + _ = this.AuthUri; + _ = this.ClientID; + _ = this.ClientX509CertUrl; + _ = this.PrivateKeyID; + _ = this.ProjectID; + _ = this.TokenUri; + this.Type?.Validate(); + _ = this.UniverseDomain; + } + + public ExecutionModelVertexModelConfigObjectAuthCredentials() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExecutionModelVertexModelConfigObjectAuthCredentials( + ExecutionModelVertexModelConfigObjectAuthCredentials executionModelVertexModelConfigObjectAuthCredentials + ) + : base(executionModelVertexModelConfigObjectAuthCredentials) { } +#pragma warning restore CS8618 + + public ExecutionModelVertexModelConfigObjectAuthCredentials( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExecutionModelVertexModelConfigObjectAuthCredentials( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExecutionModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExecutionModelVertexModelConfigObjectAuthCredentialsFromRaw + : IFromRawJson +{ + /// + public ExecutionModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExecutionModelVertexModelConfigObjectAuthCredentials.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(ExecutionModelVertexModelConfigObjectAuthCredentialsTypeConverter))] +public enum ExecutionModelVertexModelConfigObjectAuthCredentialsType +{ + ServiceAccount, +} + +sealed class ExecutionModelVertexModelConfigObjectAuthCredentialsTypeConverter + : JsonConverter +{ + public override ExecutionModelVertexModelConfigObjectAuthCredentialsType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "service_account" => + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + _ => (ExecutionModelVertexModelConfigObjectAuthCredentialsType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + ExecutionModelVertexModelConfigObjectAuthCredentialsType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + ExecutionModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount => + "service_account", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Google auth scopes for the desired API request +/// +[JsonConverter(typeof(ExecutionModelVertexModelConfigObjectAuthScopesConverter))] +public record class ExecutionModelVertexModelConfigObjectAuthScopes : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ExecutionModelVertexModelConfigObjectAuthScopes( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public ExecutionModelVertexModelConfigObjectAuthScopes( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public ExecutionModelVertexModelConfigObjectAuthScopes(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a string. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickStrings(out var value)) { + /// // `value` is of type `IReadOnlyList<string>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickStrings([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action> strings + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case IReadOnlyList value: + strings(value); + break; + default: + throw new StagehandInvalidDataException( + "Data did not match any variant of ExecutionModelVertexModelConfigObjectAuthScopes" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func, T> strings + ) + { + return this.Value switch + { + string value => @string(value), + IReadOnlyList value => strings(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of ExecutionModelVertexModelConfigObjectAuthScopes" + ), + }; + } + + public static implicit operator ExecutionModelVertexModelConfigObjectAuthScopes(string value) => + new(value); + + public static implicit operator ExecutionModelVertexModelConfigObjectAuthScopes( + List value + ) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new StagehandInvalidDataException( + "Data did not match any variant of ExecutionModelVertexModelConfigObjectAuthScopes" + ); + } + } + + public virtual bool Equals(ExecutionModelVertexModelConfigObjectAuthScopes? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + IReadOnlyList _ => 1, + _ => -1, + }; + } +} + +sealed class ExecutionModelVertexModelConfigObjectAuthScopesConverter + : JsonConverter +{ + public override ExecutionModelVertexModelConfigObjectAuthScopes? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + ExecutionModelVertexModelConfigObjectAuthScopes value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Vertex provider-specific model configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + ExecutionModelVertexModelConfigObjectProviderOptions, + ExecutionModelVertexModelConfigObjectProviderOptionsFromRaw + >) +)] +public sealed record class ExecutionModelVertexModelConfigObjectProviderOptions : JsonModel +{ + /// + /// Vertex AI provider-specific settings + /// + public required ExecutionModelVertexModelConfigObjectProviderOptionsVertex Vertex + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "vertex" + ); + } + init { this._rawData.Set("vertex", value); } + } + + /// + public override void Validate() + { + this.Vertex.Validate(); + } + + public ExecutionModelVertexModelConfigObjectProviderOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExecutionModelVertexModelConfigObjectProviderOptions( + ExecutionModelVertexModelConfigObjectProviderOptions executionModelVertexModelConfigObjectProviderOptions + ) + : base(executionModelVertexModelConfigObjectProviderOptions) { } +#pragma warning restore CS8618 + + public ExecutionModelVertexModelConfigObjectProviderOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExecutionModelVertexModelConfigObjectProviderOptions( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExecutionModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExecutionModelVertexModelConfigObjectProviderOptions( + ExecutionModelVertexModelConfigObjectProviderOptionsVertex vertex + ) + : this() + { + this.Vertex = vertex; + } +} + +class ExecutionModelVertexModelConfigObjectProviderOptionsFromRaw + : IFromRawJson +{ + /// + public ExecutionModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExecutionModelVertexModelConfigObjectProviderOptions.FromRawUnchecked(rawData); +} + +/// +/// Vertex AI provider-specific settings +/// +[JsonConverter( + typeof(JsonModelConverter< + ExecutionModelVertexModelConfigObjectProviderOptionsVertex, + ExecutionModelVertexModelConfigObjectProviderOptionsVertexFromRaw + >) +)] +public sealed record class ExecutionModelVertexModelConfigObjectProviderOptionsVertex : JsonModel +{ + /// + /// Google Cloud location for Vertex AI models + /// + public required string Location + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("location"); + } + init { this._rawData.Set("location", value); } + } + + /// + /// Google Cloud project ID for Vertex AI models + /// + public required string Project + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("project"); + } + init { this._rawData.Set("project", value); } + } + + /// + /// Base URL for the Vertex AI provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the Vertex AI provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Location; + _ = this.Project; + _ = this.BaseUrl; + _ = this.Headers; + } + + public ExecutionModelVertexModelConfigObjectProviderOptionsVertex() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExecutionModelVertexModelConfigObjectProviderOptionsVertex( + ExecutionModelVertexModelConfigObjectProviderOptionsVertex executionModelVertexModelConfigObjectProviderOptionsVertex + ) + : base(executionModelVertexModelConfigObjectProviderOptionsVertex) { } +#pragma warning restore CS8618 + + public ExecutionModelVertexModelConfigObjectProviderOptionsVertex( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExecutionModelVertexModelConfigObjectProviderOptionsVertex( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExecutionModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ExecutionModelVertexModelConfigObjectProviderOptionsVertexFromRaw + : IFromRawJson +{ + /// + public ExecutionModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExecutionModelVertexModelConfigObjectProviderOptionsVertex.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + ExecutionModelGenericModelConfigObject, + ExecutionModelGenericModelConfigObjectFromRaw + >) +)] +public sealed record class ExecutionModelGenericModelConfigObject : JsonModel +{ + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// AI provider for the model (or provide a baseURL endpoint instead) + /// + public ApiEnum? Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("provider"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("provider", value); + } + } + + /// + public override void Validate() + { + _ = this.ModelName; + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + this.Provider?.Validate(); + } + + public ExecutionModelGenericModelConfigObject() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ExecutionModelGenericModelConfigObject( + ExecutionModelGenericModelConfigObject executionModelGenericModelConfigObject + ) + : base(executionModelGenericModelConfigObject) { } +#pragma warning restore CS8618 + + public ExecutionModelGenericModelConfigObject(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ExecutionModelGenericModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ExecutionModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ExecutionModelGenericModelConfigObject(string modelName) + : this() + { + this.ModelName = modelName; + } +} + +class ExecutionModelGenericModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public ExecutionModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ExecutionModelGenericModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// AI provider for the model (or provide a baseURL endpoint instead) +/// +[JsonConverter(typeof(ExecutionModelGenericModelConfigObjectProviderConverter))] +public enum ExecutionModelGenericModelConfigObjectProvider +{ + OpenAI, + Anthropic, + Google, + Microsoft, + Bedrock, +} + +sealed class ExecutionModelGenericModelConfigObjectProviderConverter + : JsonConverter +{ + public override ExecutionModelGenericModelConfigObjectProvider Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "openai" => ExecutionModelGenericModelConfigObjectProvider.OpenAI, + "anthropic" => ExecutionModelGenericModelConfigObjectProvider.Anthropic, + "google" => ExecutionModelGenericModelConfigObjectProvider.Google, + "microsoft" => ExecutionModelGenericModelConfigObjectProvider.Microsoft, + "bedrock" => ExecutionModelGenericModelConfigObjectProvider.Bedrock, + _ => (ExecutionModelGenericModelConfigObjectProvider)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + ExecutionModelGenericModelConfigObjectProvider value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + ExecutionModelGenericModelConfigObjectProvider.OpenAI => "openai", + ExecutionModelGenericModelConfigObjectProvider.Anthropic => "anthropic", + ExecutionModelGenericModelConfigObjectProvider.Google => "google", + ExecutionModelGenericModelConfigObjectProvider.Microsoft => "microsoft", + ExecutionModelGenericModelConfigObjectProvider.Bedrock => "bedrock", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Tool mode for the agent (dom, hybrid, cua). If set, overrides cua. +/// +[JsonConverter(typeof(ModeConverter))] +public enum Mode +{ + Dom, + Hybrid, + Cua, +} + +sealed class ModeConverter : JsonConverter +{ + public override Mode Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "dom" => Mode.Dom, + "hybrid" => Mode.Hybrid, + "cua" => Mode.Cua, + _ => (Mode)(-1), + }; + } + + public override void Write(Utf8JsonWriter writer, Mode value, JsonSerializerOptions options) + { + JsonSerializer.Serialize( + writer, + value switch + { + Mode.Dom => "dom", + Mode.Hybrid => "hybrid", + Mode.Cua => "cua", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Model configuration object or model name string (e.g., 'openai/gpt-5-nano') +/// +[JsonConverter(typeof(AgentConfigModelConverter))] +public record class AgentConfigModel : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public string? ModelName + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ModelName, + genericModelConfigObject: (x) => x.ModelName, + @string: (_) => null + ); + } + } + + public string? ApiKey + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ApiKey, + genericModelConfigObject: (x) => x.ApiKey, + @string: (_) => null + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + vertexModelConfigObject: (x) => x.BaseUrl, + genericModelConfigObject: (x) => x.BaseUrl, + @string: (_) => null + ); + } + } + + public AgentConfigModel( + AgentConfigModelVertexModelConfigObject value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public AgentConfigModel( + AgentConfigModelGenericModelConfigObject value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public AgentConfigModel(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public AgentConfigModel(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVertexModelConfigObject(out var value)) { + /// // `value` is of type `AgentConfigModelVertexModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVertexModelConfigObject( + [NotNullWhen(true)] out AgentConfigModelVertexModelConfigObject? value + ) + { + value = this.Value as AgentConfigModelVertexModelConfigObject; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickGenericModelConfigObject(out var value)) { + /// // `value` is of type `AgentConfigModelGenericModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickGenericModelConfigObject( + [NotNullWhen(true)] out AgentConfigModelGenericModelConfigObject? value + ) + { + value = this.Value as AgentConfigModelGenericModelConfigObject; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (AgentConfigModelVertexModelConfigObject value) => {...}, + /// (AgentConfigModelGenericModelConfigObject value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action vertexModelConfigObject, + System::Action genericModelConfigObject, + System::Action @string + ) + { + switch (this.Value) + { + case AgentConfigModelVertexModelConfigObject value: + vertexModelConfigObject(value); + break; + case AgentConfigModelGenericModelConfigObject value: + genericModelConfigObject(value); + break; + case string value: + @string(value); + break; + default: + throw new StagehandInvalidDataException( + "Data did not match any variant of AgentConfigModel" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (AgentConfigModelVertexModelConfigObject value) => {...}, + /// (AgentConfigModelGenericModelConfigObject value) => {...}, + /// (string value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func vertexModelConfigObject, + System::Func genericModelConfigObject, + System::Func @string + ) + { + return this.Value switch + { + AgentConfigModelVertexModelConfigObject value => vertexModelConfigObject(value), + AgentConfigModelGenericModelConfigObject value => genericModelConfigObject(value), + string value => @string(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of AgentConfigModel" + ), + }; + } + + public static implicit operator AgentConfigModel( + AgentConfigModelVertexModelConfigObject value + ) => new(value); + + public static implicit operator AgentConfigModel( + AgentConfigModelGenericModelConfigObject value + ) => new(value); + + public static implicit operator AgentConfigModel(string value) => new(value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new StagehandInvalidDataException( + "Data did not match any variant of AgentConfigModel" + ); + } + this.Switch( + (vertexModelConfigObject) => vertexModelConfigObject.Validate(), + (genericModelConfigObject) => genericModelConfigObject.Validate(), + (_) => { } + ); + } + + public virtual bool Equals(AgentConfigModel? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + AgentConfigModelVertexModelConfigObject _ => 0, + AgentConfigModelGenericModelConfigObject _ => 1, + string _ => 2, + _ => -1, + }; + } +} + +sealed class AgentConfigModelConverter : JsonConverter +{ + public override AgentConfigModel? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + AgentConfigModel value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + AgentConfigModelVertexModelConfigObject, + AgentConfigModelVertexModelConfigObjectFromRaw + >) +)] +public sealed record class AgentConfigModelVertexModelConfigObject : JsonModel +{ + /// + /// Vertex provider authentication configuration + /// + public required AgentConfigModelVertexModelConfigObjectAuth Auth + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "auth" + ); + } + init { this._rawData.Set("auth", value); } + } + + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// Vertex AI model provider + /// + public JsonElement Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("provider"); + } + init { this._rawData.Set("provider", value); } + } + + /// + /// Vertex provider-specific model configuration + /// + public required AgentConfigModelVertexModelConfigObjectProviderOptions ProviderOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "providerOptions" + ); + } + init { this._rawData.Set("providerOptions", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + this.Auth.Validate(); + _ = this.ModelName; + if (!JsonElement.DeepEquals(this.Provider, JsonSerializer.SerializeToElement("vertex"))) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + this.ProviderOptions.Validate(); + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + } + + public AgentConfigModelVertexModelConfigObject() + { + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AgentConfigModelVertexModelConfigObject( + AgentConfigModelVertexModelConfigObject agentConfigModelVertexModelConfigObject + ) + : base(agentConfigModelVertexModelConfigObject) { } +#pragma warning restore CS8618 + + public AgentConfigModelVertexModelConfigObject(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AgentConfigModelVertexModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AgentConfigModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AgentConfigModelVertexModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public AgentConfigModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => AgentConfigModelVertexModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// Vertex provider authentication configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + AgentConfigModelVertexModelConfigObjectAuth, + AgentConfigModelVertexModelConfigObjectAuthFromRaw + >) +)] +public sealed record class AgentConfigModelVertexModelConfigObjectAuth : JsonModel +{ + /// + /// Google Cloud service account credentials + /// + public required AgentConfigModelVertexModelConfigObjectAuthCredentials Credentials + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "credentials" + ); + } + init { this._rawData.Set("credentials", value); } + } + + /// + /// Use inline Google Cloud service account credentials for provider authentication + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Google Cloud project ID used by google-auth-library + /// + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("projectId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("projectId", value); + } + } + + /// + /// Google auth scopes for the desired API request + /// + public AgentConfigModelVertexModelConfigObjectAuthScopes? Scopes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "scopes" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("scopes", value); + } + } + + /// + /// Google Cloud universe domain + /// + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universeDomain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universeDomain", value); + } + } + + /// + public override void Validate() + { + this.Credentials.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("googleServiceAccount") + ) + ) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + _ = this.ProjectID; + this.Scopes?.Validate(); + _ = this.UniverseDomain; + } + + public AgentConfigModelVertexModelConfigObjectAuth() + { + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AgentConfigModelVertexModelConfigObjectAuth( + AgentConfigModelVertexModelConfigObjectAuth agentConfigModelVertexModelConfigObjectAuth + ) + : base(agentConfigModelVertexModelConfigObjectAuth) { } +#pragma warning restore CS8618 + + public AgentConfigModelVertexModelConfigObjectAuth( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AgentConfigModelVertexModelConfigObjectAuth(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AgentConfigModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public AgentConfigModelVertexModelConfigObjectAuth( + AgentConfigModelVertexModelConfigObjectAuthCredentials credentials + ) + : this() + { + this.Credentials = credentials; + } +} + +class AgentConfigModelVertexModelConfigObjectAuthFromRaw + : IFromRawJson +{ + /// + public AgentConfigModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) => AgentConfigModelVertexModelConfigObjectAuth.FromRawUnchecked(rawData); +} + +/// +/// Google Cloud service account credentials +/// +[JsonConverter( + typeof(JsonModelConverter< + AgentConfigModelVertexModelConfigObjectAuthCredentials, + AgentConfigModelVertexModelConfigObjectAuthCredentialsFromRaw + >) +)] +public sealed record class AgentConfigModelVertexModelConfigObjectAuthCredentials : JsonModel +{ + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("client_email"); + } + init { this._rawData.Set("client_email", value); } + } + + public required string PrivateKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("private_key"); + } + init { this._rawData.Set("private_key", value); } + } + + public string? AuthProviderX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_provider_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_provider_x509_cert_url", value); + } + } + + public string? AuthUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_uri", value); + } + } + + public string? ClientID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_id", value); + } + } + + public string? ClientX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_x509_cert_url", value); + } + } + + public string? PrivateKeyID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("private_key_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("private_key_id", value); + } + } + + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("project_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("project_id", value); + } + } + + public string? TokenUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("token_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("token_uri", value); + } + } + + public ApiEnum? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universe_domain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universe_domain", value); + } + } + + /// + public override void Validate() + { + _ = this.ClientEmail; + _ = this.PrivateKey; + _ = this.AuthProviderX509CertUrl; + _ = this.AuthUri; + _ = this.ClientID; + _ = this.ClientX509CertUrl; + _ = this.PrivateKeyID; + _ = this.ProjectID; + _ = this.TokenUri; + this.Type?.Validate(); + _ = this.UniverseDomain; + } + + public AgentConfigModelVertexModelConfigObjectAuthCredentials() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AgentConfigModelVertexModelConfigObjectAuthCredentials( + AgentConfigModelVertexModelConfigObjectAuthCredentials agentConfigModelVertexModelConfigObjectAuthCredentials + ) + : base(agentConfigModelVertexModelConfigObjectAuthCredentials) { } +#pragma warning restore CS8618 + + public AgentConfigModelVertexModelConfigObjectAuthCredentials( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AgentConfigModelVertexModelConfigObjectAuthCredentials( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AgentConfigModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class AgentConfigModelVertexModelConfigObjectAuthCredentialsFromRaw + : IFromRawJson +{ + /// + public AgentConfigModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) => AgentConfigModelVertexModelConfigObjectAuthCredentials.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(AgentConfigModelVertexModelConfigObjectAuthCredentialsTypeConverter))] +public enum AgentConfigModelVertexModelConfigObjectAuthCredentialsType +{ + ServiceAccount, +} + +sealed class AgentConfigModelVertexModelConfigObjectAuthCredentialsTypeConverter + : JsonConverter +{ + public override AgentConfigModelVertexModelConfigObjectAuthCredentialsType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "service_account" => + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + _ => (AgentConfigModelVertexModelConfigObjectAuthCredentialsType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + AgentConfigModelVertexModelConfigObjectAuthCredentialsType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + AgentConfigModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount => + "service_account", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Google auth scopes for the desired API request +/// +[JsonConverter(typeof(AgentConfigModelVertexModelConfigObjectAuthScopesConverter))] +public record class AgentConfigModelVertexModelConfigObjectAuthScopes : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public AgentConfigModelVertexModelConfigObjectAuthScopes( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public AgentConfigModelVertexModelConfigObjectAuthScopes( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public AgentConfigModelVertexModelConfigObjectAuthScopes(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a string. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickStrings(out var value)) { + /// // `value` is of type `IReadOnlyList<string>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickStrings([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action> strings + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case IReadOnlyList value: + strings(value); + break; + default: + throw new StagehandInvalidDataException( + "Data did not match any variant of AgentConfigModelVertexModelConfigObjectAuthScopes" + ); } } @@ -834,130 +3373,585 @@ public void Switch(System::Action config, System::Action @s /// /// /// var result = instance.Match( - /// (ModelConfig value) => {...}, - /// (string value) => {...} + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} /// ); /// /// /// - public T Match(System::Func config, System::Func @string) + public T Match( + System::Func @string, + System::Func, T> strings + ) + { + return this.Value switch + { + string value => @string(value), + IReadOnlyList value => strings(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of AgentConfigModelVertexModelConfigObjectAuthScopes" + ), + }; + } + + public static implicit operator AgentConfigModelVertexModelConfigObjectAuthScopes( + string value + ) => new(value); + + public static implicit operator AgentConfigModelVertexModelConfigObjectAuthScopes( + List value + ) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new StagehandInvalidDataException( + "Data did not match any variant of AgentConfigModelVertexModelConfigObjectAuthScopes" + ); + } + } + + public virtual bool Equals(AgentConfigModelVertexModelConfigObjectAuthScopes? other) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() { return this.Value switch { - ModelConfig value => config(value), - string value => @string(value), - _ => throw new StagehandInvalidDataException( - "Data did not match any variant of AgentConfigModel" - ), - }; + string _ => 0, + IReadOnlyList _ => 1, + _ => -1, + }; + } +} + +sealed class AgentConfigModelVertexModelConfigObjectAuthScopesConverter + : JsonConverter +{ + public override AgentConfigModelVertexModelConfigObjectAuthScopes? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + AgentConfigModelVertexModelConfigObjectAuthScopes value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Vertex provider-specific model configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + AgentConfigModelVertexModelConfigObjectProviderOptions, + AgentConfigModelVertexModelConfigObjectProviderOptionsFromRaw + >) +)] +public sealed record class AgentConfigModelVertexModelConfigObjectProviderOptions : JsonModel +{ + /// + /// Vertex AI provider-specific settings + /// + public required AgentConfigModelVertexModelConfigObjectProviderOptionsVertex Vertex + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "vertex" + ); + } + init { this._rawData.Set("vertex", value); } + } + + /// + public override void Validate() + { + this.Vertex.Validate(); + } + + public AgentConfigModelVertexModelConfigObjectProviderOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AgentConfigModelVertexModelConfigObjectProviderOptions( + AgentConfigModelVertexModelConfigObjectProviderOptions agentConfigModelVertexModelConfigObjectProviderOptions + ) + : base(agentConfigModelVertexModelConfigObjectProviderOptions) { } +#pragma warning restore CS8618 + + public AgentConfigModelVertexModelConfigObjectProviderOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AgentConfigModelVertexModelConfigObjectProviderOptions( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AgentConfigModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public AgentConfigModelVertexModelConfigObjectProviderOptions( + AgentConfigModelVertexModelConfigObjectProviderOptionsVertex vertex + ) + : this() + { + this.Vertex = vertex; + } +} + +class AgentConfigModelVertexModelConfigObjectProviderOptionsFromRaw + : IFromRawJson +{ + /// + public AgentConfigModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => AgentConfigModelVertexModelConfigObjectProviderOptions.FromRawUnchecked(rawData); +} + +/// +/// Vertex AI provider-specific settings +/// +[JsonConverter( + typeof(JsonModelConverter< + AgentConfigModelVertexModelConfigObjectProviderOptionsVertex, + AgentConfigModelVertexModelConfigObjectProviderOptionsVertexFromRaw + >) +)] +public sealed record class AgentConfigModelVertexModelConfigObjectProviderOptionsVertex : JsonModel +{ + /// + /// Google Cloud location for Vertex AI models + /// + public required string Location + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("location"); + } + init { this._rawData.Set("location", value); } + } + + /// + /// Google Cloud project ID for Vertex AI models + /// + public required string Project + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("project"); + } + init { this._rawData.Set("project", value); } } - public static implicit operator AgentConfigModel(ModelConfig value) => new(value); + /// + /// Base URL for the Vertex AI provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } - public static implicit operator AgentConfigModel(string value) => new(value); + this._rawData.Set("baseURL", value); + } + } /// - /// Validates that the instance was constructed with a known variant and that this variant is valid - /// (based on its own Validate method). - /// - /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). - /// - /// - /// Thrown when the instance does not pass validation. - /// + /// Custom headers sent with every request to the Vertex AI provider /// - public override void Validate() + public IReadOnlyDictionary? Headers { - if (this.Value == null) + get { - throw new StagehandInvalidDataException( - "Data did not match any variant of AgentConfigModel" + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) ); } - this.Switch((config) => config.Validate(), (_) => { }); } - public virtual bool Equals(AgentConfigModel? other) => - other != null - && this.VariantIndex() == other.VariantIndex() - && JsonElement.DeepEquals(this.Json, other.Json); + /// + public override void Validate() + { + _ = this.Location; + _ = this.Project; + _ = this.BaseUrl; + _ = this.Headers; + } - public override int GetHashCode() + public AgentConfigModelVertexModelConfigObjectProviderOptionsVertex() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AgentConfigModelVertexModelConfigObjectProviderOptionsVertex( + AgentConfigModelVertexModelConfigObjectProviderOptionsVertex agentConfigModelVertexModelConfigObjectProviderOptionsVertex + ) + : base(agentConfigModelVertexModelConfigObjectProviderOptionsVertex) { } +#pragma warning restore CS8618 + + public AgentConfigModelVertexModelConfigObjectProviderOptionsVertex( + IReadOnlyDictionary rawData + ) { - return 0; + this._rawData = new(rawData); } - public override string ToString() => - JsonSerializer.Serialize( - FriendlyJsonPrinter.PrintValue(this.Json), - ModelBase.ToStringSerializerOptions - ); +#pragma warning disable CS8618 + [SetsRequiredMembers] + AgentConfigModelVertexModelConfigObjectProviderOptionsVertex( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 - int VariantIndex() + /// + public static AgentConfigModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) { - return this.Value switch - { - ModelConfig _ => 0, - string _ => 1, - _ => -1, - }; + return new(FrozenDictionary.ToFrozenDictionary(rawData)); } } -sealed class AgentConfigModelConverter : JsonConverter +class AgentConfigModelVertexModelConfigObjectProviderOptionsVertexFromRaw + : IFromRawJson { - public override AgentConfigModel? Read( - ref Utf8JsonReader reader, - System::Type typeToConvert, - JsonSerializerOptions options - ) + /// + public AgentConfigModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) => AgentConfigModelVertexModelConfigObjectProviderOptionsVertex.FromRawUnchecked(rawData); +} + +[JsonConverter( + typeof(JsonModelConverter< + AgentConfigModelGenericModelConfigObject, + AgentConfigModelGenericModelConfigObjectFromRaw + >) +)] +public sealed record class AgentConfigModelGenericModelConfigObject : JsonModel +{ + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName { - var element = JsonSerializer.Deserialize(ref reader, options); - try + get { - var deserialized = JsonSerializer.Deserialize(element, options); - if (deserialized != null) + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) { - deserialized.Validate(); - return new(deserialized, element); + return; } + + this._rawData.Set("apiKey", value); } - catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get { - // ignore + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); } + } - try + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get { - var deserialized = JsonSerializer.Deserialize(element, options); - if (deserialized != null) + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) { - return new(deserialized, element); + return; } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); } - catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + } + + /// + /// AI provider for the model (or provide a baseURL endpoint instead) + /// + public ApiEnum? Provider + { + get { - // ignore + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("provider"); } + init + { + if (value == null) + { + return; + } - return new(element); + this._rawData.Set("provider", value); + } + } + + /// + public override void Validate() + { + _ = this.ModelName; + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + this.Provider?.Validate(); + } + + public AgentConfigModelGenericModelConfigObject() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public AgentConfigModelGenericModelConfigObject( + AgentConfigModelGenericModelConfigObject agentConfigModelGenericModelConfigObject + ) + : base(agentConfigModelGenericModelConfigObject) { } +#pragma warning restore CS8618 + + public AgentConfigModelGenericModelConfigObject( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + AgentConfigModelGenericModelConfigObject(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static AgentConfigModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public AgentConfigModelGenericModelConfigObject(string modelName) + : this() + { + this.ModelName = modelName; + } +} + +class AgentConfigModelGenericModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public AgentConfigModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => AgentConfigModelGenericModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// AI provider for the model (or provide a baseURL endpoint instead) +/// +[JsonConverter(typeof(AgentConfigModelGenericModelConfigObjectProviderConverter))] +public enum AgentConfigModelGenericModelConfigObjectProvider +{ + OpenAI, + Anthropic, + Google, + Microsoft, + Bedrock, +} + +sealed class AgentConfigModelGenericModelConfigObjectProviderConverter + : JsonConverter +{ + public override AgentConfigModelGenericModelConfigObjectProvider Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "openai" => AgentConfigModelGenericModelConfigObjectProvider.OpenAI, + "anthropic" => AgentConfigModelGenericModelConfigObjectProvider.Anthropic, + "google" => AgentConfigModelGenericModelConfigObjectProvider.Google, + "microsoft" => AgentConfigModelGenericModelConfigObjectProvider.Microsoft, + "bedrock" => AgentConfigModelGenericModelConfigObjectProvider.Bedrock, + _ => (AgentConfigModelGenericModelConfigObjectProvider)(-1), + }; } public override void Write( Utf8JsonWriter writer, - AgentConfigModel value, + AgentConfigModelGenericModelConfigObjectProvider value, JsonSerializerOptions options ) { - JsonSerializer.Serialize(writer, value.Json, options); + JsonSerializer.Serialize( + writer, + value switch + { + AgentConfigModelGenericModelConfigObjectProvider.OpenAI => "openai", + AgentConfigModelGenericModelConfigObjectProvider.Anthropic => "anthropic", + AgentConfigModelGenericModelConfigObjectProvider.Google => "google", + AgentConfigModelGenericModelConfigObjectProvider.Microsoft => "microsoft", + AgentConfigModelGenericModelConfigObjectProvider.Bedrock => "bedrock", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); } } /// /// AI provider for the agent (legacy, use model: openai/gpt-5-nano instead) /// -[JsonConverter(typeof(ProviderConverter))] -public enum Provider +[JsonConverter(typeof(AgentConfigProviderConverter))] +public enum AgentConfigProvider { OpenAI, Anthropic, @@ -966,9 +3960,9 @@ public enum Provider Bedrock, } -sealed class ProviderConverter : JsonConverter +sealed class AgentConfigProviderConverter : JsonConverter { - public override Provider Read( + public override AgentConfigProvider Read( ref Utf8JsonReader reader, System::Type typeToConvert, JsonSerializerOptions options @@ -976,26 +3970,30 @@ JsonSerializerOptions options { return JsonSerializer.Deserialize(ref reader, options) switch { - "openai" => Provider.OpenAI, - "anthropic" => Provider.Anthropic, - "google" => Provider.Google, - "microsoft" => Provider.Microsoft, - "bedrock" => Provider.Bedrock, - _ => (Provider)(-1), + "openai" => AgentConfigProvider.OpenAI, + "anthropic" => AgentConfigProvider.Anthropic, + "google" => AgentConfigProvider.Google, + "microsoft" => AgentConfigProvider.Microsoft, + "bedrock" => AgentConfigProvider.Bedrock, + _ => (AgentConfigProvider)(-1), }; } - public override void Write(Utf8JsonWriter writer, Provider value, JsonSerializerOptions options) + public override void Write( + Utf8JsonWriter writer, + AgentConfigProvider value, + JsonSerializerOptions options + ) { JsonSerializer.Serialize( writer, value switch { - Provider.OpenAI => "openai", - Provider.Anthropic => "anthropic", - Provider.Google => "google", - Provider.Microsoft => "microsoft", - Provider.Bedrock => "bedrock", + AgentConfigProvider.OpenAI => "openai", + AgentConfigProvider.Anthropic => "anthropic", + AgentConfigProvider.Google => "google", + AgentConfigProvider.Microsoft => "microsoft", + AgentConfigProvider.Bedrock => "bedrock", _ => throw new StagehandInvalidDataException( string.Format("Invalid value '{0}' in {1}", value, nameof(value)) ), diff --git a/src/Stagehand/Models/Sessions/SessionExtractParams.cs b/src/Stagehand/Models/Sessions/SessionExtractParams.cs index 953fcd0..8f06d83 100644 --- a/src/Stagehand/Models/Sessions/SessionExtractParams.cs +++ b/src/Stagehand/Models/Sessions/SessionExtractParams.cs @@ -434,7 +434,55 @@ public JsonElement Json } } - public SessionExtractParamsOptionsModel(ModelConfig value, JsonElement? element = null) + public string? ModelName + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ModelName, + genericModelConfigObject: (x) => x.ModelName, + @string: (_) => null + ); + } + } + + public string? ApiKey + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ApiKey, + genericModelConfigObject: (x) => x.ApiKey, + @string: (_) => null + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + vertexModelConfigObject: (x) => x.BaseUrl, + genericModelConfigObject: (x) => x.BaseUrl, + @string: (_) => null + ); + } + } + + public SessionExtractParamsOptionsModel( + SessionExtractParamsOptionsModelVertexModelConfigObject value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public SessionExtractParamsOptionsModel( + SessionExtractParamsOptionsModelGenericModelConfigObject value, + JsonElement? element = null + ) { this.Value = value; this._element = element; @@ -453,22 +501,47 @@ public SessionExtractParamsOptionsModel(JsonElement element) /// /// Returns true and sets the out parameter if the instance was constructed with a variant of - /// type . + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVertexModelConfigObject(out var value)) { + /// // `value` is of type `SessionExtractParamsOptionsModelVertexModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVertexModelConfigObject( + [NotNullWhen(true)] out SessionExtractParamsOptionsModelVertexModelConfigObject? value + ) + { + value = this.Value as SessionExtractParamsOptionsModelVertexModelConfigObject; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . /// /// Consider using or if you need to handle every variant. /// /// /// - /// if (instance.TryPickConfig(out var value)) { - /// // `value` is of type `ModelConfig` + /// if (instance.TryPickGenericModelConfigObject(out var value)) { + /// // `value` is of type `SessionExtractParamsOptionsModelGenericModelConfigObject` /// Console.WriteLine(value); /// } /// /// /// - public bool TryPickConfig([NotNullWhen(true)] out ModelConfig? value) + public bool TryPickGenericModelConfigObject( + [NotNullWhen(true)] out SessionExtractParamsOptionsModelGenericModelConfigObject? value + ) { - value = this.Value as ModelConfig; + value = this.Value as SessionExtractParamsOptionsModelGenericModelConfigObject; return value != null; } @@ -507,18 +580,26 @@ public bool TryPickString([NotNullWhen(true)] out string? value) /// /// /// instance.Switch( - /// (ModelConfig value) => {...}, + /// (SessionExtractParamsOptionsModelVertexModelConfigObject value) => {...}, + /// (SessionExtractParamsOptionsModelGenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public void Switch(System::Action config, System::Action @string) + public void Switch( + System::Action vertexModelConfigObject, + System::Action genericModelConfigObject, + System::Action @string + ) { switch (this.Value) { - case ModelConfig value: - config(value); + case SessionExtractParamsOptionsModelVertexModelConfigObject value: + vertexModelConfigObject(value); + break; + case SessionExtractParamsOptionsModelGenericModelConfigObject value: + genericModelConfigObject(value); break; case string value: @string(value); @@ -545,17 +626,31 @@ public void Switch(System::Action config, System::Action @s /// /// /// var result = instance.Match( - /// (ModelConfig value) => {...}, + /// (SessionExtractParamsOptionsModelVertexModelConfigObject value) => {...}, + /// (SessionExtractParamsOptionsModelGenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public T Match(System::Func config, System::Func @string) + public T Match( + System::Func< + SessionExtractParamsOptionsModelVertexModelConfigObject, + T + > vertexModelConfigObject, + System::Func< + SessionExtractParamsOptionsModelGenericModelConfigObject, + T + > genericModelConfigObject, + System::Func @string + ) { return this.Value switch { - ModelConfig value => config(value), + SessionExtractParamsOptionsModelVertexModelConfigObject value => + vertexModelConfigObject(value), + SessionExtractParamsOptionsModelGenericModelConfigObject value => + genericModelConfigObject(value), string value => @string(value), _ => throw new StagehandInvalidDataException( "Data did not match any variant of SessionExtractParamsOptionsModel" @@ -563,8 +658,13 @@ public T Match(System::Func config, System::Func @ }; } - public static implicit operator SessionExtractParamsOptionsModel(ModelConfig value) => - new(value); + public static implicit operator SessionExtractParamsOptionsModel( + SessionExtractParamsOptionsModelVertexModelConfigObject value + ) => new(value); + + public static implicit operator SessionExtractParamsOptionsModel( + SessionExtractParamsOptionsModelGenericModelConfigObject value + ) => new(value); public static implicit operator SessionExtractParamsOptionsModel(string value) => new(value); @@ -586,7 +686,11 @@ public override void Validate() "Data did not match any variant of SessionExtractParamsOptionsModel" ); } - this.Switch((config) => config.Validate(), (_) => { }); + this.Switch( + (vertexModelConfigObject) => vertexModelConfigObject.Validate(), + (genericModelConfigObject) => genericModelConfigObject.Validate(), + (_) => { } + ); } public virtual bool Equals(SessionExtractParamsOptionsModel? other) => @@ -609,8 +713,9 @@ int VariantIndex() { return this.Value switch { - ModelConfig _ => 0, - string _ => 1, + SessionExtractParamsOptionsModelVertexModelConfigObject _ => 0, + SessionExtractParamsOptionsModelGenericModelConfigObject _ => 1, + string _ => 2, _ => -1, }; } @@ -628,7 +733,29 @@ JsonSerializerOptions options var element = JsonSerializer.Deserialize(ref reader, options); try { - var deserialized = JsonSerializer.Deserialize(element, options); + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); if (deserialized != null) { deserialized.Validate(); @@ -666,6 +793,1429 @@ JsonSerializerOptions options } } +[JsonConverter( + typeof(JsonModelConverter< + SessionExtractParamsOptionsModelVertexModelConfigObject, + SessionExtractParamsOptionsModelVertexModelConfigObjectFromRaw + >) +)] +public sealed record class SessionExtractParamsOptionsModelVertexModelConfigObject : JsonModel +{ + /// + /// Vertex provider authentication configuration + /// + public required SessionExtractParamsOptionsModelVertexModelConfigObjectAuth Auth + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "auth" + ); + } + init { this._rawData.Set("auth", value); } + } + + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// Vertex AI model provider + /// + public JsonElement Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("provider"); + } + init { this._rawData.Set("provider", value); } + } + + /// + /// Vertex provider-specific model configuration + /// + public required SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions ProviderOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "providerOptions" + ); + } + init { this._rawData.Set("providerOptions", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + this.Auth.Validate(); + _ = this.ModelName; + if (!JsonElement.DeepEquals(this.Provider, JsonSerializer.SerializeToElement("vertex"))) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + this.ProviderOptions.Validate(); + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + } + + public SessionExtractParamsOptionsModelVertexModelConfigObject() + { + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelVertexModelConfigObject( + SessionExtractParamsOptionsModelVertexModelConfigObject sessionExtractParamsOptionsModelVertexModelConfigObject + ) + : base(sessionExtractParamsOptionsModelVertexModelConfigObject) { } +#pragma warning restore CS8618 + + public SessionExtractParamsOptionsModelVertexModelConfigObject( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionExtractParamsOptionsModelVertexModelConfigObject( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionExtractParamsOptionsModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SessionExtractParamsOptionsModelVertexModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public SessionExtractParamsOptionsModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SessionExtractParamsOptionsModelVertexModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// Vertex provider authentication configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionExtractParamsOptionsModelVertexModelConfigObjectAuth, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthFromRaw + >) +)] +public sealed record class SessionExtractParamsOptionsModelVertexModelConfigObjectAuth : JsonModel +{ + /// + /// Google Cloud service account credentials + /// + public required SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials Credentials + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "credentials" + ); + } + init { this._rawData.Set("credentials", value); } + } + + /// + /// Use inline Google Cloud service account credentials for provider authentication + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Google Cloud project ID used by google-auth-library + /// + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("projectId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("projectId", value); + } + } + + /// + /// Google auth scopes for the desired API request + /// + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes? Scopes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "scopes" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("scopes", value); + } + } + + /// + /// Google Cloud universe domain + /// + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universeDomain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universeDomain", value); + } + } + + /// + public override void Validate() + { + this.Credentials.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("googleServiceAccount") + ) + ) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + _ = this.ProjectID; + this.Scopes?.Validate(); + _ = this.UniverseDomain; + } + + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuth() + { + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuth( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuth sessionExtractParamsOptionsModelVertexModelConfigObjectAuth + ) + : base(sessionExtractParamsOptionsModelVertexModelConfigObjectAuth) { } +#pragma warning restore CS8618 + + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuth( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionExtractParamsOptionsModelVertexModelConfigObjectAuth( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionExtractParamsOptionsModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuth( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials credentials + ) + : this() + { + this.Credentials = credentials; + } +} + +class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthFromRaw + : IFromRawJson +{ + /// + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SessionExtractParamsOptionsModelVertexModelConfigObjectAuth.FromRawUnchecked(rawData); +} + +/// +/// Google Cloud service account credentials +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsFromRaw + >) +)] +public sealed record class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + : JsonModel +{ + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("client_email"); + } + init { this._rawData.Set("client_email", value); } + } + + public required string PrivateKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("private_key"); + } + init { this._rawData.Set("private_key", value); } + } + + public string? AuthProviderX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_provider_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_provider_x509_cert_url", value); + } + } + + public string? AuthUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_uri", value); + } + } + + public string? ClientID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_id", value); + } + } + + public string? ClientX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_x509_cert_url", value); + } + } + + public string? PrivateKeyID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("private_key_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("private_key_id", value); + } + } + + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("project_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("project_id", value); + } + } + + public string? TokenUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("token_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("token_uri", value); + } + } + + public ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + >? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universe_domain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universe_domain", value); + } + } + + /// + public override void Validate() + { + _ = this.ClientEmail; + _ = this.PrivateKey; + _ = this.AuthProviderX509CertUrl; + _ = this.AuthUri; + _ = this.ClientID; + _ = this.ClientX509CertUrl; + _ = this.PrivateKeyID; + _ = this.ProjectID; + _ = this.TokenUri; + this.Type?.Validate(); + _ = this.UniverseDomain; + } + + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials sessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials + ) + : base(sessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials) { } +#pragma warning restore CS8618 + + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsFromRaw + : IFromRawJson +{ + /// + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentials.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsTypeConverter) +)] +public enum SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType +{ + ServiceAccount, +} + +sealed class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsTypeConverter + : JsonConverter +{ + public override SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "service_account" => + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + _ => (SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount => + "service_account", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Google auth scopes for the desired API request +/// +[JsonConverter(typeof(SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopesConverter))] +public record class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a string. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickStrings(out var value)) { + /// // `value` is of type `IReadOnlyList<string>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickStrings([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action> strings + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case IReadOnlyList value: + strings(value); + break; + default: + throw new StagehandInvalidDataException( + "Data did not match any variant of SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func, T> strings + ) + { + return this.Value switch + { + string value => @string(value), + IReadOnlyList value => strings(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes" + ), + }; + } + + public static implicit operator SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes( + string value + ) => new(value); + + public static implicit operator SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes( + List value + ) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new StagehandInvalidDataException( + "Data did not match any variant of SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes" + ); + } + } + + public virtual bool Equals( + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes? other + ) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + IReadOnlyList _ => 1, + _ => -1, + }; + } +} + +sealed class SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopesConverter + : JsonConverter +{ + public override SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + SessionExtractParamsOptionsModelVertexModelConfigObjectAuthScopes value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Vertex provider-specific model configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions, + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsFromRaw + >) +)] +public sealed record class SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions + : JsonModel +{ + /// + /// Vertex AI provider-specific settings + /// + public required SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex Vertex + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "vertex" + ); + } + init { this._rawData.Set("vertex", value); } + } + + /// + public override void Validate() + { + this.Vertex.Validate(); + } + + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions( + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions sessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions + ) + : base(sessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions) { } +#pragma warning restore CS8618 + + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions( + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex vertex + ) + : this() + { + this.Vertex = vertex; + } +} + +class SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsFromRaw + : IFromRawJson +{ + /// + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptions.FromRawUnchecked( + rawData + ); +} + +/// +/// Vertex AI provider-specific settings +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex, + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertexFromRaw + >) +)] +public sealed record class SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + : JsonModel +{ + /// + /// Google Cloud location for Vertex AI models + /// + public required string Location + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("location"); + } + init { this._rawData.Set("location", value); } + } + + /// + /// Google Cloud project ID for Vertex AI models + /// + public required string Project + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("project"); + } + init { this._rawData.Set("project", value); } + } + + /// + /// Base URL for the Vertex AI provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the Vertex AI provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Location; + _ = this.Project; + _ = this.BaseUrl; + _ = this.Headers; + } + + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex( + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex sessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + ) + : base(sessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex) { } +#pragma warning restore CS8618 + + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertexFromRaw + : IFromRawJson +{ + /// + public SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + SessionExtractParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + SessionExtractParamsOptionsModelGenericModelConfigObject, + SessionExtractParamsOptionsModelGenericModelConfigObjectFromRaw + >) +)] +public sealed record class SessionExtractParamsOptionsModelGenericModelConfigObject : JsonModel +{ + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// AI provider for the model (or provide a baseURL endpoint instead) + /// + public ApiEnum< + string, + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider + >? Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("provider"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("provider", value); + } + } + + /// + public override void Validate() + { + _ = this.ModelName; + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + this.Provider?.Validate(); + } + + public SessionExtractParamsOptionsModelGenericModelConfigObject() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelGenericModelConfigObject( + SessionExtractParamsOptionsModelGenericModelConfigObject sessionExtractParamsOptionsModelGenericModelConfigObject + ) + : base(sessionExtractParamsOptionsModelGenericModelConfigObject) { } +#pragma warning restore CS8618 + + public SessionExtractParamsOptionsModelGenericModelConfigObject( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionExtractParamsOptionsModelGenericModelConfigObject( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionExtractParamsOptionsModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SessionExtractParamsOptionsModelGenericModelConfigObject(string modelName) + : this() + { + this.ModelName = modelName; + } +} + +class SessionExtractParamsOptionsModelGenericModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public SessionExtractParamsOptionsModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SessionExtractParamsOptionsModelGenericModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// AI provider for the model (or provide a baseURL endpoint instead) +/// +[JsonConverter(typeof(SessionExtractParamsOptionsModelGenericModelConfigObjectProviderConverter))] +public enum SessionExtractParamsOptionsModelGenericModelConfigObjectProvider +{ + OpenAI, + Anthropic, + Google, + Microsoft, + Bedrock, +} + +sealed class SessionExtractParamsOptionsModelGenericModelConfigObjectProviderConverter + : JsonConverter +{ + public override SessionExtractParamsOptionsModelGenericModelConfigObjectProvider Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "openai" => SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + "anthropic" => + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Anthropic, + "google" => SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Google, + "microsoft" => + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Microsoft, + "bedrock" => SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Bedrock, + _ => (SessionExtractParamsOptionsModelGenericModelConfigObjectProvider)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.OpenAI => "openai", + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Anthropic => + "anthropic", + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Google => "google", + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Microsoft => + "microsoft", + SessionExtractParamsOptionsModelGenericModelConfigObjectProvider.Bedrock => + "bedrock", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + /// /// Whether to stream the response via SSE /// diff --git a/src/Stagehand/Models/Sessions/SessionObserveParams.cs b/src/Stagehand/Models/Sessions/SessionObserveParams.cs index fcc16f9..d71c07a 100644 --- a/src/Stagehand/Models/Sessions/SessionObserveParams.cs +++ b/src/Stagehand/Models/Sessions/SessionObserveParams.cs @@ -420,7 +420,55 @@ public JsonElement Json } } - public SessionObserveParamsOptionsModel(ModelConfig value, JsonElement? element = null) + public string? ModelName + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ModelName, + genericModelConfigObject: (x) => x.ModelName, + @string: (_) => null + ); + } + } + + public string? ApiKey + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ApiKey, + genericModelConfigObject: (x) => x.ApiKey, + @string: (_) => null + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + vertexModelConfigObject: (x) => x.BaseUrl, + genericModelConfigObject: (x) => x.BaseUrl, + @string: (_) => null + ); + } + } + + public SessionObserveParamsOptionsModel( + SessionObserveParamsOptionsModelVertexModelConfigObject value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public SessionObserveParamsOptionsModel( + SessionObserveParamsOptionsModelGenericModelConfigObject value, + JsonElement? element = null + ) { this.Value = value; this._element = element; @@ -439,22 +487,47 @@ public SessionObserveParamsOptionsModel(JsonElement element) /// /// Returns true and sets the out parameter if the instance was constructed with a variant of - /// type . + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickVertexModelConfigObject(out var value)) { + /// // `value` is of type `SessionObserveParamsOptionsModelVertexModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVertexModelConfigObject( + [NotNullWhen(true)] out SessionObserveParamsOptionsModelVertexModelConfigObject? value + ) + { + value = this.Value as SessionObserveParamsOptionsModelVertexModelConfigObject; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . /// /// Consider using or if you need to handle every variant. /// /// /// - /// if (instance.TryPickConfig(out var value)) { - /// // `value` is of type `ModelConfig` + /// if (instance.TryPickGenericModelConfigObject(out var value)) { + /// // `value` is of type `SessionObserveParamsOptionsModelGenericModelConfigObject` /// Console.WriteLine(value); /// } /// /// /// - public bool TryPickConfig([NotNullWhen(true)] out ModelConfig? value) + public bool TryPickGenericModelConfigObject( + [NotNullWhen(true)] out SessionObserveParamsOptionsModelGenericModelConfigObject? value + ) { - value = this.Value as ModelConfig; + value = this.Value as SessionObserveParamsOptionsModelGenericModelConfigObject; return value != null; } @@ -493,18 +566,26 @@ public bool TryPickString([NotNullWhen(true)] out string? value) /// /// /// instance.Switch( - /// (ModelConfig value) => {...}, + /// (SessionObserveParamsOptionsModelVertexModelConfigObject value) => {...}, + /// (SessionObserveParamsOptionsModelGenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public void Switch(System::Action config, System::Action @string) + public void Switch( + System::Action vertexModelConfigObject, + System::Action genericModelConfigObject, + System::Action @string + ) { switch (this.Value) { - case ModelConfig value: - config(value); + case SessionObserveParamsOptionsModelVertexModelConfigObject value: + vertexModelConfigObject(value); + break; + case SessionObserveParamsOptionsModelGenericModelConfigObject value: + genericModelConfigObject(value); break; case string value: @string(value); @@ -531,17 +612,31 @@ public void Switch(System::Action config, System::Action @s /// /// /// var result = instance.Match( - /// (ModelConfig value) => {...}, + /// (SessionObserveParamsOptionsModelVertexModelConfigObject value) => {...}, + /// (SessionObserveParamsOptionsModelGenericModelConfigObject value) => {...}, /// (string value) => {...} /// ); /// /// /// - public T Match(System::Func config, System::Func @string) + public T Match( + System::Func< + SessionObserveParamsOptionsModelVertexModelConfigObject, + T + > vertexModelConfigObject, + System::Func< + SessionObserveParamsOptionsModelGenericModelConfigObject, + T + > genericModelConfigObject, + System::Func @string + ) { return this.Value switch { - ModelConfig value => config(value), + SessionObserveParamsOptionsModelVertexModelConfigObject value => + vertexModelConfigObject(value), + SessionObserveParamsOptionsModelGenericModelConfigObject value => + genericModelConfigObject(value), string value => @string(value), _ => throw new StagehandInvalidDataException( "Data did not match any variant of SessionObserveParamsOptionsModel" @@ -549,8 +644,13 @@ public T Match(System::Func config, System::Func @ }; } - public static implicit operator SessionObserveParamsOptionsModel(ModelConfig value) => - new(value); + public static implicit operator SessionObserveParamsOptionsModel( + SessionObserveParamsOptionsModelVertexModelConfigObject value + ) => new(value); + + public static implicit operator SessionObserveParamsOptionsModel( + SessionObserveParamsOptionsModelGenericModelConfigObject value + ) => new(value); public static implicit operator SessionObserveParamsOptionsModel(string value) => new(value); @@ -572,7 +672,11 @@ public override void Validate() "Data did not match any variant of SessionObserveParamsOptionsModel" ); } - this.Switch((config) => config.Validate(), (_) => { }); + this.Switch( + (vertexModelConfigObject) => vertexModelConfigObject.Validate(), + (genericModelConfigObject) => genericModelConfigObject.Validate(), + (_) => { } + ); } public virtual bool Equals(SessionObserveParamsOptionsModel? other) => @@ -595,8 +699,9 @@ int VariantIndex() { return this.Value switch { - ModelConfig _ => 0, - string _ => 1, + SessionObserveParamsOptionsModelVertexModelConfigObject _ => 0, + SessionObserveParamsOptionsModelGenericModelConfigObject _ => 1, + string _ => 2, _ => -1, }; } @@ -614,7 +719,29 @@ JsonSerializerOptions options var element = JsonSerializer.Deserialize(ref reader, options); try { - var deserialized = JsonSerializer.Deserialize(element, options); + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); + if (deserialized != null) + { + deserialized.Validate(); + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = + JsonSerializer.Deserialize( + element, + options + ); if (deserialized != null) { deserialized.Validate(); @@ -652,6 +779,1429 @@ JsonSerializerOptions options } } +[JsonConverter( + typeof(JsonModelConverter< + SessionObserveParamsOptionsModelVertexModelConfigObject, + SessionObserveParamsOptionsModelVertexModelConfigObjectFromRaw + >) +)] +public sealed record class SessionObserveParamsOptionsModelVertexModelConfigObject : JsonModel +{ + /// + /// Vertex provider authentication configuration + /// + public required SessionObserveParamsOptionsModelVertexModelConfigObjectAuth Auth + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "auth" + ); + } + init { this._rawData.Set("auth", value); } + } + + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// Vertex AI model provider + /// + public JsonElement Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("provider"); + } + init { this._rawData.Set("provider", value); } + } + + /// + /// Vertex provider-specific model configuration + /// + public required SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions ProviderOptions + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "providerOptions" + ); + } + init { this._rawData.Set("providerOptions", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + this.Auth.Validate(); + _ = this.ModelName; + if (!JsonElement.DeepEquals(this.Provider, JsonSerializer.SerializeToElement("vertex"))) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + this.ProviderOptions.Validate(); + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + } + + public SessionObserveParamsOptionsModelVertexModelConfigObject() + { + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelVertexModelConfigObject( + SessionObserveParamsOptionsModelVertexModelConfigObject sessionObserveParamsOptionsModelVertexModelConfigObject + ) + : base(sessionObserveParamsOptionsModelVertexModelConfigObject) { } +#pragma warning restore CS8618 + + public SessionObserveParamsOptionsModelVertexModelConfigObject( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Provider = JsonSerializer.SerializeToElement("vertex"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionObserveParamsOptionsModelVertexModelConfigObject( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionObserveParamsOptionsModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SessionObserveParamsOptionsModelVertexModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public SessionObserveParamsOptionsModelVertexModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SessionObserveParamsOptionsModelVertexModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// Vertex provider authentication configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionObserveParamsOptionsModelVertexModelConfigObjectAuth, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthFromRaw + >) +)] +public sealed record class SessionObserveParamsOptionsModelVertexModelConfigObjectAuth : JsonModel +{ + /// + /// Google Cloud service account credentials + /// + public required SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials Credentials + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "credentials" + ); + } + init { this._rawData.Set("credentials", value); } + } + + /// + /// Use inline Google Cloud service account credentials for provider authentication + /// + public JsonElement Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullStruct("type"); + } + init { this._rawData.Set("type", value); } + } + + /// + /// Google Cloud project ID used by google-auth-library + /// + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("projectId"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("projectId", value); + } + } + + /// + /// Google auth scopes for the desired API request + /// + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes? Scopes + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass( + "scopes" + ); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("scopes", value); + } + } + + /// + /// Google Cloud universe domain + /// + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universeDomain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universeDomain", value); + } + } + + /// + public override void Validate() + { + this.Credentials.Validate(); + if ( + !JsonElement.DeepEquals( + this.Type, + JsonSerializer.SerializeToElement("googleServiceAccount") + ) + ) + { + throw new StagehandInvalidDataException("Invalid value given for constant"); + } + _ = this.ProjectID; + this.Scopes?.Validate(); + _ = this.UniverseDomain; + } + + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuth() + { + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuth( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuth sessionObserveParamsOptionsModelVertexModelConfigObjectAuth + ) + : base(sessionObserveParamsOptionsModelVertexModelConfigObjectAuth) { } +#pragma warning restore CS8618 + + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuth( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionObserveParamsOptionsModelVertexModelConfigObjectAuth( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionObserveParamsOptionsModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuth( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials credentials + ) + : this() + { + this.Credentials = credentials; + } +} + +class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthFromRaw + : IFromRawJson +{ + /// + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SessionObserveParamsOptionsModelVertexModelConfigObjectAuth.FromRawUnchecked(rawData); +} + +/// +/// Google Cloud service account credentials +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsFromRaw + >) +)] +public sealed record class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + : JsonModel +{ + public required string ClientEmail + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("client_email"); + } + init { this._rawData.Set("client_email", value); } + } + + public required string PrivateKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("private_key"); + } + init { this._rawData.Set("private_key", value); } + } + + public string? AuthProviderX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_provider_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_provider_x509_cert_url", value); + } + } + + public string? AuthUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("auth_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("auth_uri", value); + } + } + + public string? ClientID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_id", value); + } + } + + public string? ClientX509CertUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("client_x509_cert_url"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("client_x509_cert_url", value); + } + } + + public string? PrivateKeyID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("private_key_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("private_key_id", value); + } + } + + public string? ProjectID + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("project_id"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("project_id", value); + } + } + + public string? TokenUri + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("token_uri"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("token_uri", value); + } + } + + public ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + >? Type + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum< + string, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType + > + >("type"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("type", value); + } + } + + public string? UniverseDomain + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("universe_domain"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("universe_domain", value); + } + } + + /// + public override void Validate() + { + _ = this.ClientEmail; + _ = this.PrivateKey; + _ = this.AuthProviderX509CertUrl; + _ = this.AuthUri; + _ = this.ClientID; + _ = this.ClientX509CertUrl; + _ = this.PrivateKeyID; + _ = this.ProjectID; + _ = this.TokenUri; + this.Type?.Validate(); + _ = this.UniverseDomain; + } + + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials sessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials + ) + : base(sessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials) { } +#pragma warning restore CS8618 + + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsFromRaw + : IFromRawJson +{ + /// + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentials.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsTypeConverter) +)] +public enum SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType +{ + ServiceAccount, +} + +sealed class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsTypeConverter + : JsonConverter +{ + public override SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "service_account" => + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + _ => (SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthCredentialsType.ServiceAccount => + "service_account", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + +/// +/// Google auth scopes for the desired API request +/// +[JsonConverter(typeof(SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopesConverter))] +public record class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes( + string value, + JsonElement? element = null + ) + { + this.Value = value; + this._element = element; + } + + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes(JsonElement element) + { + this._element = element; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type . + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickString(out var value)) { + /// // `value` is of type `string` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickString([NotNullWhen(true)] out string? value) + { + value = this.Value as string; + return value != null; + } + + /// + /// Returns true and sets the out parameter if the instance was constructed with a variant of + /// type where T is a string. + /// + /// Consider using or if you need to handle every variant. + /// + /// + /// + /// if (instance.TryPickStrings(out var value)) { + /// // `value` is of type `IReadOnlyList<string>` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickStrings([NotNullWhen(true)] out IReadOnlyList? value) + { + value = this.Value as IReadOnlyList; + return value != null; + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you need your function parameters to return something. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// instance.Switch( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action @string, + System::Action> strings + ) + { + switch (this.Value) + { + case string value: + @string(value); + break; + case IReadOnlyList value: + strings(value); + break; + default: + throw new StagehandInvalidDataException( + "Data did not match any variant of SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes" + ); + } + } + + /// + /// Calls the function parameter corresponding to the variant the instance was constructed with and + /// returns its result. + /// + /// Use the TryPick method(s) if you don't need to handle every variant, or + /// if you don't need your function parameters to return a value. + /// + /// + /// Thrown when the instance was constructed with an unknown variant (e.g. deserialized from raw data + /// that doesn't match any variant's expected shape). + /// + /// + /// + /// + /// var result = instance.Match( + /// (string value) => {...}, + /// (IReadOnlyList<string> value) => {...} + /// ); + /// + /// + /// + public T Match( + System::Func @string, + System::Func, T> strings + ) + { + return this.Value switch + { + string value => @string(value), + IReadOnlyList value => strings(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes" + ), + }; + } + + public static implicit operator SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes( + string value + ) => new(value); + + public static implicit operator SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes( + List value + ) => new((IReadOnlyList)value); + + /// + /// Validates that the instance was constructed with a known variant and that this variant is valid + /// (based on its own Validate method). + /// + /// This is useful for instances constructed from raw JSON data (e.g. deserialized from an API response). + /// + /// + /// Thrown when the instance does not pass validation. + /// + /// + public override void Validate() + { + if (this.Value == null) + { + throw new StagehandInvalidDataException( + "Data did not match any variant of SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes" + ); + } + } + + public virtual bool Equals( + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes? other + ) => + other != null + && this.VariantIndex() == other.VariantIndex() + && JsonElement.DeepEquals(this.Json, other.Json); + + public override int GetHashCode() + { + return 0; + } + + public override string ToString() => + JsonSerializer.Serialize( + FriendlyJsonPrinter.PrintValue(this.Json), + ModelBase.ToStringSerializerOptions + ); + + int VariantIndex() + { + return this.Value switch + { + string _ => 0, + IReadOnlyList _ => 1, + _ => -1, + }; + } +} + +sealed class SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopesConverter + : JsonConverter +{ + public override SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes? Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + var element = JsonSerializer.Deserialize(ref reader, options); + try + { + var deserialized = JsonSerializer.Deserialize(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + try + { + var deserialized = JsonSerializer.Deserialize>(element, options); + if (deserialized != null) + { + return new(deserialized, element); + } + } + catch (System::Exception e) when (e is JsonException || e is StagehandInvalidDataException) + { + // ignore + } + + return new(element); + } + + public override void Write( + Utf8JsonWriter writer, + SessionObserveParamsOptionsModelVertexModelConfigObjectAuthScopes value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize(writer, value.Json, options); + } +} + +/// +/// Vertex provider-specific model configuration +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions, + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsFromRaw + >) +)] +public sealed record class SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions + : JsonModel +{ + /// + /// Vertex AI provider-specific settings + /// + public required SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex Vertex + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass( + "vertex" + ); + } + init { this._rawData.Set("vertex", value); } + } + + /// + public override void Validate() + { + this.Vertex.Validate(); + } + + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions( + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions sessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions + ) + : base(sessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions) { } +#pragma warning restore CS8618 + + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions( + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex vertex + ) + : this() + { + this.Vertex = vertex; + } +} + +class SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsFromRaw + : IFromRawJson +{ + /// + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptions.FromRawUnchecked( + rawData + ); +} + +/// +/// Vertex AI provider-specific settings +/// +[JsonConverter( + typeof(JsonModelConverter< + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex, + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertexFromRaw + >) +)] +public sealed record class SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + : JsonModel +{ + /// + /// Google Cloud location for Vertex AI models + /// + public required string Location + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("location"); + } + init { this._rawData.Set("location", value); } + } + + /// + /// Google Cloud project ID for Vertex AI models + /// + public required string Project + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("project"); + } + init { this._rawData.Set("project", value); } + } + + /// + /// Base URL for the Vertex AI provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the Vertex AI provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + public override void Validate() + { + _ = this.Location; + _ = this.Project; + _ = this.BaseUrl; + _ = this.Headers; + } + + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex( + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex sessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex + ) + : base(sessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex) { } +#pragma warning restore CS8618 + + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertexFromRaw + : IFromRawJson +{ + /// + public SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex FromRawUnchecked( + IReadOnlyDictionary rawData + ) => + SessionObserveParamsOptionsModelVertexModelConfigObjectProviderOptionsVertex.FromRawUnchecked( + rawData + ); +} + +[JsonConverter( + typeof(JsonModelConverter< + SessionObserveParamsOptionsModelGenericModelConfigObject, + SessionObserveParamsOptionsModelGenericModelConfigObjectFromRaw + >) +)] +public sealed record class SessionObserveParamsOptionsModelGenericModelConfigObject : JsonModel +{ + /// + /// Model name string with provider prefix (e.g., 'openai/gpt-5-nano') + /// + public required string ModelName + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNotNullClass("modelName"); + } + init { this._rawData.Set("modelName", value); } + } + + /// + /// API key for the model provider + /// + public string? ApiKey + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("apiKey"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("apiKey", value); + } + } + + /// + /// Base URL for the model provider + /// + public string? BaseUrl + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass("baseURL"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("baseURL", value); + } + } + + /// + /// Custom headers sent with every request to the model provider + /// + public IReadOnlyDictionary? Headers + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass>("headers"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set?>( + "headers", + value == null ? null : FrozenDictionary.ToFrozenDictionary(value) + ); + } + } + + /// + /// AI provider for the model (or provide a baseURL endpoint instead) + /// + public ApiEnum< + string, + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider + >? Provider + { + get + { + this._rawData.Freeze(); + return this._rawData.GetNullableClass< + ApiEnum + >("provider"); + } + init + { + if (value == null) + { + return; + } + + this._rawData.Set("provider", value); + } + } + + /// + public override void Validate() + { + _ = this.ModelName; + _ = this.ApiKey; + _ = this.BaseUrl; + _ = this.Headers; + this.Provider?.Validate(); + } + + public SessionObserveParamsOptionsModelGenericModelConfigObject() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelGenericModelConfigObject( + SessionObserveParamsOptionsModelGenericModelConfigObject sessionObserveParamsOptionsModelGenericModelConfigObject + ) + : base(sessionObserveParamsOptionsModelGenericModelConfigObject) { } +#pragma warning restore CS8618 + + public SessionObserveParamsOptionsModelGenericModelConfigObject( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + SessionObserveParamsOptionsModelGenericModelConfigObject( + FrozenDictionary rawData + ) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static SessionObserveParamsOptionsModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public SessionObserveParamsOptionsModelGenericModelConfigObject(string modelName) + : this() + { + this.ModelName = modelName; + } +} + +class SessionObserveParamsOptionsModelGenericModelConfigObjectFromRaw + : IFromRawJson +{ + /// + public SessionObserveParamsOptionsModelGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) => SessionObserveParamsOptionsModelGenericModelConfigObject.FromRawUnchecked(rawData); +} + +/// +/// AI provider for the model (or provide a baseURL endpoint instead) +/// +[JsonConverter(typeof(SessionObserveParamsOptionsModelGenericModelConfigObjectProviderConverter))] +public enum SessionObserveParamsOptionsModelGenericModelConfigObjectProvider +{ + OpenAI, + Anthropic, + Google, + Microsoft, + Bedrock, +} + +sealed class SessionObserveParamsOptionsModelGenericModelConfigObjectProviderConverter + : JsonConverter +{ + public override SessionObserveParamsOptionsModelGenericModelConfigObjectProvider Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "openai" => SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI, + "anthropic" => + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Anthropic, + "google" => SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Google, + "microsoft" => + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Microsoft, + "bedrock" => SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Bedrock, + _ => (SessionObserveParamsOptionsModelGenericModelConfigObjectProvider)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.OpenAI => "openai", + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Anthropic => + "anthropic", + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Google => "google", + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Microsoft => + "microsoft", + SessionObserveParamsOptionsModelGenericModelConfigObjectProvider.Bedrock => + "bedrock", + _ => throw new StagehandInvalidDataException( + string.Format("Invalid value '{0}' in {1}", value, nameof(value)) + ), + }, + options + ); + } +} + [JsonConverter(typeof(SessionObserveParamsOptionsVariableConverter))] public record class SessionObserveParamsOptionsVariable : ModelBase { diff --git a/src/Stagehand/Models/Sessions/SessionStartParams.cs b/src/Stagehand/Models/Sessions/SessionStartParams.cs index f1381e3..c5ed2a1 100644 --- a/src/Stagehand/Models/Sessions/SessionStartParams.cs +++ b/src/Stagehand/Models/Sessions/SessionStartParams.cs @@ -424,14 +424,12 @@ public LaunchOptions? LaunchOptions /// /// Browser type to use /// - public ApiEnum? Type + public ApiEnum? Type { get { this._rawData.Freeze(); - return this._rawData.GetNullableClass< - ApiEnum - >("type"); + return this._rawData.GetNullableClass>("type"); } init { @@ -1345,16 +1343,16 @@ public Viewport FromRawUnchecked(IReadOnlyDictionary rawDat /// /// Browser type to use /// -[JsonConverter(typeof(TypeConverter))] -public enum Type +[JsonConverter(typeof(BrowserTypeConverter))] +public enum BrowserType { Local, Browserbase, } -sealed class TypeConverter : JsonConverter +sealed class BrowserTypeConverter : JsonConverter { - public override global::Stagehand.Models.Sessions.Type Read( + public override BrowserType Read( ref Utf8JsonReader reader, System::Type typeToConvert, JsonSerializerOptions options @@ -1362,15 +1360,15 @@ JsonSerializerOptions options { return JsonSerializer.Deserialize(ref reader, options) switch { - "local" => global::Stagehand.Models.Sessions.Type.Local, - "browserbase" => global::Stagehand.Models.Sessions.Type.Browserbase, - _ => (global::Stagehand.Models.Sessions.Type)(-1), + "local" => BrowserType.Local, + "browserbase" => BrowserType.Browserbase, + _ => (BrowserType)(-1), }; } public override void Write( Utf8JsonWriter writer, - global::Stagehand.Models.Sessions.Type value, + BrowserType value, JsonSerializerOptions options ) { @@ -1378,8 +1376,8 @@ JsonSerializerOptions options writer, value switch { - global::Stagehand.Models.Sessions.Type.Local => "local", - global::Stagehand.Models.Sessions.Type.Browserbase => "browserbase", + BrowserType.Local => "local", + BrowserType.Browserbase => "browserbase", _ => throw new StagehandInvalidDataException( string.Format("Invalid value '{0}' in {1}", value, nameof(value)) ), From ebca6e031e6d7894675cc0502803fb0cbe985219 Mon Sep 17 00:00:00 2001 From: samfinton Date: Fri, 29 May 2026 17:05:42 +0200 Subject: [PATCH 09/10] fix(csharp): update examples for generated model unions --- examples/local_browser_playwright_example.cs | 5 ++--- examples/local_server_multiregion_browser_example.cs | 5 ++--- examples/remote_browser_playwright_example.cs | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/examples/local_browser_playwright_example.cs b/examples/local_browser_playwright_example.cs index 25a2d12..5cda434 100644 --- a/examples/local_browser_playwright_example.cs +++ b/examples/local_browser_playwright_example.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Stagehand; using Stagehand.Models.Sessions; -using SessionType = Stagehand.Models.Sessions.Type; using StagehandAction = Stagehand.Models.Sessions.Action; namespace Stagehand.Examples @@ -27,7 +26,7 @@ public static async Task RunAsync() ModelName = "anthropic/claude-sonnet-4-6", Browser = new Browser { - Type = SessionType.Local, + Type = BrowserType.Local, LaunchOptions = new LaunchOptions { Headless = true }, }, } @@ -170,7 +169,7 @@ await client.Sessions.Navigate( AgentConfig = new AgentConfig { Model = new AgentConfigModel( - new ModelConfig + new AgentConfigModelGenericModelConfigObject { ModelName = "anthropic/claude-opus-4-6", ApiKey = Environment.GetEnvironmentVariable("MODEL_API_KEY"), diff --git a/examples/local_server_multiregion_browser_example.cs b/examples/local_server_multiregion_browser_example.cs index af3c357..62b4e3c 100644 --- a/examples/local_server_multiregion_browser_example.cs +++ b/examples/local_server_multiregion_browser_example.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Stagehand; using Stagehand.Models.Sessions; -using SessionType = Stagehand.Models.Sessions.Type; using StagehandAction = Stagehand.Models.Sessions.Action; namespace Stagehand.Examples @@ -24,7 +23,7 @@ public static async Task RunAsync() new SessionStartParams { ModelName = "anthropic/claude-sonnet-4-6", - Browser = new Browser { Type = SessionType.Browserbase }, + Browser = new Browser { Type = BrowserType.Browserbase }, BrowserbaseSessionCreateParams = new BrowserbaseSessionCreateParams { Region = Region.EuCentral1, @@ -163,7 +162,7 @@ await client.Sessions.Navigate( AgentConfig = new AgentConfig { Model = new AgentConfigModel( - new ModelConfig + new AgentConfigModelGenericModelConfigObject { ModelName = "anthropic/claude-opus-4-6", ApiKey = Environment.GetEnvironmentVariable("MODEL_API_KEY"), diff --git a/examples/remote_browser_playwright_example.cs b/examples/remote_browser_playwright_example.cs index 3d0e2e5..10ef483 100644 --- a/examples/remote_browser_playwright_example.cs +++ b/examples/remote_browser_playwright_example.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Stagehand; using Stagehand.Models.Sessions; -using SessionType = Stagehand.Models.Sessions.Type; using StagehandAction = Stagehand.Models.Sessions.Action; namespace Stagehand.Examples @@ -24,7 +23,7 @@ public static async Task RunAsync() new SessionStartParams { ModelName = "anthropic/claude-sonnet-4-6", - Browser = new Browser { Type = SessionType.Browserbase }, + Browser = new Browser { Type = BrowserType.Browserbase }, } ); Console.WriteLine($"Session started: {startResponse.Data.SessionID}"); @@ -165,7 +164,7 @@ await client.Sessions.Navigate( AgentConfig = new AgentConfig { Model = new AgentConfigModel( - new ModelConfig + new AgentConfigModelGenericModelConfigObject { ModelName = "anthropic/claude-opus-4-6", ApiKey = Environment.GetEnvironmentVariable("MODEL_API_KEY"), From 9d9c139a9fd48fd2404c183e48a36b783649a47e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 15:06:15 +0000 Subject: [PATCH 10/10] release: 3.21.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 17 +++++++++++++++++ src/Stagehand/Stagehand.csproj | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d11c8fc..eba8a04 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.20.0" + ".": "3.21.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 65365ce..0350a1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 3.21.0 (2026-05-29) + +Full Changelog: [v3.20.0...v3.21.0](https://github.com/browserbase/stagehand-net/compare/v3.20.0...v3.21.0) + +### Features + +* [feat]: add `ignoreSelectors` to `observe()` ([37f80f3](https://github.com/browserbase/stagehand-net/commit/37f80f3936fc9a85e6e6079381c41ef4e3793472)) +* [STG-1756] forward Vertex model config ([491fbd8](https://github.com/browserbase/stagehand-net/commit/491fbd8190a86a1a8e9ba41e849753207aa8574c)) +* Add `screenshot` option to Extract ([1dae7c2](https://github.com/browserbase/stagehand-net/commit/1dae7c22aa40af570d387fa871ee10ea4c0b9084)) +* STG-1756 add Vertex auth params to Stagehand spec ([8fa4926](https://github.com/browserbase/stagehand-net/commit/8fa49262dca9eda52850f3226279b7ee97228f39)) + + +### Bug Fixes + +* **csharp:** update examples for generated model unions ([ebca6e0](https://github.com/browserbase/stagehand-net/commit/ebca6e031e6d7894675cc0502803fb0cbe985219)) +* **internal:** disable default HttpClient timeout as we have our own ([c20c6e0](https://github.com/browserbase/stagehand-net/commit/c20c6e01fb40ff0ab9547bf733f6c735e0949e4e)) + ## 3.20.0 (2026-05-06) Full Changelog: [v3.19.3...v3.20.0](https://github.com/browserbase/stagehand-net/compare/v3.19.3...v3.20.0) diff --git a/src/Stagehand/Stagehand.csproj b/src/Stagehand/Stagehand.csproj index c81c48b..0acca9d 100644 --- a/src/Stagehand/Stagehand.csproj +++ b/src/Stagehand/Stagehand.csproj @@ -3,7 +3,7 @@ Stagehand C# Stagehand - 3.20.0 + 3.21.0 The official .NET library for the Stagehand API. Library README.md