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' 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/.stats.yml b/.stats.yml index 391cde3..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-6f6bfb81d092f30a5e2005328c97d61b9ea36132bb19e9e79e55294b9534ce20.yml -openapi_spec_hash: f3fc1e3688a38dc2c28f7178f7d534e5 +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/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/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"), diff --git a/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs b/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs index bdfc359..e459154 100644 --- a/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/ModelConfigTest.cs @@ -7,24 +7,1651 @@ namespace Stagehand.Tests.Models.Sessions; public class ModelConfigTest : TestBase +{ + [Fact] + 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() + { + 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, + }; + value.Validate(); + } + + [Fact] + public void VertexModelConfigObjectSerializationRoundtripWorks() + { + 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" } }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + 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() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.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", + }; + 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" } }; + + 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 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" } }, + }; + + 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 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" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ModelConfigVertexModelConfigObjectAuth 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 = 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" } }; + + 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 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" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_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" } }, + } + ), + }; + + 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 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(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_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" } }, + } + ), + + // 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 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, + Headers = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_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" } }, + }; + + ModelConfigVertexModelConfigObject copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ModelConfigVertexModelConfigObjectAuthTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + 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", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + ModelConfigVertexModelConfigObjectAuthCredentials 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 = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + 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); + } + + [Fact] + public void SerializationRoundtrip_Works() + { + 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", + }, + 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 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", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + Assert.NotNull(deserialized); + + ModelConfigVertexModelConfigObjectAuthCredentials 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 = ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + UniverseDomain = "universe_domain", + }; + JsonElement expectedType = JsonSerializer.SerializeToElement("googleServiceAccount"); + string expectedProjectID = "projectId"; + 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); + } + + [Fact] + public void Validation_Works() + { + 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", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + 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); + Assert.False(model.RawData.ContainsKey("universeDomain")); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetValidation_Works() + { + 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(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + 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 + 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 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 + ProjectID = null, + Scopes = null, + UniverseDomain = null, + }; + + model.Validate(); + } + + [Fact] + public void CopyConstructor_Works() + { + 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", + }, + ProjectID = "projectId", + Scopes = "string", + UniverseDomain = "universeDomain", + }; + + ModelConfigVertexModelConfigObjectAuth copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ModelConfigVertexModelConfigObjectAuthCredentialsTest : TestBase +{ + [Fact] + public void FieldRoundtrip_Works() + { + var model = new ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.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", + }; + + 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 = + ModelConfigVertexModelConfigObjectAuthCredentialsType.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 ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.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", + }; + + 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 ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.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", + }; + + 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 = + ModelConfigVertexModelConfigObjectAuthCredentialsType.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 ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.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(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() + { + var model = new ModelConfigVertexModelConfigObjectAuthCredentials + { + 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 ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() + { + var model = new ModelConfigVertexModelConfigObjectAuthCredentials + { + 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 ModelConfigVertexModelConfigObjectAuthCredentials + { + 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 ModelConfigVertexModelConfigObjectAuthCredentials + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.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", + }; + + ModelConfigVertexModelConfigObjectAuthCredentials copied = new(model); + + Assert.Equal(model, copied); + } +} + +public class ModelConfigVertexModelConfigObjectAuthCredentialsTypeTest : TestBase +{ + [Theory] + [InlineData(ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void Validation_Works(ModelConfigVertexModelConfigObjectAuthCredentialsType 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(ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount)] + public void SerializationRoundtrip_Works( + ModelConfigVertexModelConfigObjectAuthCredentialsType 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 ModelConfigVertexModelConfigObjectAuthScopesTest : TestBase +{ + [Fact] + public void StringValidationWorks() + { + ModelConfigVertexModelConfigObjectAuthScopes value = "string"; + value.Validate(); + } + + [Fact] + public void StringsValidationWorks() + { + ModelConfigVertexModelConfigObjectAuthScopes value = new(["string"]); + value.Validate(); + } + + [Fact] + public void StringSerializationRoundtripWorks() + { + ModelConfigVertexModelConfigObjectAuthScopes value = "string"; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void StringsSerializationRoundtripWorks() + { + ModelConfigVertexModelConfigObjectAuthScopes value = new(["string"]); + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } +} + +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 ModelConfig + 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 = ModelConfigProvider.OpenAI, + 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 = ModelConfigProvider.OpenAI; + ApiEnum expectedProvider = + ModelConfigGenericModelConfigObjectProvider.OpenAI; Assert.Equal(expectedModelName, model.ModelName); Assert.Equal(expectedApiKey, model.ApiKey); @@ -43,17 +1670,17 @@ public void FieldRoundtrip_Works() [Fact] public void SerializationRoundtrip_Works() { - var model = new ModelConfig + 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 = ModelConfigProvider.OpenAI, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, }; string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + var deserialized = JsonSerializer.Deserialize( json, ModelBase.SerializerOptions ); @@ -64,17 +1691,17 @@ public void SerializationRoundtrip_Works() [Fact] public void FieldRoundtripThroughSerialization_Works() { - var model = new ModelConfig + 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 = ModelConfigProvider.OpenAI, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, }; string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( + var deserialized = JsonSerializer.Deserialize( element, ModelBase.SerializerOptions ); @@ -84,7 +1711,8 @@ public void FieldRoundtripThroughSerialization_Works() string expectedApiKey = "sk-some-openai-api-key"; string expectedBaseUrl = "https://api.openai.com/v1"; Dictionary expectedHeaders = new() { { "foo", "string" } }; - ApiEnum expectedProvider = ModelConfigProvider.OpenAI; + ApiEnum expectedProvider = + ModelConfigGenericModelConfigObjectProvider.OpenAI; Assert.Equal(expectedModelName, deserialized.ModelName); Assert.Equal(expectedApiKey, deserialized.ApiKey); @@ -103,13 +1731,13 @@ public void FieldRoundtripThroughSerialization_Works() [Fact] public void Validation_Works() { - var model = new ModelConfig + 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 = ModelConfigProvider.OpenAI, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, }; model.Validate(); @@ -118,7 +1746,7 @@ public void Validation_Works() [Fact] public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() { - var model = new ModelConfig { ModelName = "openai/gpt-5.4-mini" }; + var model = new ModelConfigGenericModelConfigObject { ModelName = "openai/gpt-5.4-mini" }; Assert.Null(model.ApiKey); Assert.False(model.RawData.ContainsKey("apiKey")); @@ -133,7 +1761,7 @@ public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() [Fact] public void OptionalNonNullablePropertiesUnsetValidation_Works() { - var model = new ModelConfig { ModelName = "openai/gpt-5.4-mini" }; + var model = new ModelConfigGenericModelConfigObject { ModelName = "openai/gpt-5.4-mini" }; model.Validate(); } @@ -141,7 +1769,7 @@ public void OptionalNonNullablePropertiesUnsetValidation_Works() [Fact] public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() { - var model = new ModelConfig + var model = new ModelConfigGenericModelConfigObject { ModelName = "openai/gpt-5.4-mini", @@ -165,7 +1793,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() [Fact] public void OptionalNonNullablePropertiesSetToNullValidation_Works() { - var model = new ModelConfig + var model = new ModelConfigGenericModelConfigObject { ModelName = "openai/gpt-5.4-mini", @@ -182,64 +1810,62 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() [Fact] public void CopyConstructor_Works() { - var model = new ModelConfig + 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 = ModelConfigProvider.OpenAI, + Provider = ModelConfigGenericModelConfigObjectProvider.OpenAI, }; - ModelConfig copied = new(model); + ModelConfigGenericModelConfigObject copied = new(model); Assert.Equal(model, copied); } } -public class ModelConfigProviderTest : TestBase +public class ModelConfigGenericModelConfigObjectProviderTest : TestBase { [Theory] - [InlineData(ModelConfigProvider.OpenAI)] - [InlineData(ModelConfigProvider.Anthropic)] - [InlineData(ModelConfigProvider.Google)] - [InlineData(ModelConfigProvider.Microsoft)] - [InlineData(ModelConfigProvider.Bedrock)] - 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)] - 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); } @@ -247,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 9982b63..c6a187c 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionActParamsTest.cs @@ -20,13 +20,41 @@ public void FieldRoundtrip_Works() FrameID = "frameId", Options = new() { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -50,13 +78,41 @@ public void FieldRoundtrip_Works() string expectedFrameID = "frameId"; Sessions::Options expectedOptions = new() { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -127,13 +183,41 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Input = "Click the login button", Options = new() { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -165,13 +249,41 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Input = "Click the login button", Options = new() { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -258,13 +370,41 @@ public void CopyConstructor_Works() FrameID = "frameId", Options = new() { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -353,13 +493,41 @@ public void FieldRoundtrip_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -376,13 +544,41 @@ public void FieldRoundtrip_Works() }, }; - Sessions::Model expectedModel = new Sessions::ModelConfig() + Sessions::Model expectedModel = 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -415,13 +611,41 @@ public void SerializationRoundtrip_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -452,13 +676,41 @@ public void FieldRoundtripThroughSerialization_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -482,13 +734,41 @@ public void FieldRoundtripThroughSerialization_Works() ); Assert.NotNull(deserialized); - Sessions::Model expectedModel = new Sessions::ModelConfig() + Sessions::Model expectedModel = 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -521,13 +801,41 @@ public void Validation_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -606,13 +914,41 @@ public void CopyConstructor_Works() { var model = new Sessions::Options { - Model = new Sessions::ModelConfig() + 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" } }, - Provider = Sessions::ModelConfigProvider.OpenAI, }, Timeout = 30000, Variables = new Dictionary() @@ -638,15 +974,57 @@ public void CopyConstructor_Works() public class ModelTest : TestBase { [Fact] - public void ConfigValidationWorks() + public void VertexModelConfigObjectValidationWorks() + { + Sessions::Model value = 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" } }, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() { - Sessions::Model value = new Sessions::ModelConfig() + 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::ModelConfigProvider.OpenAI, + Provider = Sessions::Provider.OpenAI, }; value.Validate(); } @@ -659,15 +1037,63 @@ public void StringValidationWorks() } [Fact] - public void ConfigSerializationRoundtripWorks() + public void VertexModelConfigObjectSerializationRoundtripWorks() + { + Sessions::Model value = 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(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GenericModelConfigObjectSerializationRoundtripWorks() { - Sessions::Model value = new Sessions::ModelConfig() + 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::ModelConfigProvider.OpenAI, + Provider = Sessions::Provider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); var deserialized = JsonSerializer.Deserialize( @@ -692,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 20aaaca..1de9022 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionExecuteParamsTest.cs @@ -19,24 +19,82 @@ public void FieldRoundtrip_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -61,24 +119,82 @@ public void FieldRoundtrip_Works() AgentConfig expectedAgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; ExecuteOptions expectedExecuteOptions = new() @@ -113,24 +229,82 @@ public void OptionalNonNullableParamsUnsetAreNotSet_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -164,24 +338,82 @@ public void OptionalNonNullableParamsSetToNullAreNotSet_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -219,24 +451,82 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -269,24 +559,82 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -321,24 +669,82 @@ public void Url_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -385,24 +791,82 @@ public void AddHeadersToRequest_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -443,24 +907,82 @@ public void CopyConstructor_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -495,46 +1017,161 @@ public void FieldRoundtrip_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; bool expectedCua = true; - ExecutionModel expectedExecutionModel = new ModelConfig() + ExecutionModel expectedExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }; ApiEnum expectedMode = Mode.Cua; - AgentConfigModel expectedModel = new ModelConfig() + AgentConfigModel expectedModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }; - ApiEnum expectedProvider = Provider.OpenAI; + ApiEnum expectedProvider = AgentConfigProvider.OpenAI; string expectedSystemPrompt = "systemPrompt"; Assert.Equal(expectedCua, model.Cua); @@ -551,24 +1188,82 @@ public void SerializationRoundtrip_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -587,24 +1282,82 @@ public void FieldRoundtripThroughSerialization_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -616,24 +1369,81 @@ public void FieldRoundtripThroughSerialization_Works() Assert.NotNull(deserialized); bool expectedCua = true; - ExecutionModel expectedExecutionModel = new ModelConfig() + ExecutionModel expectedExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }; ApiEnum expectedMode = Mode.Cua; - AgentConfigModel expectedModel = new ModelConfig() + AgentConfigModel expectedModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }; - ApiEnum expectedProvider = Provider.OpenAI; + ApiEnum expectedProvider = AgentConfigProvider.OpenAI; string expectedSystemPrompt = "systemPrompt"; Assert.Equal(expectedCua, deserialized.Cua); @@ -650,24 +1460,82 @@ public void Validation_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -752,24 +1620,82 @@ public void CopyConstructor_Works() var model = new AgentConfig { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }; @@ -782,15 +1708,57 @@ public void CopyConstructor_Works() public class ExecutionModelTest : TestBase { [Fact] - public void ModelConfigValidationWorks() + public void VertexModelConfigObjectValidationWorks() + { + ExecutionModel value = 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" } }, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() { - ExecutionModel value = new ModelConfig() + 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 = ModelConfigProvider.OpenAI, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, }; value.Validate(); } @@ -803,15 +1771,63 @@ public void StringValidationWorks() } [Fact] - public void ModelConfigSerializationRoundtripWorks() + public void VertexModelConfigObjectSerializationRoundtripWorks() + { + ExecutionModel value = 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" } }, + }; + string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, + ModelBase.SerializerOptions + ); + + Assert.Equal(value, deserialized); + } + + [Fact] + public void GenericModelConfigObjectSerializationRoundtripWorks() { - ExecutionModel value = new ModelConfig() + 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 = ModelConfigProvider.OpenAI, + Provider = ExecutionModelGenericModelConfigObjectProvider.OpenAI, }; string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); var deserialized = JsonSerializer.Deserialize( @@ -836,142 +1852,3771 @@ public void StringSerializationRoundtripWorks() } } -public class ModeTest : TestBase +public class ExecutionModelVertexModelConfigObjectTest : 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() + public void FieldRoundtrip_Works() { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), - 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.NotNull(value); - Assert.Throws(() => value.Validate()); + Assert.Equal(value, model.Headers[item.Key]); + } } - [Theory] - [InlineData(Mode.Dom)] - [InlineData(Mode.Hybrid)] - [InlineData(Mode.Cua)] - public void SerializationRoundtrip_Works(Mode rawValue) + [Fact] + public void SerializationRoundtrip_Works() { - // force implicit conversion because Theory can't do that for us - ApiEnum value = rawValue; + 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" } }, + }; - string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( + string json = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( json, ModelBase.SerializerOptions ); - Assert.Equal(value, deserialized); + Assert.Equal(model, deserialized); } [Fact] - public void InvalidEnumSerializationRoundtrip_Works() + public void FieldRoundtripThroughSerialization_Works() { - var value = JsonSerializer.Deserialize>( - JsonSerializer.SerializeToElement("invalid value"), + 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" } }, + }; + + string element = JsonSerializer.Serialize(model, ModelBase.SerializerOptions); + var deserialized = JsonSerializer.Deserialize( + element, ModelBase.SerializerOptions ); - string json = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize>( - json, - ModelBase.SerializerOptions + Assert.NotNull(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); + Assert.Equal(value, deserialized.Headers[item.Key]); + } } -} -public class AgentConfigModelTest : TestBase -{ [Fact] - public void ConfigValidationWorks() + public void Validation_Works() { - AgentConfigModel value = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }; - value.Validate(); + + model.Validate(); } [Fact] - public void StringValidationWorks() + public void OptionalNonNullablePropertiesUnsetAreNotSet_Works() { - AgentConfigModel value = "string"; - value.Validate(); + 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 ConfigSerializationRoundtripWorks() + public void OptionalNonNullablePropertiesUnsetValidation_Works() { - AgentConfigModel value = new ModelConfig() + 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", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, + ProviderOptions = new( + new ExecutionModelVertexModelConfigObjectProviderOptionsVertex() + { + Location = "us-central1", + Project = "my-gcp-project", + BaseUrl = "https://example.com", + Headers = new Dictionary() { { "foo", "string" } }, + } + ), }; - 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 OptionalNonNullablePropertiesSetToNullAreNotSet_Works() { - AgentConfigModel value = "string"; - string element = JsonSerializer.Serialize(value, ModelBase.SerializerOptions); - var deserialized = JsonSerializer.Deserialize( - element, - 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" } }, + } + ), + + // 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() + { + 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 ); @@ -981,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 ); @@ -1003,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 acbc7a0..3c3947f 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionExtractParamsTest.cs @@ -21,14 +21,44 @@ public void FieldRoundtrip_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -45,14 +75,44 @@ public void FieldRoundtrip_Works() SessionExtractParamsOptions expectedOptions = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -132,14 +192,44 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -164,14 +254,44 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -246,14 +366,44 @@ public void CopyConstructor_Works() Options = new() { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }, @@ -278,27 +428,88 @@ public void FieldRoundtrip_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; 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", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + SessionExtractParamsOptionsModel expectedModel = + 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" } }, + }; + bool expectedScreenshot = false; string expectedSelector = "#main-content"; double expectedTimeout = 30000; @@ -309,6 +520,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); } @@ -319,14 +531,44 @@ public void SerializationRoundtrip_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -346,14 +588,44 @@ public void FieldRoundtripThroughSerialization_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -366,14 +638,45 @@ 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", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + SessionExtractParamsOptionsModel expectedModel = + 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" } }, + }; + bool expectedScreenshot = false; string expectedSelector = "#main-content"; double expectedTimeout = 30000; @@ -384,6 +687,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); } @@ -394,14 +698,44 @@ public void Validation_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -418,6 +752,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); @@ -440,6 +776,7 @@ public void OptionalNonNullablePropertiesSetToNullAreNotSet_Works() // Null should be interpreted as omitted for these properties IgnoreSelectors = null, Model = null, + Screenshot = null, Selector = null, Timeout = null, }; @@ -448,6 +785,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); @@ -462,6 +801,7 @@ public void OptionalNonNullablePropertiesSetToNullValidation_Works() // Null should be interpreted as omitted for these properties IgnoreSelectors = null, Model = null, + Screenshot = null, Selector = null, Timeout = null, }; @@ -475,14 +815,44 @@ public void CopyConstructor_Works() var model = new SessionExtractParamsOptions { IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, + Screenshot = false, Selector = "#main-content", Timeout = 30000, }; @@ -496,16 +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", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + SessionExtractParamsOptionsModel value = + 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" } }, + }; + 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(); } @@ -517,16 +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", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + SessionExtractParamsOptionsModel value = + 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(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, @@ -550,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 e615f51..b5a6a9a 100644 --- a/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs +++ b/src/Stagehand.Tests/Models/Sessions/SessionObserveParamsTest.cs @@ -20,13 +20,43 @@ public void FieldRoundtrip_Works() Instruction = "Find all clickable navigation links", Options = new() { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -51,13 +81,43 @@ public void FieldRoundtrip_Works() string expectedInstruction = "Find all clickable navigation links"; SessionObserveParamsOptions expectedOptions = new() { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -132,13 +192,43 @@ public void OptionalNullableParamsUnsetAreNotSet_Works() Instruction = "Find all clickable navigation links", Options = new() { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -171,13 +261,43 @@ public void OptionalNullableParamsSetToNullAreSetToNull_Works() Instruction = "Find all clickable navigation links", Options = new() { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -260,13 +380,43 @@ public void CopyConstructor_Works() Instruction = "Find all clickable navigation links", Options = new() { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -299,13 +449,43 @@ public void FieldRoundtrip_Works() { var model = new SessionObserveParamsOptions { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -323,14 +503,45 @@ public void FieldRoundtrip_Works() }, }; - SessionObserveParamsOptionsModel expectedModel = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; + SessionObserveParamsOptionsModel expectedModel = + 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 expectedSelector = "nav"; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -346,6 +557,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,13 +581,43 @@ public void SerializationRoundtrip_Works() { var model = new SessionObserveParamsOptions { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -402,13 +649,43 @@ public void FieldRoundtripThroughSerialization_Works() { var model = new SessionObserveParamsOptions { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -433,14 +710,45 @@ public void FieldRoundtripThroughSerialization_Works() ); Assert.NotNull(deserialized); - SessionObserveParamsOptionsModel expectedModel = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + List expectedIgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"]; + SessionObserveParamsOptionsModel expectedModel = + 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 expectedSelector = "nav"; double expectedTimeout = 30000; Dictionary expectedVariables = new() @@ -456,6 +764,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,13 +788,43 @@ public void Validation_Works() { var model = new SessionObserveParamsOptions { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -506,6 +850,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 +876,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 +901,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,13 +916,43 @@ public void CopyConstructor_Works() { var model = new SessionObserveParamsOptions { - Model = new ModelConfig() + IgnoreSelectors = ["nav", ".cookie-banner", "#sidebar-ads"], + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Selector = "nav", Timeout = 30000, @@ -599,16 +979,61 @@ 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", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + 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" } }, + }; + value.Validate(); + } + + [Fact] + public void GenericModelConfigObjectValidationWorks() + { + 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(); } @@ -620,16 +1045,67 @@ public void StringValidationWorks() } [Fact] - public void ConfigSerializationRoundtripWorks() + public void VertexModelConfigObjectSerializationRoundtripWorks() { - SessionObserveParamsOptionsModel value = new ModelConfig() - { - ModelName = "openai/gpt-5.4-mini", - ApiKey = "sk-some-openai-api-key", - BaseUrl = "https://api.openai.com/v1", - Headers = new Dictionary() { { "foo", "string" } }, - Provider = ModelConfigProvider.OpenAI, - }; + 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, @@ -653,6 +1129,1851 @@ public void StringSerializationRoundtripWorks() } } +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", + 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() + { + ClientEmail = "client_email", + PrivateKey = "private_key", + AuthProviderX509CertUrl = "https://example.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" } }, + }; + + model.Validate(); + } + + [Fact] + public void OptionalNonNullablePropertiesUnsetAreNotSet_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" } }, + } + ), + }; + + 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); + } +} + public class SessionObserveParamsOptionsVariableTest : TestBase { [Fact] 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 bf5d2b0..5389ffc 100644 --- a/src/Stagehand.Tests/Services/SessionServiceTest.cs +++ b/src/Stagehand.Tests/Services/SessionServiceTest.cs @@ -53,24 +53,82 @@ public async Task Execute_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() @@ -102,24 +160,82 @@ public async Task ExecuteStreaming_Works() AgentConfig = new() { Cua = true, - ExecutionModel = new ModelConfig() + ExecutionModel = 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" } }, - Provider = ModelConfigProvider.OpenAI, }, Mode = Mode.Cua, - Model = new ModelConfig() + 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" } }, - Provider = ModelConfigProvider.OpenAI, }, - Provider = Provider.OpenAI, + Provider = AgentConfigProvider.OpenAI, SystemPrompt = "systemPrompt", }, ExecuteOptions = new() 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") diff --git a/src/Stagehand/Core/ModelBase.cs b/src/Stagehand/Core/ModelBase.cs index 08c436c..9307f4d 100644 --- a/src/Stagehand/Core/ModelBase.cs +++ b/src/Stagehand/Core/ModelBase.cs @@ -21,20 +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< + 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 1fcacce..da083aa 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; @@ -9,8 +10,1461 @@ namespace Stagehand.Models.Sessions; -[JsonConverter(typeof(JsonModelConverter))] -public sealed record class ModelConfig : JsonModel +[JsonConverter(typeof(ModelConfigConverter))] +public record class ModelConfig : 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 + ); + } + } + + public string? ApiKey + { + get + { + return Match( + vertexModelConfigObject: (x) => x.ApiKey, + genericModelConfigObject: (x) => x.ApiKey + ); + } + } + + public string? BaseUrl + { + get + { + return Match( + vertexModelConfigObject: (x) => x.BaseUrl, + genericModelConfigObject: (x) => x.BaseUrl + ); + } + } + + public ModelConfig(ModelConfigVertexModelConfigObject value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ModelConfig(ModelConfigGenericModelConfigObject value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ModelConfig(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 `ModelConfigVertexModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickVertexModelConfigObject( + [NotNullWhen(true)] out ModelConfigVertexModelConfigObject? value + ) + { + value = this.Value as ModelConfigVertexModelConfigObject; + 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 `ModelConfigGenericModelConfigObject` + /// Console.WriteLine(value); + /// } + /// + /// + /// + public bool TryPickGenericModelConfigObject( + [NotNullWhen(true)] out ModelConfigGenericModelConfigObject? value + ) + { + value = this.Value as ModelConfigGenericModelConfigObject; + 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( + /// (ModelConfigVertexModelConfigObject value) => {...}, + /// (ModelConfigGenericModelConfigObject value) => {...} + /// ); + /// + /// + /// + public void Switch( + System::Action vertexModelConfigObject, + System::Action genericModelConfigObject + ) + { + switch (this.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" + ); + } + } + + /// + /// 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 + ) + { + return this.Value switch + { + ModelConfigVertexModelConfigObject value => vertexModelConfigObject(value), + ModelConfigGenericModelConfigObject value => genericModelConfigObject(value), + _ => throw new StagehandInvalidDataException( + "Data did not match any variant of ModelConfig" + ), + }; + } + + public static implicit operator ModelConfig(ModelConfigVertexModelConfigObject value) => + new(value); + + public static implicit operator ModelConfig(ModelConfigGenericModelConfigObject 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 ModelConfig" + ); + } + this.Switch( + (vertexModelConfigObject) => vertexModelConfigObject.Validate(), + (genericModelConfigObject) => genericModelConfigObject.Validate() + ); + } + + public virtual bool Equals(ModelConfig? 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 + { + ModelConfigVertexModelConfigObject _ => 0, + ModelConfigGenericModelConfigObject _ => 1, + _ => -1, + }; + } +} + +sealed class ModelConfigConverter : JsonConverter +{ + 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); + } +} + +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigVertexModelConfigObject, + ModelConfigVertexModelConfigObjectFromRaw + >) +)] +public sealed record class ModelConfigVertexModelConfigObject : JsonModel +{ + /// + /// 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 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 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); } + } + + /// + /// 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 ModelConfigVertexModelConfigObjectAuthScopes? 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 ModelConfigVertexModelConfigObjectAuth() + { + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObjectAuth( + ModelConfigVertexModelConfigObjectAuth modelConfigVertexModelConfigObjectAuth + ) + : base(modelConfigVertexModelConfigObjectAuth) { } +#pragma warning restore CS8618 + + public ModelConfigVertexModelConfigObjectAuth(IReadOnlyDictionary rawData) + { + this._rawData = new(rawData); + + this.Type = JsonSerializer.SerializeToElement("googleServiceAccount"); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ModelConfigVertexModelConfigObjectAuth(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ModelConfigVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } + + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObjectAuth( + ModelConfigVertexModelConfigObjectAuthCredentials credentials + ) + : this() + { + this.Credentials = credentials; + } +} + +class ModelConfigVertexModelConfigObjectAuthFromRaw + : IFromRawJson +{ + /// + public ModelConfigVertexModelConfigObjectAuth FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigVertexModelConfigObjectAuth.FromRawUnchecked(rawData); +} + +/// +/// Google Cloud service account credentials +/// +[JsonConverter( + typeof(JsonModelConverter< + ModelConfigVertexModelConfigObjectAuthCredentials, + ModelConfigVertexModelConfigObjectAuthCredentialsFromRaw + >) +)] +public sealed record class ModelConfigVertexModelConfigObjectAuthCredentials : 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 ModelConfigVertexModelConfigObjectAuthCredentials() { } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + public ModelConfigVertexModelConfigObjectAuthCredentials( + ModelConfigVertexModelConfigObjectAuthCredentials modelConfigVertexModelConfigObjectAuthCredentials + ) + : base(modelConfigVertexModelConfigObjectAuthCredentials) { } +#pragma warning restore CS8618 + + public ModelConfigVertexModelConfigObjectAuthCredentials( + IReadOnlyDictionary rawData + ) + { + this._rawData = new(rawData); + } + +#pragma warning disable CS8618 + [SetsRequiredMembers] + ModelConfigVertexModelConfigObjectAuthCredentials(FrozenDictionary rawData) + { + this._rawData = new(rawData); + } +#pragma warning restore CS8618 + + /// + public static ModelConfigVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) + { + return new(FrozenDictionary.ToFrozenDictionary(rawData)); + } +} + +class ModelConfigVertexModelConfigObjectAuthCredentialsFromRaw + : IFromRawJson +{ + /// + public ModelConfigVertexModelConfigObjectAuthCredentials FromRawUnchecked( + IReadOnlyDictionary rawData + ) => ModelConfigVertexModelConfigObjectAuthCredentials.FromRawUnchecked(rawData); +} + +[JsonConverter(typeof(ModelConfigVertexModelConfigObjectAuthCredentialsTypeConverter))] +public enum ModelConfigVertexModelConfigObjectAuthCredentialsType +{ + ServiceAccount, +} + +sealed class ModelConfigVertexModelConfigObjectAuthCredentialsTypeConverter + : JsonConverter +{ + public override ModelConfigVertexModelConfigObjectAuthCredentialsType Read( + ref Utf8JsonReader reader, + System::Type typeToConvert, + JsonSerializerOptions options + ) + { + return JsonSerializer.Deserialize(ref reader, options) switch + { + "service_account" => + ModelConfigVertexModelConfigObjectAuthCredentialsType.ServiceAccount, + _ => (ModelConfigVertexModelConfigObjectAuthCredentialsType)(-1), + }; + } + + public override void Write( + Utf8JsonWriter writer, + ModelConfigVertexModelConfigObjectAuthCredentialsType value, + JsonSerializerOptions options + ) + { + JsonSerializer.Serialize( + writer, + value switch + { + ModelConfigVertexModelConfigObjectAuthCredentialsType.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(ModelConfigVertexModelConfigObjectAuthScopesConverter))] +public record class ModelConfigVertexModelConfigObjectAuthScopes : ModelBase +{ + public object? Value { get; } = null; + + JsonElement? _element = null; + + public JsonElement Json + { + get + { + return this._element ??= JsonSerializer.SerializeToElement( + this.Value, + ModelBase.SerializerOptions + ); + } + } + + public ModelConfigVertexModelConfigObjectAuthScopes(string value, JsonElement? element = null) + { + this.Value = value; + this._element = element; + } + + public ModelConfigVertexModelConfigObjectAuthScopes( + IReadOnlyList value, + JsonElement? element = null + ) + { + this.Value = ImmutableArray.ToImmutableArray(value); + this._element = element; + } + + public ModelConfigVertexModelConfigObjectAuthScopes(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 ModelConfigVertexModelConfigObjectAuthScopes" + ); + } + } + + /// + /// 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 ModelConfigVertexModelConfigObjectAuthScopes" + ), + }; + } + + public static implicit operator ModelConfigVertexModelConfigObjectAuthScopes(string value) => + new(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 + /// (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 ModelConfigVertexModelConfigObjectAuthScopes" + ); + } + } + + public virtual bool Equals(ModelConfigVertexModelConfigObjectAuthScopes? 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 ModelConfigVertexModelConfigObjectAuthScopesConverter + : JsonConverter +{ + public override ModelConfigVertexModelConfigObjectAuthScopes? 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, + 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') @@ -94,12 +1548,14 @@ public IReadOnlyDictionary? Headers /// /// AI provider for the model (or provide a baseURL endpoint instead) /// - public ApiEnum? Provider + public ApiEnum? Provider { get { this._rawData.Freeze(); - return this._rawData.GetNullableClass>("provider"); + return this._rawData.GetNullableClass< + ApiEnum + >("provider"); } init { @@ -122,53 +1578,58 @@ public override void Validate() this.Provider?.Validate(); } - public ModelConfig() { } + public ModelConfigGenericModelConfigObject() { } #pragma warning disable CS8618 [SetsRequiredMembers] - public ModelConfig(ModelConfig modelConfig) - : base(modelConfig) { } + public ModelConfigGenericModelConfigObject( + ModelConfigGenericModelConfigObject modelConfigGenericModelConfigObject + ) + : base(modelConfigGenericModelConfigObject) { } #pragma warning restore CS8618 - public ModelConfig(IReadOnlyDictionary rawData) + public ModelConfigGenericModelConfigObject(IReadOnlyDictionary rawData) { this._rawData = new(rawData); } #pragma warning disable CS8618 [SetsRequiredMembers] - ModelConfig(FrozenDictionary rawData) + ModelConfigGenericModelConfigObject(FrozenDictionary rawData) { this._rawData = new(rawData); } #pragma warning restore CS8618 - /// - public static ModelConfig FromRawUnchecked(IReadOnlyDictionary rawData) + /// + public static ModelConfigGenericModelConfigObject FromRawUnchecked( + IReadOnlyDictionary rawData + ) { return new(FrozenDictionary.ToFrozenDictionary(rawData)); } [SetsRequiredMembers] - public ModelConfig(string modelName) + public ModelConfigGenericModelConfigObject(string modelName) : this() { this.ModelName = modelName; } } -class ModelConfigFromRaw : IFromRawJson +class ModelConfigGenericModelConfigObjectFromRaw : IFromRawJson { /// - public ModelConfig FromRawUnchecked(IReadOnlyDictionary rawData) => - ModelConfig.FromRawUnchecked(rawData); + 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, @@ -177,9 +1638,10 @@ public enum ModelConfigProvider Bedrock, } -sealed class ModelConfigProviderConverter : JsonConverter +sealed class ModelConfigGenericModelConfigObjectProviderConverter + : JsonConverter { - public override ModelConfigProvider Read( + public override ModelConfigGenericModelConfigObjectProvider Read( ref Utf8JsonReader reader, System::Type typeToConvert, JsonSerializerOptions options @@ -187,18 +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, - _ => (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 ) { @@ -206,11 +1668,11 @@ JsonSerializerOptions options writer, value switch { - ModelConfigProvider.OpenAI => "openai", - ModelConfigProvider.Anthropic => "anthropic", - ModelConfigProvider.Google => "google", - ModelConfigProvider.Microsoft => "microsoft", - ModelConfigProvider.Bedrock => "bedrock", + 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 a549d13..8f06d83 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; } @@ -411,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; @@ -430,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; } @@ -484,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); @@ -522,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" @@ -540,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); @@ -563,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) => @@ -586,8 +713,9 @@ int VariantIndex() { return this.Value switch { - ModelConfig _ => 0, - string _ => 1, + SessionExtractParamsOptionsModelVertexModelConfigObject _ => 0, + SessionExtractParamsOptionsModelGenericModelConfigObject _ => 1, + string _ => 2, _ => -1, }; } @@ -605,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(); @@ -643,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 62a87ed..d71c07a 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; @@ -394,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; @@ -413,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; } @@ -467,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); @@ -505,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" @@ -523,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); @@ -546,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) => @@ -569,8 +699,9 @@ int VariantIndex() { return this.Value switch { - ModelConfig _ => 0, - string _ => 1, + SessionObserveParamsOptionsModelVertexModelConfigObject _ => 0, + SessionObserveParamsOptionsModelGenericModelConfigObject _ => 1, + string _ => 2, _ => -1, }; } @@ -588,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(); @@ -626,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)) ), 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