diff --git a/.github/ISSUE_TEMPLATE/Bug_report.yml b/.github/ISSUE_TEMPLATE/Bug_report.yml index badb60b97ef..790457f2d98 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -47,6 +47,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-csharp/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-csharp/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-csharp/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-csharp/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingClient.cs index e65a1032f3a..42a41a18e59 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingClient.cs @@ -830,8 +830,7 @@ public AbtestingClient( new AbtestingConfig(applicationId, apiKey, region), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingV3Client.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingV3Client.cs index 2206ad27cc7..5b0a76cc687 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingV3Client.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/AbtestingV3Client.cs @@ -938,8 +938,7 @@ public AbtestingV3Client( new AbtestingV3Config(applicationId, apiKey, region), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/AnalyticsClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/AnalyticsClient.cs index 89196f67d1a..27c41933c62 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/AnalyticsClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/AnalyticsClient.cs @@ -2542,8 +2542,7 @@ public AnalyticsClient( new AnalyticsConfig(applicationId, apiKey, region), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/CompositionClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/CompositionClient.cs index 979c8d45c2d..6ddb80bc327 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/CompositionClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/CompositionClient.cs @@ -1540,8 +1540,7 @@ public CompositionClient(string applicationId, string apiKey, ILoggerFactory log new CompositionConfig(applicationId, apiKey), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/IngestionClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/IngestionClient.cs index e25969ef31e..b437e97fda9 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/IngestionClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/IngestionClient.cs @@ -5582,8 +5582,7 @@ public IngestionClient( new IngestionConfig(applicationId, apiKey, region), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/InsightsClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/InsightsClient.cs index 207a259f79c..3099ec3db48 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/InsightsClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/InsightsClient.cs @@ -502,8 +502,7 @@ public InsightsClient( new InsightsConfig(applicationId, apiKey, region), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/MonitoringClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/MonitoringClient.cs index aca780a104c..3ce59ab6dda 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/MonitoringClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/MonitoringClient.cs @@ -904,8 +904,7 @@ public MonitoringClient(string applicationId, string apiKey, ILoggerFactory logg new MonitoringConfig(applicationId, apiKey), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/PersonalizationClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/PersonalizationClient.cs index 83d930cefca..0dce180567e 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/PersonalizationClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/PersonalizationClient.cs @@ -646,8 +646,7 @@ public PersonalizationClient( new PersonalizationConfig(applicationId, apiKey, region), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/QuerySuggestionsClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/QuerySuggestionsClient.cs index 30c094f6688..12e75121e7c 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/QuerySuggestionsClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/QuerySuggestionsClient.cs @@ -882,8 +882,7 @@ public QuerySuggestionsClient( new QuerySuggestionsConfig(applicationId, apiKey, region), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/RecommendClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/RecommendClient.cs index dfbd16c56cd..cebf5b80637 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/RecommendClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/RecommendClient.cs @@ -880,8 +880,7 @@ public RecommendClient(string applicationId, string apiKey, ILoggerFactory logge new RecommendConfig(applicationId, apiKey), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Clients/SearchClient.cs b/clients/algoliasearch-client-csharp/algoliasearch/Clients/SearchClient.cs index 0aa06d3158e..fdc7579fd5c 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Clients/SearchClient.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Clients/SearchClient.cs @@ -5158,8 +5158,7 @@ public SearchClient(string applicationId, string apiKey, ILoggerFactory loggerFa new SearchConfig(applicationId, apiKey), new AlgoliaHttpRequester(loggerFactory), loggerFactory - ) - { } + ) { } /// /// Initialize a client with custom config diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Serializer/EnumConverter.cs b/clients/algoliasearch-client-csharp/algoliasearch/Serializer/EnumConverter.cs index 8c41dfea480..bcb0e4a9e20 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Serializer/EnumConverter.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Serializer/EnumConverter.cs @@ -91,21 +91,21 @@ JsonSerializerOptions options switch (type) { case JsonTokenType.String: + { + var stringValue = reader.GetString(); + if (stringValue != null && _stringToEnum.TryGetValue(stringValue, out var enumValue)) { - var stringValue = reader.GetString(); - if (stringValue != null && _stringToEnum.TryGetValue(stringValue, out var enumValue)) - { - return enumValue; - } - - break; - } - case JsonTokenType.Number: - { - var numValue = reader.GetInt32(); - _numberToEnum.TryGetValue(numValue, out var enumValue); return enumValue; } + + break; + } + case JsonTokenType.Number: + { + var numValue = reader.GetInt32(); + _numberToEnum.TryGetValue(numValue, out var enumValue); + return enumValue; + } } return default; diff --git a/clients/algoliasearch-client-csharp/algoliasearch/Utils/QueryStringHelper.cs b/clients/algoliasearch-client-csharp/algoliasearch/Utils/QueryStringHelper.cs index a890dda9b58..07decce4895 100644 --- a/clients/algoliasearch-client-csharp/algoliasearch/Utils/QueryStringHelper.cs +++ b/clients/algoliasearch-client-csharp/algoliasearch/Utils/QueryStringHelper.cs @@ -36,18 +36,18 @@ public static string ParameterToString(object obj) case bool boolean: return boolean ? "true" : "false"; case ICollection collection: - { - var entries = new List(); - foreach (var entry in collection) - entries.Add(ParameterToString(entry)); - return string.Join(",", entries); - } + { + var entries = new List(); + foreach (var entry in collection) + entries.Add(ParameterToString(entry)); + return string.Join(",", entries); + } case Enum when HasEnumMemberAttrValue(obj): return GetEnumMemberAttrValue(obj); case AbstractSchema schema when obj.GetType().IsClass: - { - return ParameterToString(schema.ActualInstance); - } + { + return ParameterToString(schema.ActualInstance); + } default: return Convert.ToString(obj, CultureInfo.InvariantCulture); } diff --git a/clients/algoliasearch-client-dart/packages/client_core/lib/src/algolia_exception.dart b/clients/algoliasearch-client-dart/packages/client_core/lib/src/algolia_exception.dart index 8fb9847cfe4..79d45ff4b6b 100644 --- a/clients/algoliasearch-client-dart/packages/client_core/lib/src/algolia_exception.dart +++ b/clients/algoliasearch-client-dart/packages/client_core/lib/src/algolia_exception.dart @@ -67,6 +67,17 @@ final class AlgoliaWaitException implements AlgoliaException { } } +/// Represents a successful API response with no content (HTTP 204). +/// +/// Returned by custom request methods when the server responds with +/// 204 No Content, as Dart's type system requires a non-null return value. +final class AlgoliaNoResponse { + const AlgoliaNoResponse(); + + @override + String toString() => 'AlgoliaNoResponse{}'; +} + /// Exception thrown when all hosts for the Algolia API are unreachable. /// /// Contains a list of the errors associated with each unreachable host. diff --git a/clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/retry_strategy.dart b/clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/retry_strategy.dart index 68e5b50ad57..2313b5a8130 100644 --- a/clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/retry_strategy.dart +++ b/clients/algoliasearch-client-dart/packages/client_core/lib/src/transport/retry_strategy.dart @@ -48,7 +48,7 @@ final class RetryStrategy { ); /// Run an request and get a response. - Future> execute({ + Future?> execute({ required ApiRequest request, RequestOptions? options, }) async { @@ -66,7 +66,7 @@ final class RetryStrategy { final response = await requester.perform(httpRequest); host.reset(); requester.setConnectTimeout(requesterConnectTimeout); - return response.body ?? const {}; + return response.statusCode == 204 ? null : response.body; } on AlgoliaTimeoutException catch (e) { host.timedOut(); errors.add(e); diff --git a/clients/algoliasearch-client-go/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-go/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-go/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-go/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-go/.golangci.yml b/clients/algoliasearch-client-go/.golangci.yml index be69abccf84..a92789d3335 100644 --- a/clients/algoliasearch-client-go/.golangci.yml +++ b/clients/algoliasearch-client-go/.golangci.yml @@ -56,6 +56,10 @@ linters: presets: - comments - std-error-handling + rules: + - linters: + - wrapcheck + text: ChunkedPush formatters: enable: - gofmt diff --git a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_direction.go b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_direction.go index 59734c8db21..4363fd133fb 100644 --- a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_direction.go +++ b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_direction.go @@ -4,6 +4,7 @@ package abtestingV3 import ( "encoding/json" "fmt" + "slices" ) // Direction Sort order for A/B tests by start date. Use 'asc' for ascending or 'desc' for descending. Active A/B tests are always listed first. @@ -41,12 +42,10 @@ func (v *Direction) UnmarshalJSON(src []byte) error { } enumTypeValue := Direction(value) - for _, existing := range AllowedDirectionEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDirectionEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Direction", value) @@ -54,13 +53,7 @@ func (v *Direction) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Direction) IsValid() bool { - for _, existing := range AllowedDirectionEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDirectionEnumValues, v) } // Ptr returns reference to direction value. diff --git a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_effect_metric.go b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_effect_metric.go index 5d55f4bd295..cd1161289bb 100644 --- a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_effect_metric.go +++ b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_effect_metric.go @@ -4,6 +4,7 @@ package abtestingV3 import ( "encoding/json" "fmt" + "slices" ) // EffectMetric Metric for which you want to detect the smallest relative difference. @@ -47,12 +48,10 @@ func (v *EffectMetric) UnmarshalJSON(src []byte) error { } enumTypeValue := EffectMetric(value) - for _, existing := range AllowedEffectMetricEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEffectMetricEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EffectMetric", value) @@ -60,13 +59,7 @@ func (v *EffectMetric) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EffectMetric) IsValid() bool { - for _, existing := range AllowedEffectMetricEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEffectMetricEnumValues, v) } // Ptr returns reference to EffectMetric value. diff --git a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_error_correction_type.go b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_error_correction_type.go index 21d99e50f92..68c8b0d4567 100644 --- a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_error_correction_type.go +++ b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_error_correction_type.go @@ -4,6 +4,7 @@ package abtestingV3 import ( "encoding/json" "fmt" + "slices" ) // ErrorCorrectionType Multiple-testing correction method applied when evaluating metric significance. @@ -41,12 +42,10 @@ func (v *ErrorCorrectionType) UnmarshalJSON(src []byte) error { } enumTypeValue := ErrorCorrectionType(value) - for _, existing := range AllowedErrorCorrectionTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedErrorCorrectionTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ErrorCorrectionType", value) @@ -54,13 +53,7 @@ func (v *ErrorCorrectionType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ErrorCorrectionType) IsValid() bool { - for _, existing := range AllowedErrorCorrectionTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedErrorCorrectionTypeEnumValues, v) } // Ptr returns reference to ErrorCorrectionType value. diff --git a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_metric_name.go b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_metric_name.go index bbf56c4651b..078bd9968c9 100644 --- a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_metric_name.go +++ b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_metric_name.go @@ -4,6 +4,7 @@ package abtestingV3 import ( "encoding/json" "fmt" + "slices" ) // MetricName the model 'MetricName'. @@ -67,12 +68,10 @@ func (v *MetricName) UnmarshalJSON(src []byte) error { } enumTypeValue := MetricName(value) - for _, existing := range AllowedMetricNameEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMetricNameEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid MetricName", value) @@ -80,13 +79,7 @@ func (v *MetricName) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v MetricName) IsValid() bool { - for _, existing := range AllowedMetricNameEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMetricNameEnumValues, v) } // Ptr returns reference to MetricName value. diff --git a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_status.go b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_status.go index 0f5860b6897..2f51e7bf4f3 100644 --- a/clients/algoliasearch-client-go/algolia/abtesting-v3/model_status.go +++ b/clients/algoliasearch-client-go/algolia/abtesting-v3/model_status.go @@ -4,6 +4,7 @@ package abtestingV3 import ( "encoding/json" "fmt" + "slices" ) // Status A/B test status. - `active`. The A/B test is live and search traffic is split between the two variants. - `stopped`. You stopped the A/B test. The A/B test data is still available for analysis. - `expired`. The A/B test was automatically stopped after reaching its end date. - `failed`. Creating the A/B test failed. @@ -45,12 +46,10 @@ func (v *Status) UnmarshalJSON(src []byte) error { } enumTypeValue := Status(value) - for _, existing := range AllowedStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Status", value) @@ -58,13 +57,7 @@ func (v *Status) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Status) IsValid() bool { - for _, existing := range AllowedStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedStatusEnumValues, v) } // Ptr returns reference to Status value. diff --git a/clients/algoliasearch-client-go/algolia/abtesting/model_effect_metric.go b/clients/algoliasearch-client-go/algolia/abtesting/model_effect_metric.go index a2d37f3479e..d324ac98111 100644 --- a/clients/algoliasearch-client-go/algolia/abtesting/model_effect_metric.go +++ b/clients/algoliasearch-client-go/algolia/abtesting/model_effect_metric.go @@ -4,6 +4,7 @@ package abtesting import ( "encoding/json" "fmt" + "slices" ) // EffectMetric Metric for which you want to detect the smallest relative difference. @@ -45,12 +46,10 @@ func (v *EffectMetric) UnmarshalJSON(src []byte) error { } enumTypeValue := EffectMetric(value) - for _, existing := range AllowedEffectMetricEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEffectMetricEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EffectMetric", value) @@ -58,13 +57,7 @@ func (v *EffectMetric) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EffectMetric) IsValid() bool { - for _, existing := range AllowedEffectMetricEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEffectMetricEnumValues, v) } // Ptr returns reference to EffectMetric value. diff --git a/clients/algoliasearch-client-go/algolia/abtesting/model_status.go b/clients/algoliasearch-client-go/algolia/abtesting/model_status.go index 81b21da36f2..b1bc0ce3441 100644 --- a/clients/algoliasearch-client-go/algolia/abtesting/model_status.go +++ b/clients/algoliasearch-client-go/algolia/abtesting/model_status.go @@ -4,6 +4,7 @@ package abtesting import ( "encoding/json" "fmt" + "slices" ) // Status A/B test status. - `active`. The A/B test is live and search traffic is split between the two variants. - `stopped`. You stopped the A/B test. The A/B test data is still available for analysis. - `expired`. The A/B test was automatically stopped after reaching its end date. - `failed`. Creating the A/B test failed. @@ -45,12 +46,10 @@ func (v *Status) UnmarshalJSON(src []byte) error { } enumTypeValue := Status(value) - for _, existing := range AllowedStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Status", value) @@ -58,13 +57,7 @@ func (v *Status) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Status) IsValid() bool { - for _, existing := range AllowedStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedStatusEnumValues, v) } // Ptr returns reference to Status value. diff --git a/clients/algoliasearch-client-go/algolia/analytics/model_direction.go b/clients/algoliasearch-client-go/algolia/analytics/model_direction.go index 1f708fd8709..2c334e2c425 100644 --- a/clients/algoliasearch-client-go/algolia/analytics/model_direction.go +++ b/clients/algoliasearch-client-go/algolia/analytics/model_direction.go @@ -4,6 +4,7 @@ package analytics import ( "encoding/json" "fmt" + "slices" ) // Direction the model 'Direction'. @@ -41,12 +42,10 @@ func (v *Direction) UnmarshalJSON(src []byte) error { } enumTypeValue := Direction(value) - for _, existing := range AllowedDirectionEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDirectionEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Direction", value) @@ -54,13 +53,7 @@ func (v *Direction) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Direction) IsValid() bool { - for _, existing := range AllowedDirectionEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDirectionEnumValues, v) } // Ptr returns reference to direction value. diff --git a/clients/algoliasearch-client-go/algolia/analytics/model_operator.go b/clients/algoliasearch-client-go/algolia/analytics/model_operator.go index f341fc45c78..b9a363bedf9 100644 --- a/clients/algoliasearch-client-go/algolia/analytics/model_operator.go +++ b/clients/algoliasearch-client-go/algolia/analytics/model_operator.go @@ -4,6 +4,7 @@ package analytics import ( "encoding/json" "fmt" + "slices" ) // Operator Character that characterizes how the filter is applied. For example, for a facet filter `facet:value`, `:` is the operator. For a numeric filter `count>50`, `>` is the operator. @@ -51,12 +52,10 @@ func (v *Operator) UnmarshalJSON(src []byte) error { } enumTypeValue := Operator(value) - for _, existing := range AllowedOperatorEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedOperatorEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Operator", value) @@ -64,13 +63,7 @@ func (v *Operator) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Operator) IsValid() bool { - for _, existing := range AllowedOperatorEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedOperatorEnumValues, v) } // Ptr returns reference to operator value. diff --git a/clients/algoliasearch-client-go/algolia/analytics/model_order_by.go b/clients/algoliasearch-client-go/algolia/analytics/model_order_by.go index 99e585d2f3b..eb3ca5ba067 100644 --- a/clients/algoliasearch-client-go/algolia/analytics/model_order_by.go +++ b/clients/algoliasearch-client-go/algolia/analytics/model_order_by.go @@ -4,6 +4,7 @@ package analytics import ( "encoding/json" "fmt" + "slices" ) // OrderBy Attribute by which to order the response items. If the `clickAnalytics` parameter is false, only `searchCount` is available. @@ -45,12 +46,10 @@ func (v *OrderBy) UnmarshalJSON(src []byte) error { } enumTypeValue := OrderBy(value) - for _, existing := range AllowedOrderByEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedOrderByEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid OrderBy", value) @@ -58,13 +57,7 @@ func (v *OrderBy) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v OrderBy) IsValid() bool { - for _, existing := range AllowedOrderByEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedOrderByEnumValues, v) } // Ptr returns reference to orderBy value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_action.go b/clients/algoliasearch-client-go/algolia/composition/model_action.go index 9e5e6a0ae2e..4d35fe9fba8 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_action.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_action.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // Action Type of Composition Batch operation. @@ -41,12 +42,10 @@ func (v *Action) UnmarshalJSON(src []byte) error { } enumTypeValue := Action(value) - for _, existing := range AllowedActionEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedActionEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Action", value) @@ -54,13 +53,7 @@ func (v *Action) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Action) IsValid() bool { - for _, existing := range AllowedActionEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedActionEnumValues, v) } // Ptr returns reference to action value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_advanced_syntax_features.go b/clients/algoliasearch-client-go/algolia/composition/model_advanced_syntax_features.go index 6c575acb526..efcde635daf 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_advanced_syntax_features.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_advanced_syntax_features.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // AdvancedSyntaxFeatures the model 'AdvancedSyntaxFeatures'. @@ -41,12 +42,10 @@ func (v *AdvancedSyntaxFeatures) UnmarshalJSON(src []byte) error { } enumTypeValue := AdvancedSyntaxFeatures(value) - for _, existing := range AllowedAdvancedSyntaxFeaturesEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAdvancedSyntaxFeaturesEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AdvancedSyntaxFeatures", value) @@ -54,13 +53,7 @@ func (v *AdvancedSyntaxFeatures) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AdvancedSyntaxFeatures) IsValid() bool { - for _, existing := range AllowedAdvancedSyntaxFeaturesEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAdvancedSyntaxFeaturesEnumValues, v) } // Ptr returns reference to advancedSyntaxFeatures value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_alternatives_as_exact.go b/clients/algoliasearch-client-go/algolia/composition/model_alternatives_as_exact.go index 7f95508bad3..e04aab49141 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_alternatives_as_exact.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_alternatives_as_exact.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // AlternativesAsExact the model 'AlternativesAsExact'. @@ -45,12 +46,10 @@ func (v *AlternativesAsExact) UnmarshalJSON(src []byte) error { } enumTypeValue := AlternativesAsExact(value) - for _, existing := range AllowedAlternativesAsExactEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAlternativesAsExactEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AlternativesAsExact", value) @@ -58,13 +57,7 @@ func (v *AlternativesAsExact) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AlternativesAsExact) IsValid() bool { - for _, existing := range AllowedAlternativesAsExactEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAlternativesAsExactEnumValues, v) } // Ptr returns reference to alternativesAsExact value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_anchoring.go b/clients/algoliasearch-client-go/algolia/composition/model_anchoring.go index e27c4c7ffd9..5a17897bbf3 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_anchoring.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_anchoring.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // Anchoring Which part of the search query the pattern should match: - `startsWith`. The pattern must match the beginning of the query. - `endsWith`. The pattern must match the end of the query. - `is`. The pattern must match the query exactly. - `contains`. The pattern must match anywhere in the query. Empty queries are only allowed as patterns with `anchoring: is`. @@ -45,12 +46,10 @@ func (v *Anchoring) UnmarshalJSON(src []byte) error { } enumTypeValue := Anchoring(value) - for _, existing := range AllowedAnchoringEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAnchoringEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Anchoring", value) @@ -58,13 +57,7 @@ func (v *Anchoring) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Anchoring) IsValid() bool { - for _, existing := range AllowedAnchoringEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAnchoringEnumValues, v) } // Ptr returns reference to anchoring value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_around_radius_all.go b/clients/algoliasearch-client-go/algolia/composition/model_around_radius_all.go index 999d37910c1..c309566cfa1 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_around_radius_all.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_around_radius_all.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // AroundRadiusAll Return all records with a valid `_geoloc` attribute. Don't filter by distance. @@ -39,12 +40,10 @@ func (v *AroundRadiusAll) UnmarshalJSON(src []byte) error { } enumTypeValue := AroundRadiusAll(value) - for _, existing := range AllowedAroundRadiusAllEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAroundRadiusAllEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AroundRadiusAll", value) @@ -52,13 +51,7 @@ func (v *AroundRadiusAll) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AroundRadiusAll) IsValid() bool { - for _, existing := range AllowedAroundRadiusAllEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAroundRadiusAllEnumValues, v) } // Ptr returns reference to aroundRadiusAll value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_boolean_string.go b/clients/algoliasearch-client-go/algolia/composition/model_boolean_string.go index 69237354e7e..92ce7a0b812 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_boolean_string.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_boolean_string.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // BooleanString the model 'BooleanString'. @@ -41,12 +42,10 @@ func (v *BooleanString) UnmarshalJSON(src []byte) error { } enumTypeValue := BooleanString(value) - for _, existing := range AllowedBooleanStringEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedBooleanStringEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid BooleanString", value) @@ -54,13 +53,7 @@ func (v *BooleanString) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v BooleanString) IsValid() bool { - for _, existing := range AllowedBooleanStringEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedBooleanStringEnumValues, v) } // Ptr returns reference to booleanString value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_dedup_positioning.go b/clients/algoliasearch-client-go/algolia/composition/model_dedup_positioning.go index 590470ec7cf..9ce710e874e 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_dedup_positioning.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_dedup_positioning.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // DedupPositioning Deduplication positioning configures how a duplicate result should be resolved between an injected item and main search results. Current configuration supports: - 'highest': always select the item in the highest position, and remove duplicates that appear lower in the results. - 'highestInjected': duplicate result will be moved to its highest possible injected position, but not higher. If a duplicate appears higher in main search results, it will be removed to stay it's intended group position (which could be lower than main). @@ -41,12 +42,10 @@ func (v *DedupPositioning) UnmarshalJSON(src []byte) error { } enumTypeValue := DedupPositioning(value) - for _, existing := range AllowedDedupPositioningEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDedupPositioningEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DedupPositioning", value) @@ -54,13 +53,7 @@ func (v *DedupPositioning) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DedupPositioning) IsValid() bool { - for _, existing := range AllowedDedupPositioningEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDedupPositioningEnumValues, v) } // Ptr returns reference to dedupPositioning value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_exact_on_single_word_query.go b/clients/algoliasearch-client-go/algolia/composition/model_exact_on_single_word_query.go index 9f1718530e0..9d6333914a4 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_exact_on_single_word_query.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_exact_on_single_word_query.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // ExactOnSingleWordQuery Determines how the [Exact ranking criterion](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/adjust-exact-settings/#turn-off-exact-for-some-attributes) is computed when the search query has only one word. - `attribute`. The Exact ranking criterion is 1 if the query word and attribute value are the same. For example, a search for \"road\" will match the value \"road\", but not \"road trip\". - `none`. The Exact ranking criterion is ignored on single-word searches. - `word`. The Exact ranking criterion is 1 if the query word is found in the attribute value. The query word must have at least 3 characters and must not be a stop word. Only exact matches will be highlighted, partial and prefix matches won't. @@ -43,12 +44,10 @@ func (v *ExactOnSingleWordQuery) UnmarshalJSON(src []byte) error { } enumTypeValue := ExactOnSingleWordQuery(value) - for _, existing := range AllowedExactOnSingleWordQueryEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedExactOnSingleWordQueryEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ExactOnSingleWordQuery", value) @@ -56,13 +55,7 @@ func (v *ExactOnSingleWordQuery) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ExactOnSingleWordQuery) IsValid() bool { - for _, existing := range AllowedExactOnSingleWordQueryEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedExactOnSingleWordQueryEnumValues, v) } // Ptr returns reference to exactOnSingleWordQuery value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_external_ordering.go b/clients/algoliasearch-client-go/algolia/composition/model_external_ordering.go index 5af1f3444c3..6c80e326ffe 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_external_ordering.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_external_ordering.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // ExternalOrdering Ordering to apply on the injected items coming from the external source. 'default' means the items will be ordered as they are in the index (natural relevance) in the smart group. 'userDefined' means the order in which the objectIDs are provided in the run request payload will be preserved in the smart group. @@ -41,12 +42,10 @@ func (v *ExternalOrdering) UnmarshalJSON(src []byte) error { } enumTypeValue := ExternalOrdering(value) - for _, existing := range AllowedExternalOrderingEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedExternalOrderingEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ExternalOrdering", value) @@ -54,13 +53,7 @@ func (v *ExternalOrdering) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ExternalOrdering) IsValid() bool { - for _, existing := range AllowedExternalOrderingEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedExternalOrderingEnumValues, v) } // Ptr returns reference to externalOrdering value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_match_level.go b/clients/algoliasearch-client-go/algolia/composition/model_match_level.go index 85fcd8fe8f2..50228216831 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_match_level.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_match_level.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // MatchLevel Whether the whole query string matches or only a part. @@ -43,12 +44,10 @@ func (v *MatchLevel) UnmarshalJSON(src []byte) error { } enumTypeValue := MatchLevel(value) - for _, existing := range AllowedMatchLevelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMatchLevelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid MatchLevel", value) @@ -56,13 +55,7 @@ func (v *MatchLevel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v MatchLevel) IsValid() bool { - for _, existing := range AllowedMatchLevelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMatchLevelEnumValues, v) } // Ptr returns reference to matchLevel value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_model.go b/clients/algoliasearch-client-go/algolia/composition/model_model.go index b131e78a732..4633efa59c1 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_model.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_model.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // Model Recommendation model to use for retrieving recommendations. @@ -39,12 +40,10 @@ func (v *Model) UnmarshalJSON(src []byte) error { } enumTypeValue := Model(value) - for _, existing := range AllowedModelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedModelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Model", value) @@ -52,13 +51,7 @@ func (v *Model) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Model) IsValid() bool { - for _, existing := range AllowedModelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedModelEnumValues, v) } // Ptr returns reference to model value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_query_type.go b/clients/algoliasearch-client-go/algolia/composition/model_query_type.go index 482365110c7..9f0032759cd 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_query_type.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_query_type.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // QueryType Determines if and how query words are interpreted as prefixes. By default, only the last query word is treated as a prefix (`prefixLast`). To turn off prefix search, use `prefixNone`. Avoid `prefixAll`, which treats all query words as prefixes. This might lead to counterintuitive results and makes your search slower. For more information, see [Prefix searching](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/prefix-searching). @@ -43,12 +44,10 @@ func (v *QueryType) UnmarshalJSON(src []byte) error { } enumTypeValue := QueryType(value) - for _, existing := range AllowedQueryTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedQueryTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid QueryType", value) @@ -56,13 +55,7 @@ func (v *QueryType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v QueryType) IsValid() bool { - for _, existing := range AllowedQueryTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedQueryTypeEnumValues, v) } // Ptr returns reference to queryType value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_remove_words_if_no_results.go b/clients/algoliasearch-client-go/algolia/composition/model_remove_words_if_no_results.go index e319c9e0b98..76003ee2c61 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_remove_words_if_no_results.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_remove_words_if_no_results.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // RemoveWordsIfNoResults Strategy for removing words from the query when it doesn't return any results. This helps to avoid returning empty search results. - `none`. No words are removed when a query doesn't return results. - `lastWords`. Treat the last (then second to last, then third to last) word as optional, until there are results or at most 5 words have been removed. - `firstWords`. Treat the first (then second, then third) word as optional, until there are results or at most 5 words have been removed. - `allOptional`. Treat all words as optional. For more information, see [Remove words to improve results](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/empty-or-insufficient-results/in-depth/why-use-remove-words-if-no-results). @@ -45,12 +46,10 @@ func (v *RemoveWordsIfNoResults) UnmarshalJSON(src []byte) error { } enumTypeValue := RemoveWordsIfNoResults(value) - for _, existing := range AllowedRemoveWordsIfNoResultsEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRemoveWordsIfNoResultsEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RemoveWordsIfNoResults", value) @@ -58,13 +57,7 @@ func (v *RemoveWordsIfNoResults) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RemoveWordsIfNoResults) IsValid() bool { - for _, existing := range AllowedRemoveWordsIfNoResultsEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRemoveWordsIfNoResultsEnumValues, v) } // Ptr returns reference to removeWordsIfNoResults value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_sort_remaining_by.go b/clients/algoliasearch-client-go/algolia/composition/model_sort_remaining_by.go index c5dedfd62d1..13dbb2ae10b 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_sort_remaining_by.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_sort_remaining_by.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // SortRemainingBy Order of facet values that aren't explicitly positioned with the `order` setting. - `count`. Order remaining facet values by decreasing count. The count is the number of matching records containing this facet value. - `alpha`. Sort facet values alphabetically. - `hidden`. Don't show facet values that aren't explicitly positioned. @@ -43,12 +44,10 @@ func (v *SortRemainingBy) UnmarshalJSON(src []byte) error { } enumTypeValue := SortRemainingBy(value) - for _, existing := range AllowedSortRemainingByEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSortRemainingByEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SortRemainingBy", value) @@ -56,13 +55,7 @@ func (v *SortRemainingBy) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SortRemainingBy) IsValid() bool { - for _, existing := range AllowedSortRemainingByEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSortRemainingByEnumValues, v) } // Ptr returns reference to sortRemainingBy value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_supported_language.go b/clients/algoliasearch-client-go/algolia/composition/model_supported_language.go index d4f6c155b5c..bcf6fae0a22 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_supported_language.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_supported_language.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // SupportedLanguage ISO code for a supported language. @@ -173,12 +174,10 @@ func (v *SupportedLanguage) UnmarshalJSON(src []byte) error { } enumTypeValue := SupportedLanguage(value) - for _, existing := range AllowedSupportedLanguageEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSupportedLanguageEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SupportedLanguage", value) @@ -186,13 +185,7 @@ func (v *SupportedLanguage) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SupportedLanguage) IsValid() bool { - for _, existing := range AllowedSupportedLanguageEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSupportedLanguageEnumValues, v) } // Ptr returns reference to supportedLanguage value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_task_status.go b/clients/algoliasearch-client-go/algolia/composition/model_task_status.go index 8c31d3c263d..c22cc7d5fbc 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_task_status.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_task_status.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // TaskStatus Task status, `published` if the task is completed, `notPublished` otherwise. @@ -41,12 +42,10 @@ func (v *TaskStatus) UnmarshalJSON(src []byte) error { } enumTypeValue := TaskStatus(value) - for _, existing := range AllowedTaskStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTaskStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TaskStatus", value) @@ -54,13 +53,7 @@ func (v *TaskStatus) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TaskStatus) IsValid() bool { - for _, existing := range AllowedTaskStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTaskStatusEnumValues, v) } // Ptr returns reference to taskStatus value. diff --git a/clients/algoliasearch-client-go/algolia/composition/model_typo_tolerance_enum.go b/clients/algoliasearch-client-go/algolia/composition/model_typo_tolerance_enum.go index b7c9268fda7..b2a7c071e86 100644 --- a/clients/algoliasearch-client-go/algolia/composition/model_typo_tolerance_enum.go +++ b/clients/algoliasearch-client-go/algolia/composition/model_typo_tolerance_enum.go @@ -4,6 +4,7 @@ package composition import ( "encoding/json" "fmt" + "slices" ) // TypoToleranceEnum - `min`. Return matches with the lowest number of typos. For example, if you have matches without typos, only include those. But if there are no matches without typos (with 1 typo), include matches with 1 typo (2 typos). - `strict`. Return matches with the two lowest numbers of typos. With `strict`, the Typo ranking criterion is applied first in the `ranking` setting. @@ -45,12 +46,10 @@ func (v *TypoToleranceEnum) UnmarshalJSON(src []byte) error { } enumTypeValue := TypoToleranceEnum(value) - for _, existing := range AllowedTypoToleranceEnumEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTypoToleranceEnumEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TypoToleranceEnum", value) @@ -58,13 +57,7 @@ func (v *TypoToleranceEnum) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TypoToleranceEnum) IsValid() bool { - for _, existing := range AllowedTypoToleranceEnumEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTypoToleranceEnumEnumValues, v) } // Ptr returns reference to typoToleranceEnum value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_action.go b/clients/algoliasearch-client-go/algolia/ingestion/model_action.go index 629b50242f6..e2ba9b66d5a 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_action.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_action.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // Action Which indexing operation to perform: - `addObject`: adds records to an index. Equivalent to the \"Add a new record (with auto-generated object ID)\" operation. - `updateObject`: adds or replaces records in an index. Equivalent to the \"Add or replace a record\" operation. - `partialUpdateObject`: adds or updates attributes within records. Equivalent to the \"Add or update attributes\" operation with the `createIfNoExists` parameter set to true. (If a record with the specified `objectID` doesn't exist in the specified index, this action creates adds the record to the index) - `partialUpdateObjectNoCreate`: same as `partialUpdateObject`, but with `createIfNoExists` set to false. (A record isn't added to the index if its `objectID` doesn't exist) - `deleteObject`: delete records from an index. Equivalent to the \"Delete a record\" operation. - `delete`. Delete an index. Equivalent to the \"Delete an index\" operation. - `clear`: delete all records from an index. Equivalent to the \"Delete all records from an index operation\". @@ -51,12 +52,10 @@ func (v *Action) UnmarshalJSON(src []byte) error { } enumTypeValue := Action(value) - for _, existing := range AllowedActionEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedActionEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Action", value) @@ -64,13 +63,7 @@ func (v *Action) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Action) IsValid() bool { - for _, existing := range AllowedActionEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedActionEnumValues, v) } // Ptr returns reference to action value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_action_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_action_type.go index 1446709b7b7..9b7725a8616 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_action_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_action_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // ActionType Action to perform on the Algolia index. @@ -47,12 +48,10 @@ func (v *ActionType) UnmarshalJSON(src []byte) error { } enumTypeValue := ActionType(value) - for _, existing := range AllowedActionTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedActionTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ActionType", value) @@ -60,13 +59,7 @@ func (v *ActionType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ActionType) IsValid() bool { - for _, existing := range AllowedActionTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedActionTypeEnumValues, v) } // Ptr returns reference to ActionType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_sort_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_sort_keys.go index dd238b18d54..067b8dd6660 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_sort_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_sort_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // AuthenticationSortKeys Property by which to sort the list of authentications. @@ -47,12 +48,10 @@ func (v *AuthenticationSortKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := AuthenticationSortKeys(value) - for _, existing := range AllowedAuthenticationSortKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAuthenticationSortKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AuthenticationSortKeys", value) @@ -60,13 +59,7 @@ func (v *AuthenticationSortKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AuthenticationSortKeys) IsValid() bool { - for _, existing := range AllowedAuthenticationSortKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAuthenticationSortKeysEnumValues, v) } // Ptr returns reference to authenticationSortKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_type.go index c88598005c0..396b93542fa 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_authentication_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // AuthenticationType Type of authentication. This determines the type of credentials required in the `input` object. @@ -51,12 +52,10 @@ func (v *AuthenticationType) UnmarshalJSON(src []byte) error { } enumTypeValue := AuthenticationType(value) - for _, existing := range AllowedAuthenticationTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAuthenticationTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AuthenticationType", value) @@ -64,13 +63,7 @@ func (v *AuthenticationType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AuthenticationType) IsValid() bool { - for _, existing := range AllowedAuthenticationTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAuthenticationTypeEnumValues, v) } // Ptr returns reference to AuthenticationType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_big_query_data_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_big_query_data_type.go index fdf78588ecd..20ec6b3e88f 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_big_query_data_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_big_query_data_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // BigQueryDataType the model 'BigQueryDataType'. @@ -41,12 +42,10 @@ func (v *BigQueryDataType) UnmarshalJSON(src []byte) error { } enumTypeValue := BigQueryDataType(value) - for _, existing := range AllowedBigQueryDataTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedBigQueryDataTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid BigQueryDataType", value) @@ -54,13 +53,7 @@ func (v *BigQueryDataType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v BigQueryDataType) IsValid() bool { - for _, existing := range AllowedBigQueryDataTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedBigQueryDataTypeEnumValues, v) } // Ptr returns reference to BigQueryDataType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_destination_sort_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_destination_sort_keys.go index 43b80a0231f..ed908ddfcc9 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_destination_sort_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_destination_sort_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // DestinationSortKeys Property by which to sort the destinations. @@ -45,12 +46,10 @@ func (v *DestinationSortKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := DestinationSortKeys(value) - for _, existing := range AllowedDestinationSortKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDestinationSortKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DestinationSortKeys", value) @@ -58,13 +57,7 @@ func (v *DestinationSortKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DestinationSortKeys) IsValid() bool { - for _, existing := range AllowedDestinationSortKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDestinationSortKeysEnumValues, v) } // Ptr returns reference to destinationSortKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_destination_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_destination_type.go index f29de419ed8..baf3fd3c33e 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_destination_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_destination_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // DestinationType Destination type. - `search`. Data is stored in an Algolia index. - `insights`. Data is recorded as user events in the Insights API. @@ -41,12 +42,10 @@ func (v *DestinationType) UnmarshalJSON(src []byte) error { } enumTypeValue := DestinationType(value) - for _, existing := range AllowedDestinationTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDestinationTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DestinationType", value) @@ -54,13 +53,7 @@ func (v *DestinationType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DestinationType) IsValid() bool { - for _, existing := range AllowedDestinationTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDestinationTypeEnumValues, v) } // Ptr returns reference to DestinationType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_docker_streams_sync_mode.go b/clients/algoliasearch-client-go/algolia/ingestion/model_docker_streams_sync_mode.go index 36bc3fb30c9..1a256323f33 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_docker_streams_sync_mode.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_docker_streams_sync_mode.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // DockerStreamsSyncMode The strategy to use to fetch the data. @@ -41,12 +42,10 @@ func (v *DockerStreamsSyncMode) UnmarshalJSON(src []byte) error { } enumTypeValue := DockerStreamsSyncMode(value) - for _, existing := range AllowedDockerStreamsSyncModeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDockerStreamsSyncModeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DockerStreamsSyncMode", value) @@ -54,13 +53,7 @@ func (v *DockerStreamsSyncMode) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DockerStreamsSyncMode) IsValid() bool { - for _, existing := range AllowedDockerStreamsSyncModeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDockerStreamsSyncModeEnumValues, v) } // Ptr returns reference to DockerStreamsSyncMode value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_entity_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_entity_type.go index 7bb7b984f7b..907e5c1ce1d 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_entity_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_entity_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // EntityType Type of entity to update. @@ -41,12 +42,10 @@ func (v *EntityType) UnmarshalJSON(src []byte) error { } enumTypeValue := EntityType(value) - for _, existing := range AllowedEntityTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEntityTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EntityType", value) @@ -54,13 +53,7 @@ func (v *EntityType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EntityType) IsValid() bool { - for _, existing := range AllowedEntityTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEntityTypeEnumValues, v) } // Ptr returns reference to EntityType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_event_sort_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_event_sort_keys.go index fbe127a4e8b..574f3cdf7f2 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_event_sort_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_event_sort_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // EventSortKeys Property by which to sort the list of task run events. @@ -43,12 +44,10 @@ func (v *EventSortKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := EventSortKeys(value) - for _, existing := range AllowedEventSortKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEventSortKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EventSortKeys", value) @@ -56,13 +55,7 @@ func (v *EventSortKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EventSortKeys) IsValid() bool { - for _, existing := range AllowedEventSortKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEventSortKeysEnumValues, v) } // Ptr returns reference to eventSortKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_event_status.go b/clients/algoliasearch-client-go/algolia/ingestion/model_event_status.go index cd35394e164..fada95fea33 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_event_status.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_event_status.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // EventStatus the model 'EventStatus'. @@ -49,12 +50,10 @@ func (v *EventStatus) UnmarshalJSON(src []byte) error { } enumTypeValue := EventStatus(value) - for _, existing := range AllowedEventStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEventStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EventStatus", value) @@ -62,13 +61,7 @@ func (v *EventStatus) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EventStatus) IsValid() bool { - for _, existing := range AllowedEventStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEventStatusEnumValues, v) } // Ptr returns reference to EventStatus value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_event_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_event_type.go index 36935dad756..8e3eb6e858e 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_event_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_event_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // EventType the model 'EventType'. @@ -45,12 +46,10 @@ func (v *EventType) UnmarshalJSON(src []byte) error { } enumTypeValue := EventType(value) - for _, existing := range AllowedEventTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEventTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EventType", value) @@ -58,13 +57,7 @@ func (v *EventType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EventType) IsValid() bool { - for _, existing := range AllowedEventTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEventTypeEnumValues, v) } // Ptr returns reference to EventType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_format_schema.go b/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_format_schema.go index 81f9ef628bb..1309319da98 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_format_schema.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_format_schema.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // MappingFormatSchema Mapping format schema. @@ -39,12 +40,10 @@ func (v *MappingFormatSchema) UnmarshalJSON(src []byte) error { } enumTypeValue := MappingFormatSchema(value) - for _, existing := range AllowedMappingFormatSchemaEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMappingFormatSchemaEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid MappingFormatSchema", value) @@ -52,13 +51,7 @@ func (v *MappingFormatSchema) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v MappingFormatSchema) IsValid() bool { - for _, existing := range AllowedMappingFormatSchemaEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMappingFormatSchemaEnumValues, v) } // Ptr returns reference to MappingFormatSchema value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_type_csv.go b/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_type_csv.go index bf7799b6a3c..194f81aa211 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_type_csv.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_mapping_type_csv.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // MappingTypeCSV the model 'MappingTypeCSV'. @@ -47,12 +48,10 @@ func (v *MappingTypeCSV) UnmarshalJSON(src []byte) error { } enumTypeValue := MappingTypeCSV(value) - for _, existing := range AllowedMappingTypeCSVEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMappingTypeCSVEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid MappingTypeCSV", value) @@ -60,13 +59,7 @@ func (v *MappingTypeCSV) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v MappingTypeCSV) IsValid() bool { - for _, existing := range AllowedMappingTypeCSVEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMappingTypeCSVEnumValues, v) } // Ptr returns reference to MappingTypeCSV value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_method_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_method_type.go index 16bd666e3c9..d99ab0ca808 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_method_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_method_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // MethodType HTTP method to be used for retrieving your data. @@ -41,12 +42,10 @@ func (v *MethodType) UnmarshalJSON(src []byte) error { } enumTypeValue := MethodType(value) - for _, existing := range AllowedMethodTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMethodTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid MethodType", value) @@ -54,13 +53,7 @@ func (v *MethodType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v MethodType) IsValid() bool { - for _, existing := range AllowedMethodTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMethodTypeEnumValues, v) } // Ptr returns reference to MethodType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_on_demand_trigger_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_on_demand_trigger_type.go index 9165952782d..bd7677acc3d 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_on_demand_trigger_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_on_demand_trigger_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // OnDemandTriggerType Task is run manually, with the `/run` endpoint. @@ -39,12 +40,10 @@ func (v *OnDemandTriggerType) UnmarshalJSON(src []byte) error { } enumTypeValue := OnDemandTriggerType(value) - for _, existing := range AllowedOnDemandTriggerTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedOnDemandTriggerTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid OnDemandTriggerType", value) @@ -52,13 +51,7 @@ func (v *OnDemandTriggerType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v OnDemandTriggerType) IsValid() bool { - for _, existing := range AllowedOnDemandTriggerTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedOnDemandTriggerTypeEnumValues, v) } // Ptr returns reference to OnDemandTriggerType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_order_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_order_keys.go index d166a095fd7..5a7f167a269 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_order_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_order_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // OrderKeys Ascending or descending sort order. @@ -41,12 +42,10 @@ func (v *OrderKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := OrderKeys(value) - for _, existing := range AllowedOrderKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedOrderKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid OrderKeys", value) @@ -54,13 +53,7 @@ func (v *OrderKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v OrderKeys) IsValid() bool { - for _, existing := range AllowedOrderKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedOrderKeysEnumValues, v) } // Ptr returns reference to orderKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_platform.go b/clients/algoliasearch-client-go/algolia/ingestion/model_platform.go index 96b4b89c348..03acbed16ce 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_platform.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_platform.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // Platform Name of an ecommerce platform with which to authenticate. This determines which authentication type you can select. @@ -43,12 +44,10 @@ func (v *Platform) UnmarshalJSON(src []byte) error { } enumTypeValue := Platform(value) - for _, existing := range AllowedPlatformEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedPlatformEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Platform", value) @@ -56,13 +55,7 @@ func (v *Platform) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Platform) IsValid() bool { - for _, existing := range AllowedPlatformEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedPlatformEnumValues, v) } // Ptr returns reference to Platform value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_platform_none.go b/clients/algoliasearch-client-go/algolia/ingestion/model_platform_none.go index f00f5833731..ef8de76dfee 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_platform_none.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_platform_none.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // PlatformNone Authentication resource not tied to any ecommerce platform, used for filtering. @@ -39,12 +40,10 @@ func (v *PlatformNone) UnmarshalJSON(src []byte) error { } enumTypeValue := PlatformNone(value) - for _, existing := range AllowedPlatformNoneEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedPlatformNoneEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid PlatformNone", value) @@ -52,13 +51,7 @@ func (v *PlatformNone) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v PlatformNone) IsValid() bool { - for _, existing := range AllowedPlatformNoneEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedPlatformNoneEnumValues, v) } // Ptr returns reference to platformNone value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_record_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_record_type.go index a826f1d61c9..cab032facdb 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_record_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_record_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // RecordType Record type for ecommerce sources. @@ -43,12 +44,10 @@ func (v *RecordType) UnmarshalJSON(src []byte) error { } enumTypeValue := RecordType(value) - for _, existing := range AllowedRecordTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRecordTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RecordType", value) @@ -56,13 +55,7 @@ func (v *RecordType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RecordType) IsValid() bool { - for _, existing := range AllowedRecordTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRecordTypeEnumValues, v) } // Ptr returns reference to RecordType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_run_outcome.go b/clients/algoliasearch-client-go/algolia/ingestion/model_run_outcome.go index f8521e62d27..2d2f11c3ddb 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_run_outcome.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_run_outcome.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // RunOutcome Task run outcome. @@ -41,12 +42,10 @@ func (v *RunOutcome) UnmarshalJSON(src []byte) error { } enumTypeValue := RunOutcome(value) - for _, existing := range AllowedRunOutcomeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRunOutcomeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RunOutcome", value) @@ -54,13 +53,7 @@ func (v *RunOutcome) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RunOutcome) IsValid() bool { - for _, existing := range AllowedRunOutcomeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRunOutcomeEnumValues, v) } // Ptr returns reference to RunOutcome value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_run_reason_code.go b/clients/algoliasearch-client-go/algolia/ingestion/model_run_reason_code.go index 0e91efe2c81..14b15280082 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_run_reason_code.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_run_reason_code.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // RunReasonCode A code for the task run's outcome. A readable description of the code is included in the `reason` response property. @@ -55,12 +56,10 @@ func (v *RunReasonCode) UnmarshalJSON(src []byte) error { } enumTypeValue := RunReasonCode(value) - for _, existing := range AllowedRunReasonCodeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRunReasonCodeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RunReasonCode", value) @@ -68,13 +67,7 @@ func (v *RunReasonCode) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RunReasonCode) IsValid() bool { - for _, existing := range AllowedRunReasonCodeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRunReasonCodeEnumValues, v) } // Ptr returns reference to RunReasonCode value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_run_sort_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_run_sort_keys.go index 4b71e72c63e..506b460f2b1 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_run_sort_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_run_sort_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // RunSortKeys Property by which to sort the list of task runs. @@ -43,12 +44,10 @@ func (v *RunSortKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := RunSortKeys(value) - for _, existing := range AllowedRunSortKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRunSortKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RunSortKeys", value) @@ -56,13 +55,7 @@ func (v *RunSortKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RunSortKeys) IsValid() bool { - for _, existing := range AllowedRunSortKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRunSortKeysEnumValues, v) } // Ptr returns reference to runSortKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_run_status.go b/clients/algoliasearch-client-go/algolia/ingestion/model_run_status.go index cfa48f95252..d261e11a4a6 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_run_status.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_run_status.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // RunStatus Task run status. @@ -45,12 +46,10 @@ func (v *RunStatus) UnmarshalJSON(src []byte) error { } enumTypeValue := RunStatus(value) - for _, existing := range AllowedRunStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRunStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RunStatus", value) @@ -58,13 +57,7 @@ func (v *RunStatus) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RunStatus) IsValid() bool { - for _, existing := range AllowedRunStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRunStatusEnumValues, v) } // Ptr returns reference to RunStatus value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_run_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_run_type.go index 62c1749fe5e..ca4371bf548 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_run_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_run_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // RunType Task run type. @@ -47,12 +48,10 @@ func (v *RunType) UnmarshalJSON(src []byte) error { } enumTypeValue := RunType(value) - for _, existing := range AllowedRunTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRunTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RunType", value) @@ -60,13 +59,7 @@ func (v *RunType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RunType) IsValid() bool { - for _, existing := range AllowedRunTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRunTypeEnumValues, v) } // Ptr returns reference to RunType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_schedule_trigger_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_schedule_trigger_type.go index 6480d121b0a..3ec9d1aa567 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_schedule_trigger_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_schedule_trigger_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // ScheduleTriggerType Task runs on a schedule. @@ -39,12 +40,10 @@ func (v *ScheduleTriggerType) UnmarshalJSON(src []byte) error { } enumTypeValue := ScheduleTriggerType(value) - for _, existing := range AllowedScheduleTriggerTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedScheduleTriggerTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ScheduleTriggerType", value) @@ -52,13 +51,7 @@ func (v *ScheduleTriggerType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ScheduleTriggerType) IsValid() bool { - for _, existing := range AllowedScheduleTriggerTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedScheduleTriggerTypeEnumValues, v) } // Ptr returns reference to ScheduleTriggerType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_source_sort_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_source_sort_keys.go index 4da07892136..8d9da549c04 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_source_sort_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_source_sort_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // SourceSortKeys Property by which to sort the list of sources. @@ -45,12 +46,10 @@ func (v *SourceSortKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := SourceSortKeys(value) - for _, existing := range AllowedSourceSortKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSourceSortKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SourceSortKeys", value) @@ -58,13 +57,7 @@ func (v *SourceSortKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SourceSortKeys) IsValid() bool { - for _, existing := range AllowedSourceSortKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSourceSortKeysEnumValues, v) } // Ptr returns reference to sourceSortKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_source_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_source_type.go index e523ba9daa9..ded8b99b715 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_source_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_source_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // SourceType the model 'SourceType'. @@ -57,12 +58,10 @@ func (v *SourceType) UnmarshalJSON(src []byte) error { } enumTypeValue := SourceType(value) - for _, existing := range AllowedSourceTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSourceTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SourceType", value) @@ -70,13 +69,7 @@ func (v *SourceType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SourceType) IsValid() bool { - for _, existing := range AllowedSourceTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSourceTypeEnumValues, v) } // Ptr returns reference to SourceType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_streaming_trigger_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_streaming_trigger_type.go index 76d7edfdfec..c21b76226eb 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_streaming_trigger_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_streaming_trigger_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // StreamingTriggerType Task runs continuously. @@ -39,12 +40,10 @@ func (v *StreamingTriggerType) UnmarshalJSON(src []byte) error { } enumTypeValue := StreamingTriggerType(value) - for _, existing := range AllowedStreamingTriggerTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedStreamingTriggerTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid StreamingTriggerType", value) @@ -52,13 +51,7 @@ func (v *StreamingTriggerType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v StreamingTriggerType) IsValid() bool { - for _, existing := range AllowedStreamingTriggerTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedStreamingTriggerTypeEnumValues, v) } // Ptr returns reference to StreamingTriggerType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_subscription_trigger_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_subscription_trigger_type.go index 9064d57275a..9a067bc6161 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_subscription_trigger_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_subscription_trigger_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // SubscriptionTriggerType Task runs after receiving subscribed event. @@ -39,12 +40,10 @@ func (v *SubscriptionTriggerType) UnmarshalJSON(src []byte) error { } enumTypeValue := SubscriptionTriggerType(value) - for _, existing := range AllowedSubscriptionTriggerTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSubscriptionTriggerTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SubscriptionTriggerType", value) @@ -52,13 +51,7 @@ func (v *SubscriptionTriggerType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SubscriptionTriggerType) IsValid() bool { - for _, existing := range AllowedSubscriptionTriggerTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSubscriptionTriggerTypeEnumValues, v) } // Ptr returns reference to SubscriptionTriggerType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_task_sort_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_task_sort_keys.go index c3a97040281..ab3c1d8fc94 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_task_sort_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_task_sort_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // TaskSortKeys Property by which to sort the list of tasks. @@ -47,12 +48,10 @@ func (v *TaskSortKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := TaskSortKeys(value) - for _, existing := range AllowedTaskSortKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTaskSortKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TaskSortKeys", value) @@ -60,13 +59,7 @@ func (v *TaskSortKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TaskSortKeys) IsValid() bool { - for _, existing := range AllowedTaskSortKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTaskSortKeysEnumValues, v) } // Ptr returns reference to taskSortKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_sort_keys.go b/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_sort_keys.go index 652ff073373..086268854a3 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_sort_keys.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_sort_keys.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // TransformationSortKeys Property by which to sort the list of transformations. @@ -43,12 +44,10 @@ func (v *TransformationSortKeys) UnmarshalJSON(src []byte) error { } enumTypeValue := TransformationSortKeys(value) - for _, existing := range AllowedTransformationSortKeysEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTransformationSortKeysEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TransformationSortKeys", value) @@ -56,13 +55,7 @@ func (v *TransformationSortKeys) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TransformationSortKeys) IsValid() bool { - for _, existing := range AllowedTransformationSortKeysEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTransformationSortKeysEnumValues, v) } // Ptr returns reference to transformationSortKeys value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_type.go index 96ab5f32ff8..2f75c4866b7 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_transformation_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // TransformationType The type of transformation, which can be either 'code' or 'noCode'. @@ -41,12 +42,10 @@ func (v *TransformationType) UnmarshalJSON(src []byte) error { } enumTypeValue := TransformationType(value) - for _, existing := range AllowedTransformationTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTransformationTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TransformationType", value) @@ -54,13 +53,7 @@ func (v *TransformationType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TransformationType) IsValid() bool { - for _, existing := range AllowedTransformationTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTransformationTypeEnumValues, v) } // Ptr returns reference to TransformationType value. diff --git a/clients/algoliasearch-client-go/algolia/ingestion/model_trigger_type.go b/clients/algoliasearch-client-go/algolia/ingestion/model_trigger_type.go index b01b1e458c0..76c7af548a2 100644 --- a/clients/algoliasearch-client-go/algolia/ingestion/model_trigger_type.go +++ b/clients/algoliasearch-client-go/algolia/ingestion/model_trigger_type.go @@ -4,6 +4,7 @@ package ingestion import ( "encoding/json" "fmt" + "slices" ) // TriggerType Task trigger, describing when a task should run. - `onDemand`. Manually trigger the task with the `/run` endpoint. - `schedule`. Regularly trigger the task on a `cron` schedule. - `subscription`. Trigger the task after an event is received, such as, a webhook. - `streaming`. Run the task continuously. @@ -45,12 +46,10 @@ func (v *TriggerType) UnmarshalJSON(src []byte) error { } enumTypeValue := TriggerType(value) - for _, existing := range AllowedTriggerTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTriggerTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TriggerType", value) @@ -58,13 +57,7 @@ func (v *TriggerType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TriggerType) IsValid() bool { - for _, existing := range AllowedTriggerTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTriggerTypeEnumValues, v) } // Ptr returns reference to TriggerType value. diff --git a/clients/algoliasearch-client-go/algolia/insights/model_add_to_cart_event.go b/clients/algoliasearch-client-go/algolia/insights/model_add_to_cart_event.go index 1bbde46bc01..9a600a4992c 100644 --- a/clients/algoliasearch-client-go/algolia/insights/model_add_to_cart_event.go +++ b/clients/algoliasearch-client-go/algolia/insights/model_add_to_cart_event.go @@ -4,6 +4,7 @@ package insights import ( "encoding/json" "fmt" + "slices" ) // AddToCartEvent the model 'AddToCartEvent'. @@ -39,12 +40,10 @@ func (v *AddToCartEvent) UnmarshalJSON(src []byte) error { } enumTypeValue := AddToCartEvent(value) - for _, existing := range AllowedAddToCartEventEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAddToCartEventEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AddToCartEvent", value) @@ -52,13 +51,7 @@ func (v *AddToCartEvent) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AddToCartEvent) IsValid() bool { - for _, existing := range AllowedAddToCartEventEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAddToCartEventEnumValues, v) } // Ptr returns reference to AddToCartEvent value. diff --git a/clients/algoliasearch-client-go/algolia/insights/model_click_event.go b/clients/algoliasearch-client-go/algolia/insights/model_click_event.go index 55bd98e05cf..ef22ae13bdb 100644 --- a/clients/algoliasearch-client-go/algolia/insights/model_click_event.go +++ b/clients/algoliasearch-client-go/algolia/insights/model_click_event.go @@ -4,6 +4,7 @@ package insights import ( "encoding/json" "fmt" + "slices" ) // ClickEvent the model 'ClickEvent'. @@ -39,12 +40,10 @@ func (v *ClickEvent) UnmarshalJSON(src []byte) error { } enumTypeValue := ClickEvent(value) - for _, existing := range AllowedClickEventEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedClickEventEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ClickEvent", value) @@ -52,13 +51,7 @@ func (v *ClickEvent) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ClickEvent) IsValid() bool { - for _, existing := range AllowedClickEventEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedClickEventEnumValues, v) } // Ptr returns reference to ClickEvent value. diff --git a/clients/algoliasearch-client-go/algolia/insights/model_conversion_event.go b/clients/algoliasearch-client-go/algolia/insights/model_conversion_event.go index aedb5067c5d..02e27d00811 100644 --- a/clients/algoliasearch-client-go/algolia/insights/model_conversion_event.go +++ b/clients/algoliasearch-client-go/algolia/insights/model_conversion_event.go @@ -4,6 +4,7 @@ package insights import ( "encoding/json" "fmt" + "slices" ) // ConversionEvent the model 'ConversionEvent'. @@ -39,12 +40,10 @@ func (v *ConversionEvent) UnmarshalJSON(src []byte) error { } enumTypeValue := ConversionEvent(value) - for _, existing := range AllowedConversionEventEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedConversionEventEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ConversionEvent", value) @@ -52,13 +51,7 @@ func (v *ConversionEvent) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ConversionEvent) IsValid() bool { - for _, existing := range AllowedConversionEventEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedConversionEventEnumValues, v) } // Ptr returns reference to ConversionEvent value. diff --git a/clients/algoliasearch-client-go/algolia/insights/model_purchase_event.go b/clients/algoliasearch-client-go/algolia/insights/model_purchase_event.go index 0d775360f7c..abdcf05de44 100644 --- a/clients/algoliasearch-client-go/algolia/insights/model_purchase_event.go +++ b/clients/algoliasearch-client-go/algolia/insights/model_purchase_event.go @@ -4,6 +4,7 @@ package insights import ( "encoding/json" "fmt" + "slices" ) // PurchaseEvent the model 'PurchaseEvent'. @@ -39,12 +40,10 @@ func (v *PurchaseEvent) UnmarshalJSON(src []byte) error { } enumTypeValue := PurchaseEvent(value) - for _, existing := range AllowedPurchaseEventEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedPurchaseEventEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid PurchaseEvent", value) @@ -52,13 +51,7 @@ func (v *PurchaseEvent) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v PurchaseEvent) IsValid() bool { - for _, existing := range AllowedPurchaseEventEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedPurchaseEventEnumValues, v) } // Ptr returns reference to PurchaseEvent value. diff --git a/clients/algoliasearch-client-go/algolia/insights/model_view_event.go b/clients/algoliasearch-client-go/algolia/insights/model_view_event.go index 792e2c33f64..63c4113c8a1 100644 --- a/clients/algoliasearch-client-go/algolia/insights/model_view_event.go +++ b/clients/algoliasearch-client-go/algolia/insights/model_view_event.go @@ -4,6 +4,7 @@ package insights import ( "encoding/json" "fmt" + "slices" ) // ViewEvent the model 'ViewEvent'. @@ -39,12 +40,10 @@ func (v *ViewEvent) UnmarshalJSON(src []byte) error { } enumTypeValue := ViewEvent(value) - for _, existing := range AllowedViewEventEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedViewEventEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ViewEvent", value) @@ -52,13 +51,7 @@ func (v *ViewEvent) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ViewEvent) IsValid() bool { - for _, existing := range AllowedViewEventEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedViewEventEnumValues, v) } // Ptr returns reference to ViewEvent value. diff --git a/clients/algoliasearch-client-go/algolia/monitoring/model_metric.go b/clients/algoliasearch-client-go/algolia/monitoring/model_metric.go index ae4107d4c4b..12092a18f72 100644 --- a/clients/algoliasearch-client-go/algolia/monitoring/model_metric.go +++ b/clients/algoliasearch-client-go/algolia/monitoring/model_metric.go @@ -4,6 +4,7 @@ package monitoring import ( "encoding/json" "fmt" + "slices" ) // Metric the model 'Metric'. @@ -49,12 +50,10 @@ func (v *Metric) UnmarshalJSON(src []byte) error { } enumTypeValue := Metric(value) - for _, existing := range AllowedMetricEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMetricEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Metric", value) @@ -62,13 +61,7 @@ func (v *Metric) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Metric) IsValid() bool { - for _, existing := range AllowedMetricEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMetricEnumValues, v) } // Ptr returns reference to Metric value. diff --git a/clients/algoliasearch-client-go/algolia/monitoring/model_period.go b/clients/algoliasearch-client-go/algolia/monitoring/model_period.go index d741cf258c8..71a6f46bd46 100644 --- a/clients/algoliasearch-client-go/algolia/monitoring/model_period.go +++ b/clients/algoliasearch-client-go/algolia/monitoring/model_period.go @@ -4,6 +4,7 @@ package monitoring import ( "encoding/json" "fmt" + "slices" ) // Period the model 'Period'. @@ -47,12 +48,10 @@ func (v *Period) UnmarshalJSON(src []byte) error { } enumTypeValue := Period(value) - for _, existing := range AllowedPeriodEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedPeriodEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Period", value) @@ -60,13 +59,7 @@ func (v *Period) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Period) IsValid() bool { - for _, existing := range AllowedPeriodEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedPeriodEnumValues, v) } // Ptr returns reference to Period value. diff --git a/clients/algoliasearch-client-go/algolia/monitoring/model_region.go b/clients/algoliasearch-client-go/algolia/monitoring/model_region.go index dc91a5f2070..7d261f2ed71 100644 --- a/clients/algoliasearch-client-go/algolia/monitoring/model_region.go +++ b/clients/algoliasearch-client-go/algolia/monitoring/model_region.go @@ -4,6 +4,7 @@ package monitoring import ( "encoding/json" "fmt" + "slices" ) // Region Region where the cluster is located. @@ -67,12 +68,10 @@ func (v *Region) UnmarshalJSON(src []byte) error { } enumTypeValue := Region(value) - for _, existing := range AllowedRegionEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRegionEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Region", value) @@ -80,13 +79,7 @@ func (v *Region) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Region) IsValid() bool { - for _, existing := range AllowedRegionEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRegionEnumValues, v) } // Ptr returns reference to Region value. diff --git a/clients/algoliasearch-client-go/algolia/monitoring/model_server_status.go b/clients/algoliasearch-client-go/algolia/monitoring/model_server_status.go index e1d3355786f..9e2248ca535 100644 --- a/clients/algoliasearch-client-go/algolia/monitoring/model_server_status.go +++ b/clients/algoliasearch-client-go/algolia/monitoring/model_server_status.go @@ -4,6 +4,7 @@ package monitoring import ( "encoding/json" "fmt" + "slices" ) // ServerStatus the model 'ServerStatus'. @@ -39,12 +40,10 @@ func (v *ServerStatus) UnmarshalJSON(src []byte) error { } enumTypeValue := ServerStatus(value) - for _, existing := range AllowedServerStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedServerStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ServerStatus", value) @@ -52,13 +51,7 @@ func (v *ServerStatus) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ServerStatus) IsValid() bool { - for _, existing := range AllowedServerStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedServerStatusEnumValues, v) } // Ptr returns reference to ServerStatus value. diff --git a/clients/algoliasearch-client-go/algolia/monitoring/model_status.go b/clients/algoliasearch-client-go/algolia/monitoring/model_status.go index 89dfaa32022..8f5459cb65a 100644 --- a/clients/algoliasearch-client-go/algolia/monitoring/model_status.go +++ b/clients/algoliasearch-client-go/algolia/monitoring/model_status.go @@ -4,6 +4,7 @@ package monitoring import ( "encoding/json" "fmt" + "slices" ) // Status Status of the cluster. @@ -45,12 +46,10 @@ func (v *Status) UnmarshalJSON(src []byte) error { } enumTypeValue := Status(value) - for _, existing := range AllowedStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Status", value) @@ -58,13 +57,7 @@ func (v *Status) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Status) IsValid() bool { - for _, existing := range AllowedStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedStatusEnumValues, v) } // Ptr returns reference to Status value. diff --git a/clients/algoliasearch-client-go/algolia/monitoring/model_type.go b/clients/algoliasearch-client-go/algolia/monitoring/model_type.go index 2f8cf324204..d639e769b07 100644 --- a/clients/algoliasearch-client-go/algolia/monitoring/model_type.go +++ b/clients/algoliasearch-client-go/algolia/monitoring/model_type.go @@ -4,6 +4,7 @@ package monitoring import ( "encoding/json" "fmt" + "slices" ) // Type the model 'Type'. @@ -39,12 +40,10 @@ func (v *Type) UnmarshalJSON(src []byte) error { } enumTypeValue := Type(value) - for _, existing := range AllowedTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Type", value) @@ -52,13 +51,7 @@ func (v *Type) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Type) IsValid() bool { - for _, existing := range AllowedTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTypeEnumValues, v) } // Ptr returns reference to Type value. diff --git a/clients/algoliasearch-client-go/algolia/personalization/model_event_type.go b/clients/algoliasearch-client-go/algolia/personalization/model_event_type.go index 72fc975e585..1e66874c55d 100644 --- a/clients/algoliasearch-client-go/algolia/personalization/model_event_type.go +++ b/clients/algoliasearch-client-go/algolia/personalization/model_event_type.go @@ -4,6 +4,7 @@ package personalization import ( "encoding/json" "fmt" + "slices" ) // EventType Event type. @@ -43,12 +44,10 @@ func (v *EventType) UnmarshalJSON(src []byte) error { } enumTypeValue := EventType(value) - for _, existing := range AllowedEventTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEventTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EventType", value) @@ -56,13 +55,7 @@ func (v *EventType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EventType) IsValid() bool { - for _, existing := range AllowedEventTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEventTypeEnumValues, v) } // Ptr returns reference to EventType value. diff --git a/clients/algoliasearch-client-go/algolia/query-suggestions/model_log_level.go b/clients/algoliasearch-client-go/algolia/query-suggestions/model_log_level.go index f8f7991306c..670ac357840 100644 --- a/clients/algoliasearch-client-go/algolia/query-suggestions/model_log_level.go +++ b/clients/algoliasearch-client-go/algolia/query-suggestions/model_log_level.go @@ -4,6 +4,7 @@ package suggestions import ( "encoding/json" "fmt" + "slices" ) // LogLevel Type of log entry. - `SKIP`. A query is skipped because it doesn't match the conditions for successful inclusion. For example, when a query doesn't generate enough search results. - `INFO`. An informative log entry. - `ERROR`. The Query Suggestions process encountered an error. @@ -43,12 +44,10 @@ func (v *LogLevel) UnmarshalJSON(src []byte) error { } enumTypeValue := LogLevel(value) - for _, existing := range AllowedLogLevelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedLogLevelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid LogLevel", value) @@ -56,13 +55,7 @@ func (v *LogLevel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v LogLevel) IsValid() bool { - for _, existing := range AllowedLogLevelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedLogLevelEnumValues, v) } // Ptr returns reference to LogLevel value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_advanced_syntax_features.go b/clients/algoliasearch-client-go/algolia/recommend/model_advanced_syntax_features.go index 2434714603f..af9acb97aa2 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_advanced_syntax_features.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_advanced_syntax_features.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // AdvancedSyntaxFeatures the model 'AdvancedSyntaxFeatures'. @@ -41,12 +42,10 @@ func (v *AdvancedSyntaxFeatures) UnmarshalJSON(src []byte) error { } enumTypeValue := AdvancedSyntaxFeatures(value) - for _, existing := range AllowedAdvancedSyntaxFeaturesEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAdvancedSyntaxFeaturesEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AdvancedSyntaxFeatures", value) @@ -54,13 +53,7 @@ func (v *AdvancedSyntaxFeatures) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AdvancedSyntaxFeatures) IsValid() bool { - for _, existing := range AllowedAdvancedSyntaxFeaturesEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAdvancedSyntaxFeaturesEnumValues, v) } // Ptr returns reference to advancedSyntaxFeatures value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_alternatives_as_exact.go b/clients/algoliasearch-client-go/algolia/recommend/model_alternatives_as_exact.go index 7a2bcde5833..fc483b79157 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_alternatives_as_exact.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_alternatives_as_exact.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // AlternativesAsExact the model 'AlternativesAsExact'. @@ -45,12 +46,10 @@ func (v *AlternativesAsExact) UnmarshalJSON(src []byte) error { } enumTypeValue := AlternativesAsExact(value) - for _, existing := range AllowedAlternativesAsExactEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAlternativesAsExactEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AlternativesAsExact", value) @@ -58,13 +57,7 @@ func (v *AlternativesAsExact) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AlternativesAsExact) IsValid() bool { - for _, existing := range AllowedAlternativesAsExactEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAlternativesAsExactEnumValues, v) } // Ptr returns reference to alternativesAsExact value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_around_radius_all.go b/clients/algoliasearch-client-go/algolia/recommend/model_around_radius_all.go index 5bb4a0608d4..f323917a1c6 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_around_radius_all.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_around_radius_all.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // AroundRadiusAll Return all records with a valid `_geoloc` attribute. Don't filter by distance. @@ -39,12 +40,10 @@ func (v *AroundRadiusAll) UnmarshalJSON(src []byte) error { } enumTypeValue := AroundRadiusAll(value) - for _, existing := range AllowedAroundRadiusAllEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAroundRadiusAllEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AroundRadiusAll", value) @@ -52,13 +51,7 @@ func (v *AroundRadiusAll) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AroundRadiusAll) IsValid() bool { - for _, existing := range AllowedAroundRadiusAllEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAroundRadiusAllEnumValues, v) } // Ptr returns reference to aroundRadiusAll value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_boolean_string.go b/clients/algoliasearch-client-go/algolia/recommend/model_boolean_string.go index 34f85dcd643..45e728770ab 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_boolean_string.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_boolean_string.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // BooleanString the model 'BooleanString'. @@ -41,12 +42,10 @@ func (v *BooleanString) UnmarshalJSON(src []byte) error { } enumTypeValue := BooleanString(value) - for _, existing := range AllowedBooleanStringEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedBooleanStringEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid BooleanString", value) @@ -54,13 +53,7 @@ func (v *BooleanString) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v BooleanString) IsValid() bool { - for _, existing := range AllowedBooleanStringEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedBooleanStringEnumValues, v) } // Ptr returns reference to booleanString value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_exact_on_single_word_query.go b/clients/algoliasearch-client-go/algolia/recommend/model_exact_on_single_word_query.go index b8f9e63ca52..18328f15be6 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_exact_on_single_word_query.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_exact_on_single_word_query.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // ExactOnSingleWordQuery Determines how the [Exact ranking criterion](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/adjust-exact-settings/#turn-off-exact-for-some-attributes) is computed when the search query has only one word. - `attribute`. The Exact ranking criterion is 1 if the query word and attribute value are the same. For example, a search for \"road\" will match the value \"road\", but not \"road trip\". - `none`. The Exact ranking criterion is ignored on single-word searches. - `word`. The Exact ranking criterion is 1 if the query word is found in the attribute value. The query word must have at least 3 characters and must not be a stop word. Only exact matches will be highlighted, partial and prefix matches won't. @@ -43,12 +44,10 @@ func (v *ExactOnSingleWordQuery) UnmarshalJSON(src []byte) error { } enumTypeValue := ExactOnSingleWordQuery(value) - for _, existing := range AllowedExactOnSingleWordQueryEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedExactOnSingleWordQueryEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ExactOnSingleWordQuery", value) @@ -56,13 +55,7 @@ func (v *ExactOnSingleWordQuery) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ExactOnSingleWordQuery) IsValid() bool { - for _, existing := range AllowedExactOnSingleWordQueryEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedExactOnSingleWordQueryEnumValues, v) } // Ptr returns reference to exactOnSingleWordQuery value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_fbt_model.go b/clients/algoliasearch-client-go/algolia/recommend/model_fbt_model.go index 7fd7f1bf69f..61044ffc311 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_fbt_model.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_fbt_model.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // FbtModel Frequently bought together model. This model recommends items that have been purchased within 1 day with the item with the ID `objectID`. @@ -39,12 +40,10 @@ func (v *FbtModel) UnmarshalJSON(src []byte) error { } enumTypeValue := FbtModel(value) - for _, existing := range AllowedFbtModelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedFbtModelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid FbtModel", value) @@ -52,13 +51,7 @@ func (v *FbtModel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v FbtModel) IsValid() bool { - for _, existing := range AllowedFbtModelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedFbtModelEnumValues, v) } // Ptr returns reference to fbtModel value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_looking_similar_model.go b/clients/algoliasearch-client-go/algolia/recommend/model_looking_similar_model.go index 431913fff5a..c32ee779732 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_looking_similar_model.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_looking_similar_model.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // LookingSimilarModel Looking similar model. This model recommends items that look similar to the item with the ID `objectID` based on image attributes in your index. @@ -39,12 +40,10 @@ func (v *LookingSimilarModel) UnmarshalJSON(src []byte) error { } enumTypeValue := LookingSimilarModel(value) - for _, existing := range AllowedLookingSimilarModelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedLookingSimilarModelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid LookingSimilarModel", value) @@ -52,13 +51,7 @@ func (v *LookingSimilarModel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v LookingSimilarModel) IsValid() bool { - for _, existing := range AllowedLookingSimilarModelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedLookingSimilarModelEnumValues, v) } // Ptr returns reference to lookingSimilarModel value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_match_level.go b/clients/algoliasearch-client-go/algolia/recommend/model_match_level.go index faa4d28cebd..cd3cb8e6660 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_match_level.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_match_level.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // MatchLevel Whether the whole query string matches or only a part. @@ -43,12 +44,10 @@ func (v *MatchLevel) UnmarshalJSON(src []byte) error { } enumTypeValue := MatchLevel(value) - for _, existing := range AllowedMatchLevelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMatchLevelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid MatchLevel", value) @@ -56,13 +55,7 @@ func (v *MatchLevel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v MatchLevel) IsValid() bool { - for _, existing := range AllowedMatchLevelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMatchLevelEnumValues, v) } // Ptr returns reference to matchLevel value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_query_type.go b/clients/algoliasearch-client-go/algolia/recommend/model_query_type.go index 8dbfdac718c..a09ae642878 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_query_type.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_query_type.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // QueryType Determines if and how query words are interpreted as prefixes. By default, only the last query word is treated as a prefix (`prefixLast`). To turn off prefix search, use `prefixNone`. Avoid `prefixAll`, which treats all query words as prefixes. This might lead to counterintuitive results and makes your search slower. For more information, see [Prefix searching](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/prefix-searching). @@ -43,12 +44,10 @@ func (v *QueryType) UnmarshalJSON(src []byte) error { } enumTypeValue := QueryType(value) - for _, existing := range AllowedQueryTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedQueryTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid QueryType", value) @@ -56,13 +55,7 @@ func (v *QueryType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v QueryType) IsValid() bool { - for _, existing := range AllowedQueryTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedQueryTypeEnumValues, v) } // Ptr returns reference to queryType value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_recommend_models.go b/clients/algoliasearch-client-go/algolia/recommend/model_recommend_models.go index 7d8c1b7cae2..04d177cc4de 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_recommend_models.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_recommend_models.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // RecommendModels the model 'RecommendModels'. @@ -45,12 +46,10 @@ func (v *RecommendModels) UnmarshalJSON(src []byte) error { } enumTypeValue := RecommendModels(value) - for _, existing := range AllowedRecommendModelsEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRecommendModelsEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RecommendModels", value) @@ -58,13 +57,7 @@ func (v *RecommendModels) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RecommendModels) IsValid() bool { - for _, existing := range AllowedRecommendModelsEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRecommendModelsEnumValues, v) } // Ptr returns reference to recommendModels value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_related_model.go b/clients/algoliasearch-client-go/algolia/recommend/model_related_model.go index 9d0402ea741..b3a595d8439 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_related_model.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_related_model.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // RelatedModel Related products or similar content model. This model recommends items that are similar to the item with the ID `objectID`. Similarity is determined from the user interactions and attributes. @@ -39,12 +40,10 @@ func (v *RelatedModel) UnmarshalJSON(src []byte) error { } enumTypeValue := RelatedModel(value) - for _, existing := range AllowedRelatedModelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRelatedModelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RelatedModel", value) @@ -52,13 +51,7 @@ func (v *RelatedModel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RelatedModel) IsValid() bool { - for _, existing := range AllowedRelatedModelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRelatedModelEnumValues, v) } // Ptr returns reference to relatedModel value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_remove_words_if_no_results.go b/clients/algoliasearch-client-go/algolia/recommend/model_remove_words_if_no_results.go index 130bfc63b37..b91bdd5dbb3 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_remove_words_if_no_results.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_remove_words_if_no_results.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // RemoveWordsIfNoResults Strategy for removing words from the query when it doesn't return any results. This helps to avoid returning empty search results. - `none`. No words are removed when a query doesn't return results. - `lastWords`. Treat the last (then second to last, then third to last) word as optional, until there are results or at most 5 words have been removed. - `firstWords`. Treat the first (then second, then third) word as optional, until there are results or at most 5 words have been removed. - `allOptional`. Treat all words as optional. For more information, see [Remove words to improve results](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/empty-or-insufficient-results/in-depth/why-use-remove-words-if-no-results). @@ -45,12 +46,10 @@ func (v *RemoveWordsIfNoResults) UnmarshalJSON(src []byte) error { } enumTypeValue := RemoveWordsIfNoResults(value) - for _, existing := range AllowedRemoveWordsIfNoResultsEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRemoveWordsIfNoResultsEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RemoveWordsIfNoResults", value) @@ -58,13 +57,7 @@ func (v *RemoveWordsIfNoResults) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RemoveWordsIfNoResults) IsValid() bool { - for _, existing := range AllowedRemoveWordsIfNoResultsEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRemoveWordsIfNoResultsEnumValues, v) } // Ptr returns reference to removeWordsIfNoResults value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_sort_remaining_by.go b/clients/algoliasearch-client-go/algolia/recommend/model_sort_remaining_by.go index 2bf348c748b..d1a021a0e3a 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_sort_remaining_by.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_sort_remaining_by.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // SortRemainingBy Order of facet values that aren't explicitly positioned with the `order` setting. - `count`. Order remaining facet values by decreasing count. The count is the number of matching records containing this facet value. - `alpha`. Sort facet values alphabetically. - `hidden`. Don't show facet values that aren't explicitly positioned. @@ -43,12 +44,10 @@ func (v *SortRemainingBy) UnmarshalJSON(src []byte) error { } enumTypeValue := SortRemainingBy(value) - for _, existing := range AllowedSortRemainingByEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSortRemainingByEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SortRemainingBy", value) @@ -56,13 +55,7 @@ func (v *SortRemainingBy) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SortRemainingBy) IsValid() bool { - for _, existing := range AllowedSortRemainingByEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSortRemainingByEnumValues, v) } // Ptr returns reference to sortRemainingBy value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_supported_language.go b/clients/algoliasearch-client-go/algolia/recommend/model_supported_language.go index a9cddec3feb..b1c8509afb4 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_supported_language.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_supported_language.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // SupportedLanguage ISO code for a supported language. @@ -173,12 +174,10 @@ func (v *SupportedLanguage) UnmarshalJSON(src []byte) error { } enumTypeValue := SupportedLanguage(value) - for _, existing := range AllowedSupportedLanguageEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSupportedLanguageEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SupportedLanguage", value) @@ -186,13 +185,7 @@ func (v *SupportedLanguage) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SupportedLanguage) IsValid() bool { - for _, existing := range AllowedSupportedLanguageEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSupportedLanguageEnumValues, v) } // Ptr returns reference to supportedLanguage value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_task_status.go b/clients/algoliasearch-client-go/algolia/recommend/model_task_status.go index 531ab52e7c2..0112fc578a4 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_task_status.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_task_status.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // TaskStatus Task status, `published` if the task is completed, `notPublished` otherwise. @@ -41,12 +42,10 @@ func (v *TaskStatus) UnmarshalJSON(src []byte) error { } enumTypeValue := TaskStatus(value) - for _, existing := range AllowedTaskStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTaskStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TaskStatus", value) @@ -54,13 +53,7 @@ func (v *TaskStatus) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TaskStatus) IsValid() bool { - for _, existing := range AllowedTaskStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTaskStatusEnumValues, v) } // Ptr returns reference to taskStatus value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_trending_facets_model.go b/clients/algoliasearch-client-go/algolia/recommend/model_trending_facets_model.go index 8730d108f86..d248dca8bff 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_trending_facets_model.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_trending_facets_model.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // TrendingFacetsModel Trending facet values model. This model recommends trending facet values for the specified facet attribute. @@ -39,12 +40,10 @@ func (v *TrendingFacetsModel) UnmarshalJSON(src []byte) error { } enumTypeValue := TrendingFacetsModel(value) - for _, existing := range AllowedTrendingFacetsModelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTrendingFacetsModelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TrendingFacetsModel", value) @@ -52,13 +51,7 @@ func (v *TrendingFacetsModel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TrendingFacetsModel) IsValid() bool { - for _, existing := range AllowedTrendingFacetsModelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTrendingFacetsModelEnumValues, v) } // Ptr returns reference to trendingFacetsModel value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_trending_items_model.go b/clients/algoliasearch-client-go/algolia/recommend/model_trending_items_model.go index b00208bd8e6..951a1bd9caf 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_trending_items_model.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_trending_items_model.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // TrendingItemsModel Trending items model. Trending items are determined from the number of conversion events collected on them. @@ -39,12 +40,10 @@ func (v *TrendingItemsModel) UnmarshalJSON(src []byte) error { } enumTypeValue := TrendingItemsModel(value) - for _, existing := range AllowedTrendingItemsModelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTrendingItemsModelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TrendingItemsModel", value) @@ -52,13 +51,7 @@ func (v *TrendingItemsModel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TrendingItemsModel) IsValid() bool { - for _, existing := range AllowedTrendingItemsModelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTrendingItemsModelEnumValues, v) } // Ptr returns reference to trendingItemsModel value. diff --git a/clients/algoliasearch-client-go/algolia/recommend/model_typo_tolerance_enum.go b/clients/algoliasearch-client-go/algolia/recommend/model_typo_tolerance_enum.go index e924dc11722..1951e012e37 100644 --- a/clients/algoliasearch-client-go/algolia/recommend/model_typo_tolerance_enum.go +++ b/clients/algoliasearch-client-go/algolia/recommend/model_typo_tolerance_enum.go @@ -4,6 +4,7 @@ package recommend import ( "encoding/json" "fmt" + "slices" ) // TypoToleranceEnum - `min`. Return matches with the lowest number of typos. For example, if you have matches without typos, only include those. But if there are no matches without typos (with 1 typo), include matches with 1 typo (2 typos). - `strict`. Return matches with the two lowest numbers of typos. With `strict`, the Typo ranking criterion is applied first in the `ranking` setting. @@ -45,12 +46,10 @@ func (v *TypoToleranceEnum) UnmarshalJSON(src []byte) error { } enumTypeValue := TypoToleranceEnum(value) - for _, existing := range AllowedTypoToleranceEnumEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTypoToleranceEnumEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TypoToleranceEnum", value) @@ -58,13 +57,7 @@ func (v *TypoToleranceEnum) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TypoToleranceEnum) IsValid() bool { - for _, existing := range AllowedTypoToleranceEnumEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTypoToleranceEnumEnumValues, v) } // Ptr returns reference to typoToleranceEnum value. diff --git a/clients/algoliasearch-client-go/algolia/search/api_search.go b/clients/algoliasearch-client-go/algolia/search/api_search.go index df8a8133d0b..09863d605ae 100644 --- a/clients/algoliasearch-client-go/algolia/search/api_search.go +++ b/clients/algoliasearch-client-go/algolia/search/api_search.go @@ -9943,7 +9943,7 @@ func (c *APIClient) ReplaceAllObjectsWithTransformation( if err != nil { _, _ = c.DeleteIndex(c.NewApiDeleteIndexRequest(tmpIndexName)) - return nil, err //nolint:wrapcheck + return nil, err } _, err = c.WaitForTask(tmpIndexName, copyResp.TaskID, replaceAllObjectsToIterableOptions(opts)...) @@ -10141,7 +10141,7 @@ func (c *APIClient) SaveObjectsWithTransformation( objects, ingestion.Action(ACTION_ADD_OBJECT), nil, - toIngestionChunkedBatchOptions(opts)...) //nolint:wrapcheck + toIngestionChunkedBatchOptions(opts)...) } /* @@ -10184,5 +10184,5 @@ func (c *APIClient) PartialUpdateObjectsWithTransformation( objects, ingestion.Action(action), nil, - toIngestionChunkedBatchOptions(partialUpdateObjectsToChunkedBatchOptions(opts))...) //nolint:wrapcheck + toIngestionChunkedBatchOptions(partialUpdateObjectsToChunkedBatchOptions(opts))...) } diff --git a/clients/algoliasearch-client-go/algolia/search/model_acl.go b/clients/algoliasearch-client-go/algolia/search/model_acl.go index 900fe59b4b2..f7f40aed02e 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_acl.go +++ b/clients/algoliasearch-client-go/algolia/search/model_acl.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // Acl Access control list permissions. @@ -83,12 +84,10 @@ func (v *Acl) UnmarshalJSON(src []byte) error { } enumTypeValue := Acl(value) - for _, existing := range AllowedAclEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAclEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Acl", value) @@ -96,13 +95,7 @@ func (v *Acl) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Acl) IsValid() bool { - for _, existing := range AllowedAclEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAclEnumValues, v) } // Ptr returns reference to acl value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_action.go b/clients/algoliasearch-client-go/algolia/search/model_action.go index 9842ce9a6a1..0269c36cb28 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_action.go +++ b/clients/algoliasearch-client-go/algolia/search/model_action.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // Action Which indexing operation to perform: - `addObject`: adds records to an index. Equivalent to the \"Add a new record (with auto-generated object ID)\" operation. - `updateObject`: adds or replaces records in an index. Equivalent to the \"Add or replace a record\" operation. - `partialUpdateObject`: adds or updates attributes within records. Equivalent to the \"Add or update attributes\" operation with the `createIfNoExists` parameter set to true. (If a record with the specified `objectID` doesn't exist in the specified index, this action creates adds the record to the index) - `partialUpdateObjectNoCreate`: same as `partialUpdateObject`, but with `createIfNoExists` set to false. (A record isn't added to the index if its `objectID` doesn't exist) - `deleteObject`: delete records from an index. Equivalent to the \"Delete a record\" operation. - `delete`. Delete an index. Equivalent to the \"Delete an index\" operation. - `clear`: delete all records from an index. Equivalent to the \"Delete all records from an index operation\". @@ -51,12 +52,10 @@ func (v *Action) UnmarshalJSON(src []byte) error { } enumTypeValue := Action(value) - for _, existing := range AllowedActionEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedActionEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Action", value) @@ -64,13 +63,7 @@ func (v *Action) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Action) IsValid() bool { - for _, existing := range AllowedActionEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedActionEnumValues, v) } // Ptr returns reference to action value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_advanced_syntax_features.go b/clients/algoliasearch-client-go/algolia/search/model_advanced_syntax_features.go index c102c03bd28..ff35ef14886 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_advanced_syntax_features.go +++ b/clients/algoliasearch-client-go/algolia/search/model_advanced_syntax_features.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // AdvancedSyntaxFeatures the model 'AdvancedSyntaxFeatures'. @@ -41,12 +42,10 @@ func (v *AdvancedSyntaxFeatures) UnmarshalJSON(src []byte) error { } enumTypeValue := AdvancedSyntaxFeatures(value) - for _, existing := range AllowedAdvancedSyntaxFeaturesEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAdvancedSyntaxFeaturesEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AdvancedSyntaxFeatures", value) @@ -54,13 +53,7 @@ func (v *AdvancedSyntaxFeatures) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AdvancedSyntaxFeatures) IsValid() bool { - for _, existing := range AllowedAdvancedSyntaxFeaturesEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAdvancedSyntaxFeaturesEnumValues, v) } // Ptr returns reference to advancedSyntaxFeatures value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_alternatives_as_exact.go b/clients/algoliasearch-client-go/algolia/search/model_alternatives_as_exact.go index ca90fb6a937..27e606debd1 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_alternatives_as_exact.go +++ b/clients/algoliasearch-client-go/algolia/search/model_alternatives_as_exact.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // AlternativesAsExact the model 'AlternativesAsExact'. @@ -45,12 +46,10 @@ func (v *AlternativesAsExact) UnmarshalJSON(src []byte) error { } enumTypeValue := AlternativesAsExact(value) - for _, existing := range AllowedAlternativesAsExactEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAlternativesAsExactEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AlternativesAsExact", value) @@ -58,13 +57,7 @@ func (v *AlternativesAsExact) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AlternativesAsExact) IsValid() bool { - for _, existing := range AllowedAlternativesAsExactEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAlternativesAsExactEnumValues, v) } // Ptr returns reference to alternativesAsExact value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_anchoring.go b/clients/algoliasearch-client-go/algolia/search/model_anchoring.go index 57bd0c8d4d9..a1a574a0178 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_anchoring.go +++ b/clients/algoliasearch-client-go/algolia/search/model_anchoring.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // Anchoring Which part of the search query the pattern should match: - `startsWith`. The pattern must match the beginning of the query. - `endsWith`. The pattern must match the end of the query. - `is`. The pattern must match the query exactly. - `contains`. The pattern must match anywhere in the query. Empty queries are only allowed as patterns with `anchoring: is`. @@ -45,12 +46,10 @@ func (v *Anchoring) UnmarshalJSON(src []byte) error { } enumTypeValue := Anchoring(value) - for _, existing := range AllowedAnchoringEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAnchoringEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Anchoring", value) @@ -58,13 +57,7 @@ func (v *Anchoring) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Anchoring) IsValid() bool { - for _, existing := range AllowedAnchoringEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAnchoringEnumValues, v) } // Ptr returns reference to anchoring value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go b/clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go index 7dc39a3987d..5000d7202d3 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go +++ b/clients/algoliasearch-client-go/algolia/search/model_api_key_operation.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // ApiKeyOperation the model 'ApiKeyOperation'. @@ -43,12 +44,10 @@ func (v *ApiKeyOperation) UnmarshalJSON(src []byte) error { } enumTypeValue := ApiKeyOperation(value) - for _, existing := range AllowedApiKeyOperationEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedApiKeyOperationEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ApiKeyOperation", value) @@ -56,13 +55,7 @@ func (v *ApiKeyOperation) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ApiKeyOperation) IsValid() bool { - for _, existing := range AllowedApiKeyOperationEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedApiKeyOperationEnumValues, v) } // Ptr returns reference to apiKeyOperation value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_around_radius_all.go b/clients/algoliasearch-client-go/algolia/search/model_around_radius_all.go index 27278a8be81..80b8cc3fc08 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_around_radius_all.go +++ b/clients/algoliasearch-client-go/algolia/search/model_around_radius_all.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // AroundRadiusAll Return all records with a valid `_geoloc` attribute. Don't filter by distance. @@ -39,12 +40,10 @@ func (v *AroundRadiusAll) UnmarshalJSON(src []byte) error { } enumTypeValue := AroundRadiusAll(value) - for _, existing := range AllowedAroundRadiusAllEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedAroundRadiusAllEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid AroundRadiusAll", value) @@ -52,13 +51,7 @@ func (v *AroundRadiusAll) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v AroundRadiusAll) IsValid() bool { - for _, existing := range AllowedAroundRadiusAllEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedAroundRadiusAllEnumValues, v) } // Ptr returns reference to aroundRadiusAll value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_boolean_string.go b/clients/algoliasearch-client-go/algolia/search/model_boolean_string.go index 741245b5898..a8e6eab183b 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_boolean_string.go +++ b/clients/algoliasearch-client-go/algolia/search/model_boolean_string.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // BooleanString the model 'BooleanString'. @@ -41,12 +42,10 @@ func (v *BooleanString) UnmarshalJSON(src []byte) error { } enumTypeValue := BooleanString(value) - for _, existing := range AllowedBooleanStringEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedBooleanStringEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid BooleanString", value) @@ -54,13 +53,7 @@ func (v *BooleanString) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v BooleanString) IsValid() bool { - for _, existing := range AllowedBooleanStringEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedBooleanStringEnumValues, v) } // Ptr returns reference to booleanString value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_built_in_operation_type.go b/clients/algoliasearch-client-go/algolia/search/model_built_in_operation_type.go index 9bbed8bf242..7c1db7863e7 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_built_in_operation_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_built_in_operation_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // BuiltInOperationType How to change the attribute. @@ -51,12 +52,10 @@ func (v *BuiltInOperationType) UnmarshalJSON(src []byte) error { } enumTypeValue := BuiltInOperationType(value) - for _, existing := range AllowedBuiltInOperationTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedBuiltInOperationTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid BuiltInOperationType", value) @@ -64,13 +63,7 @@ func (v *BuiltInOperationType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v BuiltInOperationType) IsValid() bool { - for _, existing := range AllowedBuiltInOperationTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedBuiltInOperationTypeEnumValues, v) } // Ptr returns reference to builtInOperationType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_category_prediction_bin.go b/clients/algoliasearch-client-go/algolia/search/model_category_prediction_bin.go index 6a1ea5031d8..63bc2fbfecc 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_category_prediction_bin.go +++ b/clients/algoliasearch-client-go/algolia/search/model_category_prediction_bin.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // CategoryPredictionBin Confidence level of the category prediction. @@ -47,12 +48,10 @@ func (v *CategoryPredictionBin) UnmarshalJSON(src []byte) error { } enumTypeValue := CategoryPredictionBin(value) - for _, existing := range AllowedCategoryPredictionBinEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedCategoryPredictionBinEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid CategoryPredictionBin", value) @@ -60,13 +59,7 @@ func (v *CategoryPredictionBin) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v CategoryPredictionBin) IsValid() bool { - for _, existing := range AllowedCategoryPredictionBinEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedCategoryPredictionBinEnumValues, v) } // Ptr returns reference to categoryPrediction_bin value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_dictionary_action.go b/clients/algoliasearch-client-go/algolia/search/model_dictionary_action.go index c3a930fd050..d62e49527c3 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_dictionary_action.go +++ b/clients/algoliasearch-client-go/algolia/search/model_dictionary_action.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // DictionaryAction Actions to perform. @@ -41,12 +42,10 @@ func (v *DictionaryAction) UnmarshalJSON(src []byte) error { } enumTypeValue := DictionaryAction(value) - for _, existing := range AllowedDictionaryActionEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDictionaryActionEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DictionaryAction", value) @@ -54,13 +53,7 @@ func (v *DictionaryAction) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DictionaryAction) IsValid() bool { - for _, existing := range AllowedDictionaryActionEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDictionaryActionEnumValues, v) } // Ptr returns reference to dictionaryAction value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_state.go b/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_state.go index c8394923c1e..cdf1fa363a3 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_state.go +++ b/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_state.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // DictionaryEntryState Whether a dictionary entry is active. @@ -41,12 +42,10 @@ func (v *DictionaryEntryState) UnmarshalJSON(src []byte) error { } enumTypeValue := DictionaryEntryState(value) - for _, existing := range AllowedDictionaryEntryStateEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDictionaryEntryStateEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DictionaryEntryState", value) @@ -54,13 +53,7 @@ func (v *DictionaryEntryState) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DictionaryEntryState) IsValid() bool { - for _, existing := range AllowedDictionaryEntryStateEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDictionaryEntryStateEnumValues, v) } // Ptr returns reference to dictionaryEntryState value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_type.go b/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_type.go index 2b8bd113e79..db4c82e85a6 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_dictionary_entry_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // DictionaryEntryType Whether a dictionary entry is provided by Algolia (standard), or has been added by you (custom). @@ -41,12 +42,10 @@ func (v *DictionaryEntryType) UnmarshalJSON(src []byte) error { } enumTypeValue := DictionaryEntryType(value) - for _, existing := range AllowedDictionaryEntryTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDictionaryEntryTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DictionaryEntryType", value) @@ -54,13 +53,7 @@ func (v *DictionaryEntryType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DictionaryEntryType) IsValid() bool { - for _, existing := range AllowedDictionaryEntryTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDictionaryEntryTypeEnumValues, v) } // Ptr returns reference to dictionaryEntryType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_dictionary_type.go b/clients/algoliasearch-client-go/algolia/search/model_dictionary_type.go index 5ccac5e24ff..10bd97dd132 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_dictionary_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_dictionary_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // DictionaryType the model 'DictionaryType'. @@ -43,12 +44,10 @@ func (v *DictionaryType) UnmarshalJSON(src []byte) error { } enumTypeValue := DictionaryType(value) - for _, existing := range AllowedDictionaryTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedDictionaryTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid DictionaryType", value) @@ -56,13 +55,7 @@ func (v *DictionaryType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v DictionaryType) IsValid() bool { - for _, existing := range AllowedDictionaryTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedDictionaryTypeEnumValues, v) } // Ptr returns reference to dictionaryType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_edit_type.go b/clients/algoliasearch-client-go/algolia/search/model_edit_type.go index c53c17ea7ef..c830e212d1f 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_edit_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_edit_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // EditType Type of edit. @@ -41,12 +42,10 @@ func (v *EditType) UnmarshalJSON(src []byte) error { } enumTypeValue := EditType(value) - for _, existing := range AllowedEditTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEditTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EditType", value) @@ -54,13 +53,7 @@ func (v *EditType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EditType) IsValid() bool { - for _, existing := range AllowedEditTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEditTypeEnumValues, v) } // Ptr returns reference to editType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_event_status.go b/clients/algoliasearch-client-go/algolia/search/model_event_status.go index b597ca7ebea..ce345ab765e 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_event_status.go +++ b/clients/algoliasearch-client-go/algolia/search/model_event_status.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // EventStatus the model 'EventStatus'. @@ -49,12 +50,10 @@ func (v *EventStatus) UnmarshalJSON(src []byte) error { } enumTypeValue := EventStatus(value) - for _, existing := range AllowedEventStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEventStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EventStatus", value) @@ -62,13 +61,7 @@ func (v *EventStatus) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EventStatus) IsValid() bool { - for _, existing := range AllowedEventStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEventStatusEnumValues, v) } // Ptr returns reference to EventStatus value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_event_type.go b/clients/algoliasearch-client-go/algolia/search/model_event_type.go index 49d8997ee22..b0d6b058e53 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_event_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_event_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // EventType the model 'EventType'. @@ -45,12 +46,10 @@ func (v *EventType) UnmarshalJSON(src []byte) error { } enumTypeValue := EventType(value) - for _, existing := range AllowedEventTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedEventTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid EventType", value) @@ -58,13 +57,7 @@ func (v *EventType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v EventType) IsValid() bool { - for _, existing := range AllowedEventTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedEventTypeEnumValues, v) } // Ptr returns reference to EventType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_exact_on_single_word_query.go b/clients/algoliasearch-client-go/algolia/search/model_exact_on_single_word_query.go index 365c4c50f26..29f52cbc1e8 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_exact_on_single_word_query.go +++ b/clients/algoliasearch-client-go/algolia/search/model_exact_on_single_word_query.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // ExactOnSingleWordQuery Determines how the [Exact ranking criterion](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/adjust-exact-settings/#turn-off-exact-for-some-attributes) is computed when the search query has only one word. - `attribute`. The Exact ranking criterion is 1 if the query word and attribute value are the same. For example, a search for \"road\" will match the value \"road\", but not \"road trip\". - `none`. The Exact ranking criterion is ignored on single-word searches. - `word`. The Exact ranking criterion is 1 if the query word is found in the attribute value. The query word must have at least 3 characters and must not be a stop word. Only exact matches will be highlighted, partial and prefix matches won't. @@ -43,12 +44,10 @@ func (v *ExactOnSingleWordQuery) UnmarshalJSON(src []byte) error { } enumTypeValue := ExactOnSingleWordQuery(value) - for _, existing := range AllowedExactOnSingleWordQueryEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedExactOnSingleWordQueryEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ExactOnSingleWordQuery", value) @@ -56,13 +55,7 @@ func (v *ExactOnSingleWordQuery) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ExactOnSingleWordQuery) IsValid() bool { - for _, existing := range AllowedExactOnSingleWordQueryEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedExactOnSingleWordQueryEnumValues, v) } // Ptr returns reference to exactOnSingleWordQuery value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_log_type.go b/clients/algoliasearch-client-go/algolia/search/model_log_type.go index b2443eee0a0..69b29325d7c 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_log_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_log_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // LogType the model 'LogType'. @@ -45,12 +46,10 @@ func (v *LogType) UnmarshalJSON(src []byte) error { } enumTypeValue := LogType(value) - for _, existing := range AllowedLogTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedLogTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid LogType", value) @@ -58,13 +57,7 @@ func (v *LogType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v LogType) IsValid() bool { - for _, existing := range AllowedLogTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedLogTypeEnumValues, v) } // Ptr returns reference to logType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_match_level.go b/clients/algoliasearch-client-go/algolia/search/model_match_level.go index 4a622aad222..c12150b675a 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_match_level.go +++ b/clients/algoliasearch-client-go/algolia/search/model_match_level.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // MatchLevel Whether the whole query string matches or only a part. @@ -43,12 +44,10 @@ func (v *MatchLevel) UnmarshalJSON(src []byte) error { } enumTypeValue := MatchLevel(value) - for _, existing := range AllowedMatchLevelEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedMatchLevelEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid MatchLevel", value) @@ -56,13 +55,7 @@ func (v *MatchLevel) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v MatchLevel) IsValid() bool { - for _, existing := range AllowedMatchLevelEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedMatchLevelEnumValues, v) } // Ptr returns reference to matchLevel value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_mode.go b/clients/algoliasearch-client-go/algolia/search/model_mode.go index c4bba59d7dd..93252e4a6d2 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_mode.go +++ b/clients/algoliasearch-client-go/algolia/search/model_mode.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // Mode Search mode the index will use to query for results. This setting only applies to indices, for which Algolia enabled NeuralSearch for you. @@ -41,12 +42,10 @@ func (v *Mode) UnmarshalJSON(src []byte) error { } enumTypeValue := Mode(value) - for _, existing := range AllowedModeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedModeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid Mode", value) @@ -54,13 +53,7 @@ func (v *Mode) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v Mode) IsValid() bool { - for _, existing := range AllowedModeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedModeEnumValues, v) } // Ptr returns reference to mode value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_operation_type.go b/clients/algoliasearch-client-go/algolia/search/model_operation_type.go index cdcb3fec108..1f0c6de2110 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_operation_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_operation_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // OperationType Operation to perform on the index. @@ -41,12 +42,10 @@ func (v *OperationType) UnmarshalJSON(src []byte) error { } enumTypeValue := OperationType(value) - for _, existing := range AllowedOperationTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedOperationTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid OperationType", value) @@ -54,13 +53,7 @@ func (v *OperationType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v OperationType) IsValid() bool { - for _, existing := range AllowedOperationTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedOperationTypeEnumValues, v) } // Ptr returns reference to operationType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_query_categorization_type.go b/clients/algoliasearch-client-go/algolia/search/model_query_categorization_type.go index 304956f05e7..18ed4107bc6 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_query_categorization_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_query_categorization_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // QueryCategorizationType Classification of the query scope. @@ -45,12 +46,10 @@ func (v *QueryCategorizationType) UnmarshalJSON(src []byte) error { } enumTypeValue := QueryCategorizationType(value) - for _, existing := range AllowedQueryCategorizationTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedQueryCategorizationTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid QueryCategorizationType", value) @@ -58,13 +57,7 @@ func (v *QueryCategorizationType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v QueryCategorizationType) IsValid() bool { - for _, existing := range AllowedQueryCategorizationTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedQueryCategorizationTypeEnumValues, v) } // Ptr returns reference to queryCategorization_type value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_query_type.go b/clients/algoliasearch-client-go/algolia/search/model_query_type.go index 4ab4b5c1259..ec9af56c7ee 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_query_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_query_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // QueryType Determines if and how query words are interpreted as prefixes. By default, only the last query word is treated as a prefix (`prefixLast`). To turn off prefix search, use `prefixNone`. Avoid `prefixAll`, which treats all query words as prefixes. This might lead to counterintuitive results and makes your search slower. For more information, see [Prefix searching](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/prefix-searching). @@ -43,12 +44,10 @@ func (v *QueryType) UnmarshalJSON(src []byte) error { } enumTypeValue := QueryType(value) - for _, existing := range AllowedQueryTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedQueryTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid QueryType", value) @@ -56,13 +55,7 @@ func (v *QueryType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v QueryType) IsValid() bool { - for _, existing := range AllowedQueryTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedQueryTypeEnumValues, v) } // Ptr returns reference to queryType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_remove_words_if_no_results.go b/clients/algoliasearch-client-go/algolia/search/model_remove_words_if_no_results.go index 0d7e60c0c81..4b80ca5d499 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_remove_words_if_no_results.go +++ b/clients/algoliasearch-client-go/algolia/search/model_remove_words_if_no_results.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // RemoveWordsIfNoResults Strategy for removing words from the query when it doesn't return any results. This helps to avoid returning empty search results. - `none`. No words are removed when a query doesn't return results. - `lastWords`. Treat the last (then second to last, then third to last) word as optional, until there are results or at most 5 words have been removed. - `firstWords`. Treat the first (then second, then third) word as optional, until there are results or at most 5 words have been removed. - `allOptional`. Treat all words as optional. For more information, see [Remove words to improve results](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/empty-or-insufficient-results/in-depth/why-use-remove-words-if-no-results). @@ -45,12 +46,10 @@ func (v *RemoveWordsIfNoResults) UnmarshalJSON(src []byte) error { } enumTypeValue := RemoveWordsIfNoResults(value) - for _, existing := range AllowedRemoveWordsIfNoResultsEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedRemoveWordsIfNoResultsEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid RemoveWordsIfNoResults", value) @@ -58,13 +57,7 @@ func (v *RemoveWordsIfNoResults) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v RemoveWordsIfNoResults) IsValid() bool { - for _, existing := range AllowedRemoveWordsIfNoResultsEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedRemoveWordsIfNoResultsEnumValues, v) } // Ptr returns reference to removeWordsIfNoResults value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_scope_type.go b/clients/algoliasearch-client-go/algolia/search/model_scope_type.go index 17a7d6d2340..62c63bf0ade 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_scope_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_scope_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // ScopeType the model 'ScopeType'. @@ -43,12 +44,10 @@ func (v *ScopeType) UnmarshalJSON(src []byte) error { } enumTypeValue := ScopeType(value) - for _, existing := range AllowedScopeTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedScopeTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid ScopeType", value) @@ -56,13 +55,7 @@ func (v *ScopeType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v ScopeType) IsValid() bool { - for _, existing := range AllowedScopeTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedScopeTypeEnumValues, v) } // Ptr returns reference to scopeType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_search_strategy.go b/clients/algoliasearch-client-go/algolia/search/model_search_strategy.go index f96650e09b9..b2205a7ffb0 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_search_strategy.go +++ b/clients/algoliasearch-client-go/algolia/search/model_search_strategy.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // SearchStrategy Strategy for multiple search queries: - `none`. Run all queries. - `stopIfEnoughMatches`. Run the queries one by one, stopping as soon as a query matches at least the `hitsPerPage` number of results. @@ -41,12 +42,10 @@ func (v *SearchStrategy) UnmarshalJSON(src []byte) error { } enumTypeValue := SearchStrategy(value) - for _, existing := range AllowedSearchStrategyEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSearchStrategyEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SearchStrategy", value) @@ -54,13 +53,7 @@ func (v *SearchStrategy) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SearchStrategy) IsValid() bool { - for _, existing := range AllowedSearchStrategyEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSearchStrategyEnumValues, v) } // Ptr returns reference to searchStrategy value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_search_type_default.go b/clients/algoliasearch-client-go/algolia/search/model_search_type_default.go index e24c7e7058a..a4daa0e3415 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_search_type_default.go +++ b/clients/algoliasearch-client-go/algolia/search/model_search_type_default.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // SearchTypeDefault - `default`: perform a search query - `facet` [searches for facet values](https://www.algolia.com/doc/guides/managing-results/refine-results/faceting/#search-for-facet-values). @@ -39,12 +40,10 @@ func (v *SearchTypeDefault) UnmarshalJSON(src []byte) error { } enumTypeValue := SearchTypeDefault(value) - for _, existing := range AllowedSearchTypeDefaultEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSearchTypeDefaultEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SearchTypeDefault", value) @@ -52,13 +51,7 @@ func (v *SearchTypeDefault) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SearchTypeDefault) IsValid() bool { - for _, existing := range AllowedSearchTypeDefaultEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSearchTypeDefaultEnumValues, v) } // Ptr returns reference to searchTypeDefault value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_search_type_facet.go b/clients/algoliasearch-client-go/algolia/search/model_search_type_facet.go index cfaa4dd05b2..96637c8f265 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_search_type_facet.go +++ b/clients/algoliasearch-client-go/algolia/search/model_search_type_facet.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // SearchTypeFacet - `default`: perform a search query - `facet` [searches for facet values](https://www.algolia.com/doc/guides/managing-results/refine-results/faceting/#search-for-facet-values). @@ -39,12 +40,10 @@ func (v *SearchTypeFacet) UnmarshalJSON(src []byte) error { } enumTypeValue := SearchTypeFacet(value) - for _, existing := range AllowedSearchTypeFacetEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSearchTypeFacetEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SearchTypeFacet", value) @@ -52,13 +51,7 @@ func (v *SearchTypeFacet) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SearchTypeFacet) IsValid() bool { - for _, existing := range AllowedSearchTypeFacetEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSearchTypeFacetEnumValues, v) } // Ptr returns reference to searchTypeFacet value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_sort_remaining_by.go b/clients/algoliasearch-client-go/algolia/search/model_sort_remaining_by.go index b4056310651..ac290227b6c 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_sort_remaining_by.go +++ b/clients/algoliasearch-client-go/algolia/search/model_sort_remaining_by.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // SortRemainingBy Order of facet values that aren't explicitly positioned with the `order` setting. - `count`. Order remaining facet values by decreasing count. The count is the number of matching records containing this facet value. - `alpha`. Sort facet values alphabetically. - `hidden`. Don't show facet values that aren't explicitly positioned. @@ -43,12 +44,10 @@ func (v *SortRemainingBy) UnmarshalJSON(src []byte) error { } enumTypeValue := SortRemainingBy(value) - for _, existing := range AllowedSortRemainingByEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSortRemainingByEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SortRemainingBy", value) @@ -56,13 +55,7 @@ func (v *SortRemainingBy) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SortRemainingBy) IsValid() bool { - for _, existing := range AllowedSortRemainingByEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSortRemainingByEnumValues, v) } // Ptr returns reference to sortRemainingBy value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_supported_language.go b/clients/algoliasearch-client-go/algolia/search/model_supported_language.go index c5776d05400..b3ba591cd46 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_supported_language.go +++ b/clients/algoliasearch-client-go/algolia/search/model_supported_language.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // SupportedLanguage ISO code for a supported language. @@ -173,12 +174,10 @@ func (v *SupportedLanguage) UnmarshalJSON(src []byte) error { } enumTypeValue := SupportedLanguage(value) - for _, existing := range AllowedSupportedLanguageEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSupportedLanguageEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SupportedLanguage", value) @@ -186,13 +185,7 @@ func (v *SupportedLanguage) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SupportedLanguage) IsValid() bool { - for _, existing := range AllowedSupportedLanguageEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSupportedLanguageEnumValues, v) } // Ptr returns reference to supportedLanguage value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_synonym_type.go b/clients/algoliasearch-client-go/algolia/search/model_synonym_type.go index 579ffdba8e4..1de523e1235 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_synonym_type.go +++ b/clients/algoliasearch-client-go/algolia/search/model_synonym_type.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // SynonymType Synonym type. @@ -53,12 +54,10 @@ func (v *SynonymType) UnmarshalJSON(src []byte) error { } enumTypeValue := SynonymType(value) - for _, existing := range AllowedSynonymTypeEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedSynonymTypeEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid SynonymType", value) @@ -66,13 +65,7 @@ func (v *SynonymType) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v SynonymType) IsValid() bool { - for _, existing := range AllowedSynonymTypeEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedSynonymTypeEnumValues, v) } // Ptr returns reference to SynonymType value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_task_status.go b/clients/algoliasearch-client-go/algolia/search/model_task_status.go index a470d610768..2d0c8b5a38d 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_task_status.go +++ b/clients/algoliasearch-client-go/algolia/search/model_task_status.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // TaskStatus Task status, `published` if the task is completed, `notPublished` otherwise. @@ -41,12 +42,10 @@ func (v *TaskStatus) UnmarshalJSON(src []byte) error { } enumTypeValue := TaskStatus(value) - for _, existing := range AllowedTaskStatusEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTaskStatusEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TaskStatus", value) @@ -54,13 +53,7 @@ func (v *TaskStatus) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TaskStatus) IsValid() bool { - for _, existing := range AllowedTaskStatusEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTaskStatusEnumValues, v) } // Ptr returns reference to taskStatus value. diff --git a/clients/algoliasearch-client-go/algolia/search/model_typo_tolerance_enum.go b/clients/algoliasearch-client-go/algolia/search/model_typo_tolerance_enum.go index af54b790e0a..fa6a203ec89 100644 --- a/clients/algoliasearch-client-go/algolia/search/model_typo_tolerance_enum.go +++ b/clients/algoliasearch-client-go/algolia/search/model_typo_tolerance_enum.go @@ -4,6 +4,7 @@ package search import ( "encoding/json" "fmt" + "slices" ) // TypoToleranceEnum - `min`. Return matches with the lowest number of typos. For example, if you have matches without typos, only include those. But if there are no matches without typos (with 1 typo), include matches with 1 typo (2 typos). - `strict`. Return matches with the two lowest numbers of typos. With `strict`, the Typo ranking criterion is applied first in the `ranking` setting. @@ -45,12 +46,10 @@ func (v *TypoToleranceEnum) UnmarshalJSON(src []byte) error { } enumTypeValue := TypoToleranceEnum(value) - for _, existing := range AllowedTypoToleranceEnumEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue + if slices.Contains(AllowedTypoToleranceEnumEnumValues, enumTypeValue) { + *v = enumTypeValue - return nil - } + return nil } return fmt.Errorf("%+v is not a valid TypoToleranceEnum", value) @@ -58,13 +57,7 @@ func (v *TypoToleranceEnum) UnmarshalJSON(src []byte) error { // IsValid return true if the value is valid for the enum, false otherwise. func (v TypoToleranceEnum) IsValid() bool { - for _, existing := range AllowedTypoToleranceEnumEnumValues { - if existing == v { - return true - } - } - - return false + return slices.Contains(AllowedTypoToleranceEnumEnumValues, v) } // Ptr returns reference to typoToleranceEnum value. diff --git a/clients/algoliasearch-client-java/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-java/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-java/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-java/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-java/.java-version b/clients/algoliasearch-client-java/.java-version index 6a24e5df07b..5e5d5c43256 100644 --- a/clients/algoliasearch-client-java/.java-version +++ b/clients/algoliasearch-client-java/.java-version @@ -1 +1 @@ -21.0.11+10 +21.0.11 diff --git a/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal/HttpRequester.java b/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal/HttpRequester.java index bd7488365a3..5f0a585df8f 100644 --- a/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal/HttpRequester.java +++ b/clients/algoliasearch-client-java/algoliasearch/src/main/java/com/algolia/internal/HttpRequester.java @@ -93,8 +93,8 @@ private T execute(@Nonnull HttpRequest httpRequest, RequestOptions requestOp } // Return null if there's no content or the return type isn't provided. - if (returnType == null || response.body() == null || response.body().contentLength() == 0) { - return null; // No need to deserialize, either no content or no type provided + if (returnType == null || response.body() == null || response.body().contentLength() == 0 || response.code() == 204) { + return null; } // Returns the raw response when using `*WithHTTPInfo` methods. diff --git a/clients/algoliasearch-client-javascript/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-javascript/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-javascript/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-javascript/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-javascript/package.json b/clients/algoliasearch-client-javascript/package.json index 774c9fffce0..98f259ca92a 100644 --- a/clients/algoliasearch-client-javascript/package.json +++ b/clients/algoliasearch-client-javascript/package.json @@ -7,7 +7,7 @@ "packages/*" ], "scripts": { - "build": "lerna run build --scope '@algolia/requester-testing' --scope '@algolia/logger-console' --scope 'algoliasearch' --scope '@algolia/composition' --scope '@algolia/advanced-personalization' --include-dependencies", + "build": "lerna run build --scope '@algolia/requester-testing' --scope '@algolia/logger-console' --scope 'algoliasearch' --scope '@algolia/composition' --scope '@algolia/advanced-personalization' --scope '@algolia/agent-studio' --include-dependencies", "clean": "lerna run clean", "release:publish": "lerna publish from-package --yes", "release:publish:old": "tsc --project scripts/tsconfig.json && node scripts/dist/publish.js", @@ -27,55 +27,59 @@ "files": [ { "path": "packages/algoliasearch/dist/algoliasearch.umd.js", - "maxSize": "14.8KB" + "maxSize": "17KB" }, { "path": "packages/algoliasearch/dist/lite/builds/browser.umd.js", - "maxSize": "4.2KB" + "maxSize": "6KB" }, { "path": "packages/abtesting/dist/builds/browser.umd.js", - "maxSize": "4.5KB" + "maxSize": "6KB" }, { "path": "packages/client-abtesting/dist/builds/browser.umd.js", - "maxSize": "4.4KB" + "maxSize": "6KB" }, { "path": "packages/client-analytics/dist/builds/browser.umd.js", - "maxSize": "5.1KB" + "maxSize": "6KB" }, { "path": "packages/composition/dist/builds/browser.umd.js", - "maxSize": "5.0KB" + "maxSize": "6KB" }, { "path": "packages/client-insights/dist/builds/browser.umd.js", - "maxSize": "4.2KB" + "maxSize": "6KB" }, { "path": "packages/client-personalization/dist/builds/browser.umd.js", - "maxSize": "4.3KB" + "maxSize": "6KB" }, { "path": "packages/client-query-suggestions/dist/builds/browser.umd.js", - "maxSize": "4.3KB" + "maxSize": "6KB" }, { "path": "packages/client-search/dist/builds/browser.umd.js", - "maxSize": "7.7KB" + "maxSize": "9KB" }, { "path": "packages/ingestion/dist/builds/browser.umd.js", - "maxSize": "7.1KB" + "maxSize": "8KB" }, { "path": "packages/monitoring/dist/builds/browser.umd.js", - "maxSize": "4.3KB" + "maxSize": "6KB" }, { "path": "packages/recommend/dist/builds/browser.umd.js", - "maxSize": "4.5KB" + "maxSize": "6KB" + }, + { + "path": "packages/agent-studio/dist/builds/browser.umd.js", + "maxSize": "8KB" } ] }, diff --git a/clients/algoliasearch-client-javascript/packages/agent-studio/package.json b/clients/algoliasearch-client-javascript/packages/agent-studio/package.json new file mode 100644 index 00000000000..841c917680b --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/agent-studio/package.json @@ -0,0 +1,73 @@ +{ + "version": "1.0.0", + "repository": { + "type": "git", + "url": "git+https://github.com/algolia/algoliasearch-client-javascript.git" + }, + "homepage": "https://github.com/algolia/algoliasearch-client-javascript/tree/main/packages/agent-studio#readme", + "type": "module", + "license": "MIT", + "author": "Algolia", + "scripts": { + "build": "yarn clean && yarn tsup && yarn rollup -c rollup.config.js", + "clean": "rm -rf ./dist || true", + "test:bundle": "publint . && attw --pack ." + }, + "name": "@algolia/agent-studio", + "description": "JavaScript client for agent-studio", + "exports": { + ".": { + "react-native": { + "types": "./dist/browser.d.ts", + "default": "./dist/builds/browser.js" + }, + "node": { + "types": { + "import": "./dist/node.d.ts", + "module": "./dist/node.d.ts", + "require": "./dist/node.d.cts" + }, + "import": "./dist/builds/node.js", + "module": "./dist/builds/node.js", + "require": "./dist/builds/node.cjs" + }, + "worker": { + "types": "./dist/worker.d.ts", + "default": "./dist/builds/worker.js" + }, + "default": { + "types": "./dist/browser.d.ts", + "module": "./dist/builds/browser.js", + "import": "./dist/builds/browser.js", + "default": "./dist/builds/browser.umd.js" + } + }, + "./dist/builds/*": "./dist/builds/*.js", + "./dist/builds/*.js": "./dist/builds/*.js" + }, + "jsdelivr": "./dist/builds/browser.umd.js", + "unpkg": "./dist/builds/browser.umd.js", + "react-native": "./dist/builds/browser.js", + "files": [ + "dist", + "index.js", + "index.d.ts" + ], + "dependencies": { + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" + }, + "devDependencies": { + "@arethetypeswrong/cli": "0.18.2", + "@types/node": "25.1.0", + "publint": "0.3.18", + "rollup": "4.59.0", + "tsup": "8.5.1", + "typescript": "5.9.3" + }, + "engines": { + "node": ">= 14.0.0" + } +} diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/__tests__/algoliasearch.common.test.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/__tests__/algoliasearch.common.test.ts index 6d1ea96827d..45ed4e6892f 100644 --- a/clients/algoliasearch-client-javascript/packages/algoliasearch/__tests__/algoliasearch.common.test.ts +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/__tests__/algoliasearch.common.test.ts @@ -120,8 +120,10 @@ describe('api', () => { set: expect.any(Function), }, request: expect.any(Function), + requestStream: expect.any(Function), requester: { send: expect.any(Function), + sendStream: expect.any(Function), }, requestsCache: { clear: expect.any(Function), diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/browser.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/browser.ts index 596afbce4a0..e6baabb1434 100644 --- a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/browser.ts +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/browser.ts @@ -4,6 +4,8 @@ import type { ClientOptions, RequestOptions } from '@algolia/client-common'; import type { AbtestingV3Client } from '@algolia/abtesting'; import { abtestingV3Client } from '@algolia/abtesting'; +import type { AgentStudioClient } from '@algolia/agent-studio'; +import { agentStudioClient } from '@algolia/agent-studio'; import type { AbtestingClient } from '@algolia/client-abtesting'; import { abtestingClient } from '@algolia/client-abtesting'; import type { AnalyticsClient } from '@algolia/client-analytics'; @@ -55,6 +57,7 @@ export type Algoliasearch = SearchClient & { initPersonalization: (initOptions: InitClientOptions & PersonalizationRegionOptions) => PersonalizationClient; initQuerySuggestions: (initOptions: InitClientOptions & QuerySuggestionsRegionOptions) => QuerySuggestionsClient; initRecommend: (initOptions?: InitClientOptions) => RecommendClient; + initAgentStudio: (initOptions?: InitClientOptions) => AgentStudioClient; // Bridge helpers to expose along with the search endpoints at the root of the API client @@ -351,5 +354,9 @@ export function algoliasearch( initRecommend: (initOptions: InitClientOptions = {}): RecommendClient => { return recommendClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); }, + + initAgentStudio: (initOptions: InitClientOptions = {}): AgentStudioClient => { + return agentStudioClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); + }, }; } diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/fetch.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/fetch.ts index 596afbce4a0..e6baabb1434 100644 --- a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/fetch.ts +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/fetch.ts @@ -4,6 +4,8 @@ import type { ClientOptions, RequestOptions } from '@algolia/client-common'; import type { AbtestingV3Client } from '@algolia/abtesting'; import { abtestingV3Client } from '@algolia/abtesting'; +import type { AgentStudioClient } from '@algolia/agent-studio'; +import { agentStudioClient } from '@algolia/agent-studio'; import type { AbtestingClient } from '@algolia/client-abtesting'; import { abtestingClient } from '@algolia/client-abtesting'; import type { AnalyticsClient } from '@algolia/client-analytics'; @@ -55,6 +57,7 @@ export type Algoliasearch = SearchClient & { initPersonalization: (initOptions: InitClientOptions & PersonalizationRegionOptions) => PersonalizationClient; initQuerySuggestions: (initOptions: InitClientOptions & QuerySuggestionsRegionOptions) => QuerySuggestionsClient; initRecommend: (initOptions?: InitClientOptions) => RecommendClient; + initAgentStudio: (initOptions?: InitClientOptions) => AgentStudioClient; // Bridge helpers to expose along with the search endpoints at the root of the API client @@ -351,5 +354,9 @@ export function algoliasearch( initRecommend: (initOptions: InitClientOptions = {}): RecommendClient => { return recommendClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); }, + + initAgentStudio: (initOptions: InitClientOptions = {}): AgentStudioClient => { + return agentStudioClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); + }, }; } diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/models.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/models.ts index de0e4c4c08f..1e6cfb7c117 100644 --- a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/models.ts +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/models.ts @@ -142,6 +142,7 @@ import type { } from '@algolia/client-abtesting'; export * from '@algolia/abtesting'; +export * from '@algolia/agent-studio'; export * from '@algolia/client-abtesting'; export * from '@algolia/client-analytics'; export * from '@algolia/client-insights'; diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/node.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/node.ts index 596afbce4a0..e6baabb1434 100644 --- a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/node.ts +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/node.ts @@ -4,6 +4,8 @@ import type { ClientOptions, RequestOptions } from '@algolia/client-common'; import type { AbtestingV3Client } from '@algolia/abtesting'; import { abtestingV3Client } from '@algolia/abtesting'; +import type { AgentStudioClient } from '@algolia/agent-studio'; +import { agentStudioClient } from '@algolia/agent-studio'; import type { AbtestingClient } from '@algolia/client-abtesting'; import { abtestingClient } from '@algolia/client-abtesting'; import type { AnalyticsClient } from '@algolia/client-analytics'; @@ -55,6 +57,7 @@ export type Algoliasearch = SearchClient & { initPersonalization: (initOptions: InitClientOptions & PersonalizationRegionOptions) => PersonalizationClient; initQuerySuggestions: (initOptions: InitClientOptions & QuerySuggestionsRegionOptions) => QuerySuggestionsClient; initRecommend: (initOptions?: InitClientOptions) => RecommendClient; + initAgentStudio: (initOptions?: InitClientOptions) => AgentStudioClient; // Bridge helpers to expose along with the search endpoints at the root of the API client @@ -351,5 +354,9 @@ export function algoliasearch( initRecommend: (initOptions: InitClientOptions = {}): RecommendClient => { return recommendClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); }, + + initAgentStudio: (initOptions: InitClientOptions = {}): AgentStudioClient => { + return agentStudioClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); + }, }; } diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/worker.ts b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/worker.ts index 596afbce4a0..e6baabb1434 100644 --- a/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/worker.ts +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch/builds/worker.ts @@ -4,6 +4,8 @@ import type { ClientOptions, RequestOptions } from '@algolia/client-common'; import type { AbtestingV3Client } from '@algolia/abtesting'; import { abtestingV3Client } from '@algolia/abtesting'; +import type { AgentStudioClient } from '@algolia/agent-studio'; +import { agentStudioClient } from '@algolia/agent-studio'; import type { AbtestingClient } from '@algolia/client-abtesting'; import { abtestingClient } from '@algolia/client-abtesting'; import type { AnalyticsClient } from '@algolia/client-analytics'; @@ -55,6 +57,7 @@ export type Algoliasearch = SearchClient & { initPersonalization: (initOptions: InitClientOptions & PersonalizationRegionOptions) => PersonalizationClient; initQuerySuggestions: (initOptions: InitClientOptions & QuerySuggestionsRegionOptions) => QuerySuggestionsClient; initRecommend: (initOptions?: InitClientOptions) => RecommendClient; + initAgentStudio: (initOptions?: InitClientOptions) => AgentStudioClient; // Bridge helpers to expose along with the search endpoints at the root of the API client @@ -351,5 +354,9 @@ export function algoliasearch( initRecommend: (initOptions: InitClientOptions = {}): RecommendClient => { return recommendClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); }, + + initAgentStudio: (initOptions: InitClientOptions = {}): AgentStudioClient => { + return agentStudioClient(initOptions.appId || appId, initOptions.apiKey || apiKey, initOptions.options); + }, }; } diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/__tests__/sse.test.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/__tests__/sse.test.ts new file mode 100644 index 00000000000..030d9c935dc --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/__tests__/sse.test.ts @@ -0,0 +1,156 @@ +import { describe, expect, test } from 'vitest'; + +import { iterSSEEvents, type ServerSentEvent } from '../sse'; + +// ─── Helpers ────────────────────────────────────────────────────────────────── + +/** Encode each string argument as a separate Uint8Array chunk in an AsyncIterable. */ +function toStream(...chunks: string[]): AsyncIterable { + const encoder = new TextEncoder(); + return { + async *[Symbol.asyncIterator]() { + for (const chunk of chunks) { + yield encoder.encode(chunk); + } + }, + }; +} + +/** Feed chunks through iterSSEEvents and collect all emitted events. */ +async function collectEvents(...chunks: string[]): Promise { + const events: ServerSentEvent[] = []; + for await (const event of iterSSEEvents(toStream(...chunks))) { + events.push(event); + } + return events; +} + +/** Base event with all defaults — spread to override specific fields. */ +const BASE: ServerSentEvent = { data: '', event: '', id: null, retry: null }; + +// ─── Tests ──────────────────────────────────────────────────────────────────── + +describe('iterSSEEvents', () => { + describe('WHATWG spec cases', () => { + test('1. single data event', async () => { + const events = await collectEvents('data: hello\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hello' }); + }); + + test('2. multi-line data', async () => { + const events = await collectEvents('data: line1\ndata: line2\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'line1\nline2' }); + }); + + test('3. event type', async () => { + const events = await collectEvents('event: custom\ndata: hi\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, event: 'custom', data: 'hi' }); + }); + + test('4. comment ignored', async () => { + const events = await collectEvents(': comment\ndata: hi\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hi' }); + }); + + test('5. empty data suppressed (no event dispatched)', async () => { + const events = await collectEvents('event: ping\n\n'); + expect(events).toHaveLength(0); + }); + + test('6. field with no colon', async () => { + const events = await collectEvents('data\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: '' }); + }); + + test('7. single space strip (two spaces in → one space out)', async () => { + const events = await collectEvents('data: hello\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: ' hello' }); + }); + + test('8. unknown field ignored', async () => { + const events = await collectEvents('foo: bar\ndata: hi\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hi' }); + }); + + test('9. id persistence across dispatches', async () => { + const events = await collectEvents('id: 42\ndata: a\n\ndata: b\n\n'); + expect(events).toHaveLength(2); + expect(events[0]).toEqual({ ...BASE, data: 'a', id: '42' }); + expect(events[1]).toEqual({ ...BASE, data: 'b', id: '42' }); + }); + + test('10. id with NULL character ignored', async () => { + const events = await collectEvents('id: foo\0bar\ndata: hi\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hi' }); + }); + + test('11. retry with digits only', async () => { + const events = await collectEvents('retry: 3000\ndata: hi\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hi', retry: 3000 }); + }); + + test('12. retry with non-digits ignored', async () => { + const events = await collectEvents('retry: 3s\ndata: hi\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hi' }); + }); + + test('13. CR line endings', async () => { + const events = await collectEvents('data: hello\r\r'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hello' }); + }); + + test('14. CRLF line endings', async () => { + const events = await collectEvents('data: hello\r\n\r\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hello' }); + }); + + test('15. mixed line endings', async () => { + const events = await collectEvents('data: a\rdata: b\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'a\nb' }); + }); + + test('16. stream ends mid-event (no dispatch)', async () => { + const events = await collectEvents('data: partial'); + expect(events).toHaveLength(0); + }); + }); + + describe('chunk boundary handling', () => { + test('17. trailingCR across chunk boundaries', async () => { + const events = await collectEvents('data: hello\r', '\ndata: world\r\n\r\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hello\nworld' }); + }); + }); + + describe('safety limits', () => { + test('18. 10MB buffer cap throws', async () => { + const chunk = 'a'.repeat(2 * 1024 * 1024); // 2MB per chunk, no newlines + const chunks = Array(6).fill(chunk); // 12MB total > 10MB limit + await expect(collectEvents(...chunks)).rejects.toThrow('SSE line buffer exceeded 10MB'); + }); + }); + + describe('eventType reset', () => { + test('19. eventType resets on suppressed dispatch', async () => { + // First blank line: eventType="custom" but no data → suppressed, eventType resets to "" + // Second event: data="hi" with eventType="" (not "custom") + const events = await collectEvents('event: custom\n\ndata: hi\n\n'); + expect(events).toHaveLength(1); + expect(events[0]).toEqual({ ...BASE, data: 'hi', event: '' }); + }); + }); +}); diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/index.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/index.ts index 8e0a62b8469..bfb1f96d355 100644 --- a/clients/algoliasearch-client-javascript/packages/client-common/src/index.ts +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/index.ts @@ -5,5 +5,7 @@ export * from './createAuth'; export * from './createIterablePromise'; export * from './getAlgoliaAgent'; export * from './logger'; +export * from './sse'; export * from './transporter'; export * from './types'; +export * from './validateParam'; diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/sse.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/sse.ts new file mode 100644 index 00000000000..8976a220a6f --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/sse.ts @@ -0,0 +1,301 @@ +/** + * WHATWG-compliant Server-Sent Events parser. + * + * Three-layer architecture: + * 1. iterLines() — byte chunking → line decoding + * 2. SSEDecoder — line → SSE event decoding + * 3. iterSSEEvents — top-level composer (exported) + * + * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation + */ + +const MAX_LINE_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB + +// ─── Types ────────────────────────────────────────────────────────────────── + +export type ServerSentEvent = { + /** Concatenated data: field values, joined by '\n'. */ + data: string; + /** Event type from the event: field. Defaults to "" (empty string). */ + event: string; + /** Last event ID. Persists across dispatches until changed. */ + id: string | null; + /** Reconnection time in ms. Persists across dispatches until changed. */ + retry: number | null; +}; + +/** + * Wrapper for a parsed SSE event, yielded by the typed `*Stream` methods. + * + * - `data` is the JSON-parsed payload when parsing succeeds, `null` otherwise. + * - `raw` is the original {@link ServerSentEvent} (always present). + * - `error` is set when JSON parsing of `event.data` failed. + */ +export type StreamEvent> = { + /** Parsed data from the event, or `null` if parsing failed. */ + data: T | null; + /** The original, unparsed SSE event. */ + raw: ServerSentEvent; + /** The error that occurred while parsing `event.data`, if any. */ + error?: Error; +}; + +// ─── Helpers ──────────────────────────────────────────────────────────────── + +/** + * Converts a ReadableStream into an AsyncIterable via getReader(). + * Fallback for environments where ReadableStream lacks Symbol.asyncIterator. + */ +async function* readableStreamToAsyncIterable(stream: ReadableStream): AsyncGenerator { + const reader = stream.getReader(); + try { + while (true) { + const { done, value } = await reader.read(); + if (done) return; + yield value; + } + } finally { + reader.releaseLock(); + } +} + +/** + * Normalizes the input to an AsyncIterable. + * Prefers Symbol.asyncIterator if available; falls back to getReader(). + */ +function toAsyncIterable(stream: ReadableStream | AsyncIterable): AsyncIterable { + if (Symbol.asyncIterator in stream) { + return stream as AsyncIterable; + } + return readableStreamToAsyncIterable(stream as ReadableStream); +} + +// ─── Layer 1: Byte stream → Lines ────────────────────────────────────────── + +/** + * Yields individual lines from a byte stream. + * + * Handles \r, \n, and \r\n line endings, including \r\n split across chunks. + * Uses offset tracking within each decoded chunk to avoid O(n²) buffer growth. + * Strips BOM (U+FEFF) from the very first line. + * Throws if the internal line buffer exceeds 10MB. + */ +async function* iterLines(stream: ReadableStream | AsyncIterable): AsyncGenerator { + const decoder = new TextDecoder('utf-8'); + const buffer: string[] = []; + let bufferSize = 0; + let trailingCR = false; + let isFirstLine = true; + + for await (const chunk of toAsyncIterable(stream)) { + const text = decoder.decode(chunk, { stream: true }); + let offset = 0; + + // Handle \r\n split across chunks: if the previous chunk ended with \r + // and this one starts with \n, skip the \n (it's the second half of \r\n) + if (trailingCR) { + trailingCR = false; + if (text.length > 0 && text[0] === '\n') { + offset = 1; + } + } + + while (offset < text.length) { + const crIdx = text.indexOf('\r', offset); + const lfIdx = text.indexOf('\n', offset); + + // No more line endings in this chunk — buffer the rest + if (crIdx === -1 && lfIdx === -1) { + const remaining = text.slice(offset); + buffer.push(remaining); + bufferSize += remaining.length; + if (bufferSize > MAX_LINE_BUFFER_SIZE) { + throw new Error('SSE line buffer exceeded 10MB'); + } + break; + } + + let endIdx: number; + let skipLen: number; + + if (crIdx !== -1 && (lfIdx === -1 || crIdx < lfIdx)) { + // \r found before \n (or no \n at all) + endIdx = crIdx; + if (crIdx + 1 < text.length) { + // Peek ahead: \r\n or bare \r + skipLen = text[crIdx + 1] === '\n' ? 2 : 1; + } else { + // \r at end of chunk — might be \r\n split across chunks + trailingCR = true; + skipLen = 1; + } + } else { + // \n found before \r (or no \r at all) + // Safe: at least one of crIdx/lfIdx is != -1, and we're in the else + // branch, so lfIdx must be != -1 + endIdx = lfIdx; + skipLen = 1; + } + + const segment = text.slice(offset, endIdx); + buffer.push(segment); + + let line = buffer.length === 1 ? buffer[0]! : buffer.join(''); + buffer.length = 0; + bufferSize = 0; + + // Strip BOM from the very first line only + if (isFirstLine) { + if (line.startsWith('\uFEFF')) { + line = line.slice(1); + } + isFirstLine = false; + } + + yield line; + offset = endIdx + skipLen; + } + } + + // Flush TextDecoder (handles any remaining bytes from multi-byte sequences) + const remaining = decoder.decode(); + if (remaining) { + buffer.push(remaining); + } + + // Yield any remaining buffered content as the final line + if (buffer.length > 0) { + let line = buffer.join(''); + if (isFirstLine && line.startsWith('\uFEFF')) { + line = line.slice(1); + } + yield line; + } +} + +// ─── Layer 2: Lines → SSE Events ────────────────────────────────────────── + +/** + * Stateful SSE event decoder. Feed lines one at a time via decode(). + * Returns a ServerSentEvent on blank lines (dispatch), null otherwise. + * + * Per WHATWG spec §9.2.6: + * - lastEventId persists across dispatches + * - retry persists across dispatches (global reconnection setting) + * - eventType resets after every blank line (even when data is empty) + * - data buffer resets after dispatch + * - id field containing NULL (\0) is ignored entirely + * - retry field must be ASCII digits only + */ +class SSEDecoder { + private data: string[] = []; + private eventType = ''; + private lastEventId: string | null = null; + private retry: number | null = null; + + decode(line: string): ServerSentEvent | null { + // Blank line → dispatch event or reset + if (line === '') { + return this.dispatch(); + } + + // Comment line (starts with ':') + if (line[0] === ':') { + return null; + } + + // Parse field:value + const colonIdx = line.indexOf(':'); + let field: string; + let value: string; + + if (colonIdx === -1) { + // No colon → field is entire line, value is empty + field = line; + value = ''; + } else { + field = line.slice(0, colonIdx); + value = line.slice(colonIdx + 1); + // Strip exactly ONE leading space (if present) + if (value[0] === ' ') { + value = value.slice(1); + } + } + + switch (field) { + case 'data': + this.data.push(value); + break; + case 'event': + this.eventType = value; + break; + case 'id': + // Ignore if value contains NULL character (security: WHATWG spec) + if (!value.includes('\0')) { + this.lastEventId = value; + } + break; + case 'retry': + // Must consist of ASCII digits only (no negatives, no whitespace) + if (/^[0-9]+$/.test(value)) { + this.retry = parseInt(value, 10); + } + break; + // Unknown fields: ignore silently + } + + return null; + } + + private dispatch(): ServerSentEvent | null { + // eventType resets on every blank line per WHATWG spec, + // regardless of whether we actually dispatch an event + const currentEventType = this.eventType; + this.eventType = ''; + + // Suppress dispatch when no data: lines were received + if (this.data.length === 0) { + return null; + } + + const event: ServerSentEvent = { + data: this.data.join('\n'), + event: currentEventType, + id: this.lastEventId, + retry: this.retry, + }; + + // Reset data buffer; lastEventId and retry persist across dispatches + this.data = []; + + return event; + } +} + +// ─── Layer 3: Top-level composer ────────────────────────────────────────── + +/** + * Parses a byte stream as WHATWG Server-Sent Events. + * + * Accepts both ReadableStream (browser fetch) and + * AsyncIterable (Node.js streams / Buffer chunks). + * + * @example + * ```ts + * const response = await fetch(url); + * for await (const event of iterSSEEvents(response.body!)) { + * console.log(event.event, event.data); + * } + * ``` + */ +export async function* iterSSEEvents( + stream: ReadableStream | AsyncIterable, +): AsyncGenerator { + const decoder = new SSEDecoder(); + for await (const line of iterLines(stream)) { + const event = decoder.decode(line); + if (event !== null) { + yield event; + } + } +} diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/createTransporter.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/createTransporter.ts index e949f0e3c02..96c9702fbef 100644 --- a/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/createTransporter.ts +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/createTransporter.ts @@ -1,3 +1,5 @@ +import type { ServerSentEvent } from '../sse'; +import { iterSSEEvents } from '../sse'; import type { EndRequest, Host, @@ -311,6 +313,74 @@ export function createTransporter({ ); } + async function* requestStream( + request: Request, + requestOptions: RequestOptions = {}, + ): AsyncGenerator { + if (!requester.sendStream) { + throw new Error('This requester does not support streaming'); + } + + const data = serializeData(request, requestOptions); + const headers = serializeHeaders(baseHeaders, request.headers, requestOptions.headers); + headers['accept'] = 'text/event-stream'; + + // On `GET`, the data is proxied to query parameters. + const dataQueryParameters: QueryParameters = + request.method === 'GET' + ? { + ...request.data, + ...requestOptions.data, + } + : {}; + + const queryParameters: QueryParameters = { + ...baseQueryParameters, + ...request.queryParameters, + ...dataQueryParameters, + }; + + if (algoliaAgent.value) { + queryParameters['x-algolia-agent'] = algoliaAgent.value; + } + + if (requestOptions && requestOptions.queryParameters) { + for (const key of Object.keys(requestOptions.queryParameters)) { + if ( + !requestOptions.queryParameters[key] || + Object.prototype.toString.call(requestOptions.queryParameters[key]) === '[object Object]' + ) { + queryParameters[key] = requestOptions.queryParameters[key]; + } else { + queryParameters[key] = requestOptions.queryParameters[key].toString(); + } + } + } + + const isRead = request.useReadTransporter || request.method === 'GET'; + const compatibleHosts = hosts.filter( + (host) => host.accept === 'readWrite' || (isRead ? host.accept === 'read' : host.accept === 'write'), + ); + const options = await createRetryableOptions(compatibleHosts); + const host = options.hosts[0]; + if (!host) { + throw new RetryError([]); + } + + const timeout = { ...timeouts, ...requestOptions.timeouts }; + const payload: EndRequest = { + data, + headers, + method: request.method, + url: serializeUrl(host, request.path, queryParameters), + connectTimeout: timeout.connect, + responseTimeout: isRead ? timeout.read : timeout.write, + }; + + const stream = await requester.sendStream(payload); + yield* iterSSEEvents(stream); + } + return { hostsCache, requester, @@ -321,6 +391,7 @@ export function createTransporter({ baseQueryParameters, hosts, request: createRequest, + requestStream, requestsCache, responsesCache, }; diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts index 96e0716e5a3..0b9199c3468 100644 --- a/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/transporter/helpers.ts @@ -75,6 +75,11 @@ export function serializeHeaders( } export function deserializeSuccess(response: Response): TObject { + // Handle 204 No Content and other empty responses + if (response.status === 204 || response.content.length === 0) { + return undefined as unknown as TObject; + } + try { return JSON.parse(response.content); } catch (e) { diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/types/requester.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/types/requester.ts index 3b4f2f19109..6dab4daa57e 100644 --- a/clients/algoliasearch-client-javascript/packages/client-common/src/types/requester.ts +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/types/requester.ts @@ -64,4 +64,8 @@ export type Requester = { * Sends the given `request` to the server. */ send: (request: EndRequest) => Promise; + /** + * Sends the given `request` and returns a raw byte stream for streaming responses (e.g. SSE). + */ + sendStream?: (request: EndRequest) => Promise>; }; diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/types/transporter.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/types/transporter.ts index 01c666536a1..212aeb36fb6 100644 --- a/clients/algoliasearch-client-javascript/packages/client-common/src/types/transporter.ts +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/types/transporter.ts @@ -1,3 +1,4 @@ +import type { ServerSentEvent } from '../sse'; import type { Cache } from './cache'; import type { Host } from './host'; import type { Logger } from './logger'; @@ -168,4 +169,5 @@ export type Transporter = TransporterOptions & { * The `baseRequest` and `baseRequestOptions` will be merged accordingly. */ request: (baseRequest: Request, baseRequestOptions?: RequestOptions) => Promise; + requestStream: (baseRequest: Request, baseRequestOptions?: RequestOptions) => AsyncGenerator; }; diff --git a/clients/algoliasearch-client-javascript/packages/client-common/src/validateParam.ts b/clients/algoliasearch-client-javascript/packages/client-common/src/validateParam.ts new file mode 100644 index 00000000000..6491601e526 --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/client-common/src/validateParam.ts @@ -0,0 +1,5 @@ +export function validateRequired(field: string, method: string, value: unknown): void { + if (value === null || value === undefined || (typeof value === 'string' && value.length === 0)) { + throw new Error(`Parameter \`${field}\` is required when calling \`${method}\`.`); + } +} diff --git a/clients/algoliasearch-client-javascript/packages/client-common/vitest.config.ts b/clients/algoliasearch-client-javascript/packages/client-common/vitest.config.ts index 545af63885f..f6073e7c7cc 100644 --- a/clients/algoliasearch-client-javascript/packages/client-common/vitest.config.ts +++ b/clients/algoliasearch-client-javascript/packages/client-common/vitest.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ 'src/__tests__/cache/memory-cache.test.ts', 'src/__tests__/create-iterable-promise.test.ts', 'src/__tests__/logger/null-logger.test.ts', + 'src/__tests__/sse.test.ts', 'src/__tests__/transporter/cache.test.ts', 'src/__tests__/transporter/compression.test.ts', ], diff --git a/clients/algoliasearch-client-javascript/packages/requester-fetch/src/createFetchRequester.ts b/clients/algoliasearch-client-javascript/packages/requester-fetch/src/createFetchRequester.ts index a180f1f8cc6..b3e721e4816 100644 --- a/clients/algoliasearch-client-javascript/packages/requester-fetch/src/createFetchRequester.ts +++ b/clients/algoliasearch-client-javascript/packages/requester-fetch/src/createFetchRequester.ts @@ -70,5 +70,29 @@ export function createFetchRequester({ requesterOptions = {} }: FetchRequesterOp } } - return { send }; + async function sendStream(request: EndRequest): Promise> { + const fetchRes = await fetch(request.url, { + method: request.method, + body: (request.data as BodyInit) || null, + redirect: 'manual', + ...requesterOptions, + headers: { + ...requesterOptions.headers, + ...request.headers, + }, + }); + + if (!fetchRes.ok) { + const text = await fetchRes.text(); + throw new Error(`HTTP ${fetchRes.status}: ${text}`); + } + + if (fetchRes.body === null) { + throw new Error('Response body is null — streaming not supported'); + } + + return fetchRes.body; + } + + return { send, sendStream }; } diff --git a/clients/algoliasearch-client-javascript/packages/requester-node-http/src/createHttpRequester.ts b/clients/algoliasearch-client-javascript/packages/requester-node-http/src/createHttpRequester.ts index be3d3d9ed74..e88eb99e3cb 100644 --- a/clients/algoliasearch-client-javascript/packages/requester-node-http/src/createHttpRequester.ts +++ b/clients/algoliasearch-client-javascript/packages/requester-node-http/src/createHttpRequester.ts @@ -1,5 +1,6 @@ import http from 'http'; import https from 'https'; +import { Readable } from 'stream'; import { URL } from 'url'; import zlib from 'zlib'; @@ -109,5 +110,54 @@ export function createHttpRequester({ }); } - return { send }; + function sendStream(request: EndRequest): Promise> { + return new Promise((resolve, reject) => { + const url = new URL(request.url); + const path = url.search === null ? url.pathname : `${url.pathname}${url.search}`; + const options: https.RequestOptions = { + agent: url.protocol === 'https:' ? httpsAgent : httpAgent, + hostname: url.hostname, + path, + method: request.method, + ...requesterOptions, + headers: { + ...request.headers, + ...requesterOptions.headers, + }, + }; + + if (url.port && !requesterOptions.port) { + options.port = url.port; + } + + const req = (url.protocol === 'https:' ? https : http).request(options, (response) => { + const statusCode = response.statusCode || 0; + + if (statusCode < 200 || statusCode >= 300) { + let body = ''; + response.on('data', (chunk) => { + body += chunk; + }); + response.on('end', () => { + reject(new Error(`HTTP ${statusCode}: ${body}`)); + }); + return; + } + + resolve(Readable.toWeb(response) as ReadableStream); + }); + + req.on('error', (error) => { + reject(error); + }); + + if (request.data !== undefined) { + req.write(request.data); + } + + req.end(); + }); + } + + return { send, sendStream }; } diff --git a/clients/algoliasearch-client-javascript/packages/requester-testing/src/createEchoRequester.ts b/clients/algoliasearch-client-javascript/packages/requester-testing/src/createEchoRequester.ts index 750d13a5dfa..c28ecbdd2e8 100644 --- a/clients/algoliasearch-client-javascript/packages/requester-testing/src/createEchoRequester.ts +++ b/clients/algoliasearch-client-javascript/packages/requester-testing/src/createEchoRequester.ts @@ -77,5 +77,30 @@ export function createEchoRequester({ getURL, status = 200 }: EchoRequesterParam }); } - return { send }; + function sendStream(request: EndRequest): Promise> { + const { host, searchParams, algoliaAgent, path } = getUrlParams(getURL(request.url)); + + const content: EchoResponse = { + ...request, + data: request.data ? JSON.parse(request.data as string) : undefined, + path, + host, + algoliaAgent, + searchParams, + }; + + const ssePayload = `data: ${JSON.stringify(content)}\n\n`; + const encoded = new TextEncoder().encode(ssePayload); + + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(encoded); + controller.close(); + }, + }); + + return Promise.resolve(stream); + } + + return { send, sendStream }; } diff --git a/clients/algoliasearch-client-javascript/yarn.lock b/clients/algoliasearch-client-javascript/yarn.lock index 6dea89aa8be..7183ddfa936 100644 --- a/clients/algoliasearch-client-javascript/yarn.lock +++ b/clients/algoliasearch-client-javascript/yarn.lock @@ -39,6 +39,23 @@ __metadata: languageName: unknown linkType: soft +"@algolia/agent-studio@workspace:packages/agent-studio": + version: 0.0.0-use.local + resolution: "@algolia/agent-studio@workspace:packages/agent-studio" + dependencies: + "@algolia/client-common": "npm:5.52.0" + "@algolia/requester-browser-xhr": "npm:5.52.0" + "@algolia/requester-fetch": "npm:5.52.0" + "@algolia/requester-node-http": "npm:5.52.0" + "@arethetypeswrong/cli": "npm:0.18.2" + "@types/node": "npm:25.1.0" + publint: "npm:0.3.18" + rollup: "npm:4.59.0" + tsup: "npm:8.5.1" + typescript: "npm:5.9.3" + languageName: unknown + linkType: soft + "@algolia/client-abtesting@npm:5.52.1, @algolia/client-abtesting@workspace:packages/client-abtesting": version: 0.0.0-use.local resolution: "@algolia/client-abtesting@workspace:packages/client-abtesting" @@ -73,6 +90,13 @@ __metadata: languageName: unknown linkType: soft +"@algolia/client-common@npm:5.52.0": + version: 5.52.0 + resolution: "@algolia/client-common@npm:5.52.0" + checksum: 10/a5b0b834991c323435b5231b10ddcb51a2275e7800419088d8f0e44da782fff17eab931913d4b5ae5a3613e032ef24c74c0318e23a2ba493d8b84e1ec7fe9b11 + languageName: node + linkType: hard + "@algolia/client-common@npm:5.52.1, @algolia/client-common@workspace:packages/client-common": version: 0.0.0-use.local resolution: "@algolia/client-common@workspace:packages/client-common" @@ -240,6 +264,15 @@ __metadata: languageName: unknown linkType: soft +"@algolia/requester-browser-xhr@npm:5.52.0": + version: 5.52.0 + resolution: "@algolia/requester-browser-xhr@npm:5.52.0" + dependencies: + "@algolia/client-common": "npm:5.52.0" + checksum: 10/4d6ed87c45eccf44017c5089e7831e841fbdfb9995417e55e72739e5a6de9803bb57671833844d99620796342728837b4508ff50d324c3cb3928fa2c3af4bb3f + languageName: node + linkType: hard + "@algolia/requester-browser-xhr@npm:5.52.1, @algolia/requester-browser-xhr@workspace:packages/requester-browser-xhr": version: 0.0.0-use.local resolution: "@algolia/requester-browser-xhr@workspace:packages/requester-browser-xhr" @@ -256,6 +289,15 @@ __metadata: languageName: unknown linkType: soft +"@algolia/requester-fetch@npm:5.52.0": + version: 5.52.0 + resolution: "@algolia/requester-fetch@npm:5.52.0" + dependencies: + "@algolia/client-common": "npm:5.52.0" + checksum: 10/be1294748e4ff07d74e4c9a5429abcf298df65bf7fb486ad58a22bf9beb9a22ffd4a14856ef18adef92ca38e4c8ef138baed2a810fe76babccea49b3c40f9d07 + languageName: node + linkType: hard + "@algolia/requester-fetch@npm:5.52.1, @algolia/requester-fetch@workspace:packages/requester-fetch": version: 0.0.0-use.local resolution: "@algolia/requester-fetch@workspace:packages/requester-fetch" @@ -272,6 +314,15 @@ __metadata: languageName: unknown linkType: soft +"@algolia/requester-node-http@npm:5.52.0": + version: 5.52.0 + resolution: "@algolia/requester-node-http@npm:5.52.0" + dependencies: + "@algolia/client-common": "npm:5.52.0" + checksum: 10/e4e16628b3c516e92a83fd69ae48089d4ce7f253a12d6a686e03fc20b5e86268e5136e4f72df3c87a7ee49564698941d81b3d3d7e769b69220ca01795f7fa866 + languageName: node + linkType: hard + "@algolia/requester-node-http@npm:5.52.1, @algolia/requester-node-http@workspace:packages/requester-node-http": version: 0.0.0-use.local resolution: "@algolia/requester-node-http@workspace:packages/requester-node-http" diff --git a/clients/algoliasearch-client-kotlin/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-kotlin/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-kotlin/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-kotlin/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/transport/internal/KtorRequester.kt b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/transport/internal/KtorRequester.kt index 0b6cee77580..4105a208605 100644 --- a/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/transport/internal/KtorRequester.kt +++ b/clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/transport/internal/KtorRequester.kt @@ -67,7 +67,10 @@ public class KtorRequester( requestBuilder.setTimeout(requestOptions, callType, host) try { val response = httpClient.request(requestBuilder) - val body = response.body(returnType) + @Suppress("UNCHECKED_CAST") + val body: T = + if (response.status.value == 204 || response.contentLength() == 0L) null as T + else response.body(returnType) mutex.withLock { host.reset() } return body } catch (exception: Throwable) { diff --git a/clients/algoliasearch-client-php/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-php/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-php/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-php/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-php/lib/RetryStrategy/ApiWrapper.php b/clients/algoliasearch-client-php/lib/RetryStrategy/ApiWrapper.php index bf2d38ac1c4..643995085bc 100644 --- a/clients/algoliasearch-client-php/lib/RetryStrategy/ApiWrapper.php +++ b/clients/algoliasearch-client-php/lib/RetryStrategy/ApiWrapper.php @@ -284,15 +284,19 @@ private function handleResponse( throw new AlgoliaException($statusCode.': '.$response->getReasonPhrase(), $statusCode); } - try { - $deserializeStart = microtime(true); - $responseArray = Helpers::json_decode($body, true); - $deserializeDurationMs = round((microtime(true) - $deserializeStart) * 1000); - $this->log(LogLevel::DEBUG, 'Response body deserialized in '.$deserializeDurationMs.'ms'); - } catch (\InvalidArgumentException $e) { - $this->log(LogLevel::ERROR, 'Failed to deserialize response: '.$e->getMessage()); - - throw $e; + if (204 === $statusCode || '' === $body) { + $responseArray = null; + } else { + try { + $deserializeStart = microtime(true); + $responseArray = Helpers::json_decode($body, true); + $deserializeDurationMs = round((microtime(true) - $deserializeStart) * 1000); + $this->log(LogLevel::DEBUG, 'Response body deserialized in '.$deserializeDurationMs.'ms'); + } catch (\InvalidArgumentException $e) { + $this->log(LogLevel::ERROR, 'Failed to deserialize response: '.$e->getMessage()); + + throw $e; + } } if (404 === $statusCode) { diff --git a/clients/algoliasearch-client-python/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-python/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-python/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-python/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-python/algoliasearch/http/api_response.py b/clients/algoliasearch-client-python/algoliasearch/http/api_response.py index aa7c7de7bb2..a98804c9b18 100644 --- a/clients/algoliasearch-client-python/algoliasearch/http/api_response.py +++ b/clients/algoliasearch-client-python/algoliasearch/http/api_response.py @@ -60,14 +60,19 @@ def deserialize(klass: Any = None, data: Any = None) -> Any: :return: object. """ - if data is None: + if data is None or data == "": return None if hasattr(klass, "__origin__") and klass.__origin__ is list: sub_kls = klass.__args__[0] - arr = json.loads(data) + arr = json.loads(data) if isinstance(data, str) else data return [ApiResponse.deserialize(sub_kls, sub_data) for sub_data in arr] + if hasattr(klass, "__origin__") and klass.__origin__ is dict: + sub_kls = klass.__args__[1] + obj = json.loads(data) if isinstance(data, str) else data + return {k: ApiResponse.deserialize(sub_kls, v) for k, v in obj.items()} + if isinstance(klass, str): if klass.startswith("List["): sub_kls = match(r"List\[(.*)]", klass) diff --git a/clients/algoliasearch-client-python/algoliasearch/http/sse.py b/clients/algoliasearch-client-python/algoliasearch/http/sse.py new file mode 100644 index 00000000000..765f23210a0 --- /dev/null +++ b/clients/algoliasearch-client-python/algoliasearch/http/sse.py @@ -0,0 +1,302 @@ +"""WHATWG-compliant Server-Sent Events parser. + +Three-layer architecture: + Layer 1: _iter_lines / _aiter_lines — byte stream to text lines + Layer 2: SSEDecoder — text lines to ServerSentEvent objects + Layer 3: iter_sse_events / aiter_sse_events — top-level composers + +Reference: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation +""" + +import codecs +from dataclasses import dataclass +from typing import ( + Any, + AsyncIterable, + AsyncIterator, + Dict, + Generic, + Iterable, + Iterator, + List, + Optional, +) + +from typing_extensions import TypeVar + +_MAX_LINE_SIZE = 10 * 1024 * 1024 # 10 MB +_BOM = "\ufeff" + +T = TypeVar("T", default=Dict[str, Any]) + + +@dataclass +class ServerSentEvent: + """A single SSE event dispatched from the event stream.""" + + data: str + event: str + id: Optional[str] = None + retry: Optional[int] = None + + +@dataclass +class StreamEvent(Generic[T]): + """Wrapper for a parsed SSE event yielded by the typed ``*_stream`` methods. + + ``data`` is the deserialized payload when parsing succeeds, ``None`` otherwise. + ``raw`` is the original :class:`ServerSentEvent` (always present). + ``error`` is set when JSON parsing / deserialization of ``event.data`` failed. + """ + + data: Optional[T] + raw: ServerSentEvent + error: Optional[Exception] = None + + +# --------------------------------------------------------------------------- +# Layer 2 — SSEDecoder (shared between sync and async) +# --------------------------------------------------------------------------- + + +class SSEDecoder: + """Decodes text lines into ServerSentEvent objects per the WHATWG spec. + + Feed lines one at a time via :meth:`decode`. A blank line triggers event + dispatch if the data buffer is non-empty. ``_last_event_id`` persists + across dispatches; ``_event_type`` resets to ``""`` after each dispatch. + """ + + def __init__(self) -> None: + self._data: List[str] = [] + self._event_type: str = "" + self._last_event_id: Optional[str] = None + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + """Process a single line. Returns a :class:`ServerSentEvent` on + dispatch (blank line with non-empty data buffer), or ``None``.""" + + # Blank line → dispatch if data buffer is non-empty + if not line: + # eventType resets on EVERY blank line per WHATWG spec, + # regardless of whether we actually dispatch an event + current_event_type = self._event_type + self._event_type = "" + + if not self._data: + return None + + event = ServerSentEvent( + data="\n".join(self._data), + event=current_event_type, + id=self._last_event_id, + retry=self._retry, + ) + + # Reset per WHATWG spec — _last_event_id and _retry persist + self._data = [] + + return event + + # Comment line (starts with ':') + if line.startswith(":"): + return None + + # Field line + if ":" in line: + field, _, value = line.partition(":") + # Strip exactly one leading space if present + if value.startswith(" "): + value = value[1:] + else: + field = line + value = "" + + if field == "data": + self._data.append(value) + elif field == "event": + self._event_type = value + elif field == "id": + # If the value contains a NULL character, ignore the field entirely + if "\x00" not in value: + self._last_event_id = value + elif field == "retry": + # Only accept if value is non-empty and consists entirely of + # ASCII digits. Do NOT use int() directly — it accepts negatives, + # whitespace, underscores, etc. + if value and value.isdigit(): + self._retry = int(value) + + return None + + +# --------------------------------------------------------------------------- +# Layer 1 — Line iterators (byte stream → text lines) +# --------------------------------------------------------------------------- + + +def _iter_lines(byte_iter: Iterable[bytes]) -> Iterator[str]: + """Decode a byte stream into individual text lines. + + Handles ``\\r``, ``\\n``, and ``\\r\\n`` line endings including the edge + case where ``\\r\\n`` is split across two chunks. Strips the BOM from the + very first line only. Raises :class:`ValueError` if the internal buffer + exceeds 10 MB. + """ + decoder = codecs.getincrementaldecoder("utf-8")() + buf = "" + trailing_cr = False + first_line = True + + for raw_chunk in byte_iter: + chunk = decoder.decode(raw_chunk) + + if not chunk: + continue + + # Handle \r\n split across chunk boundary + if trailing_cr: + if chunk[0] == "\n": + chunk = chunk[1:] + trailing_cr = False + if not chunk: + continue + + # Track trailing \r for next chunk's \r\n check + if chunk[-1] == "\r": + trailing_cr = True + + # Normalize all line endings to \n, then split + lines = chunk.replace("\r\n", "\n").replace("\r", "\n").split("\n") + + # First segment extends the buffer; remaining segments are after + # line breaks (i.e. the buffer holds a complete line). + buf += lines[0] + + for i in range(1, len(lines)): + line = buf + if first_line: + if line.startswith(_BOM): + line = line[1:] + first_line = False + yield line + buf = lines[i] + + if len(buf) > _MAX_LINE_SIZE: + raise ValueError("SSE line exceeds maximum buffer size of 10 MB") + + # Flush the incremental UTF-8 decoder + remaining = decoder.decode(b"", final=True) + if remaining: + buf += remaining + + # At end-of-stream with trailing_cr the \r already caused a line split, + # so only yield if buf accumulated content after that split. + if trailing_cr: + if buf: + line = buf + if first_line: + if line.startswith(_BOM): + line = line[1:] + yield line + return + + if buf: + line = buf + if first_line: + if line.startswith(_BOM): + line = line[1:] + yield line + + +async def _aiter_lines(byte_iter: AsyncIterable[bytes]) -> AsyncIterator[str]: + """Async variant of :func:`_iter_lines`.""" + decoder = codecs.getincrementaldecoder("utf-8")() + buf = "" + trailing_cr = False + first_line = True + + async for raw_chunk in byte_iter: + chunk = decoder.decode(raw_chunk) + + if not chunk: + continue + + if trailing_cr: + if chunk[0] == "\n": + chunk = chunk[1:] + trailing_cr = False + if not chunk: + continue + + if chunk[-1] == "\r": + trailing_cr = True + + lines = chunk.replace("\r\n", "\n").replace("\r", "\n").split("\n") + + buf += lines[0] + + for i in range(1, len(lines)): + line = buf + if first_line: + if line.startswith(_BOM): + line = line[1:] + first_line = False + yield line + buf = lines[i] + + if len(buf) > _MAX_LINE_SIZE: + raise ValueError("SSE line exceeds maximum buffer size of 10 MB") + + remaining = decoder.decode(b"", final=True) + if remaining: + buf += remaining + + if trailing_cr: + if buf: + line = buf + if first_line: + if line.startswith(_BOM): + line = line[1:] + yield line + return + + if buf: + line = buf + if first_line: + if line.startswith(_BOM): + line = line[1:] + yield line + + +# --------------------------------------------------------------------------- +# Layer 3 — Top-level composers +# --------------------------------------------------------------------------- + + +def iter_sse_events(byte_iter: Iterable[bytes]) -> Iterator[ServerSentEvent]: + """Parse a byte stream into SSE events (sync). + + Pipes bytes through :func:`_iter_lines` then feeds each line to + :class:`SSEDecoder`, yielding every dispatched event. + """ + decoder = SSEDecoder() + for line in _iter_lines(byte_iter): + event = decoder.decode(line) + if event is not None: + yield event + + +async def aiter_sse_events( + byte_iter: AsyncIterable[bytes], +) -> AsyncIterator[ServerSentEvent]: + """Parse a byte stream into SSE events (async). + + Async variant of :func:`iter_sse_events`. + """ + decoder = SSEDecoder() + async for line in _aiter_lines(byte_iter): + event = decoder.decode(line) + if event is not None: + yield event diff --git a/clients/algoliasearch-client-python/algoliasearch/http/transporter.py b/clients/algoliasearch-client-python/algoliasearch/http/transporter.py index 0adf636f1a0..f1a3074e4aa 100644 --- a/clients/algoliasearch-client-python/algoliasearch/http/transporter.py +++ b/clients/algoliasearch-client-python/algoliasearch/http/transporter.py @@ -1,7 +1,7 @@ from asyncio import TimeoutError from gzip import compress as gzip_compress -from json import loads -from typing import List, Optional +from json import dumps, loads +from typing import AsyncIterator, List, Optional from aiohttp import ClientSession, ClientTimeout, TCPConnector @@ -15,6 +15,7 @@ from algoliasearch.http.hosts import Host from algoliasearch.http.request_options import RequestOptions from algoliasearch.http.retry import RetryOutcome, RetryStrategy +from algoliasearch.http.sse import ServerSentEvent, aiter_sse_events from algoliasearch.http.verb import Verb @@ -119,6 +120,61 @@ async def request( "Unreachable hosts. If the error persists, please visit our help center https://alg.li/support-unreachable-hosts or reach out to the Algolia Support team: https://alg.li/support" ) + async def request_stream( + self, + verb: Verb, + path: str, + request_options: RequestOptions, + use_read_transporter: bool, + ) -> AsyncIterator[ServerSentEvent]: + if self._session is None: + self._session = ClientSession( + connector=TCPConnector(use_dns_cache=False), trust_env=True + ) + + request_options.headers["accept"] = "text/event-stream" + + query_parameters = self.prepare( + request_options, verb == Verb.GET or use_read_transporter + ) + + path = self.build_path(path, query_parameters) + + valid_hosts = self._retry_strategy.valid_hosts(self._hosts) + if not valid_hosts: + raise AlgoliaUnreachableHostException( + "No hosts available for streaming request" + ) + host = valid_hosts[0] + url = self.build_url(host, path) + proxy = self.get_proxy(url) + + connect_timeout = request_options.timeouts["connect"] / 1000 + request_timeout = self._timeout / 1000 + + timeout_config = ClientTimeout( + connect=connect_timeout, sock_read=request_timeout + ) + + resp = await self._session.request( + method=verb, + url=url, + headers=request_options.headers, + data=request_options.data, + proxy=proxy, + timeout=timeout_config, + ) + + try: + if resp.status >= 400: + error_text = await resp.text() + raise RequestException(error_text, resp.status) + + async for event in aiter_sse_events(resp.content.iter_any()): + yield event + finally: + resp.release() + class EchoTransporter(Transporter): def __init__(self, config: BaseConfig) -> None: @@ -156,3 +212,30 @@ async def request( data=request_options.data, raw_data=request_options.data, # type: ignore ) + + async def request_stream( + self, + verb: Verb, + path: str, + request_options: RequestOptions, + use_read_transporter: bool, + ) -> AsyncIterator[ServerSentEvent]: + self.prepare(request_options, verb == Verb.GET or use_read_transporter) + + data = dumps( + { + "verb": verb, + "path": path, + "status_code": 200, + "host": self._retry_strategy.valid_hosts(self._hosts)[0].url, + "timeouts": { + "connect": request_options.timeouts["connect"], + "response": self._timeout, + }, + "query_parameters": request_options.query_parameters, + "headers": dict(request_options.headers), + "data": request_options.data, + } + ) + + yield ServerSentEvent(data=data, event="", id=None, retry=None) diff --git a/clients/algoliasearch-client-python/algoliasearch/http/transporter_sync.py b/clients/algoliasearch-client-python/algoliasearch/http/transporter_sync.py index c468a61ffb3..b2f9ea69a1b 100644 --- a/clients/algoliasearch-client-python/algoliasearch/http/transporter_sync.py +++ b/clients/algoliasearch-client-python/algoliasearch/http/transporter_sync.py @@ -1,11 +1,12 @@ from gzip import compress as gzip_compress -from json import loads +from json import dumps, loads from sys import version_info +from typing import Iterator, Optional from requests import Request, Session, Timeout if version_info >= (3, 11): - from typing import List, Optional, Self + from typing import List, Self else: from typing_extensions import List, Self @@ -22,6 +23,7 @@ from algoliasearch.http.hosts import Host from algoliasearch.http.request_options import RequestOptions from algoliasearch.http.retry import RetryOutcome, RetryStrategy +from algoliasearch.http.sse import ServerSentEvent, iter_sse_events from algoliasearch.http.verb import Verb @@ -131,6 +133,63 @@ def request( "Unreachable hosts. If the error persists, please visit our help center https://alg.li/support-unreachable-hosts or reach out to the Algolia Support team: https://alg.li/support" ) + def request_stream( + self, + verb: Verb, + path: str, + request_options: RequestOptions, + use_read_transporter: bool, + ) -> Iterator[ServerSentEvent]: + if self._session is None: + self._session = Session() + self._session.mount("https://", HTTPAdapter(max_retries=Retry(connect=0))) + + request_options.headers["accept"] = "text/event-stream" + + query_parameters = self.prepare( + request_options, verb == Verb.GET or use_read_transporter + ) + + path = self.build_path(path, query_parameters) + + valid_hosts = self._retry_strategy.valid_hosts(self._hosts) + if not valid_hosts: + raise AlgoliaUnreachableHostException( + "No hosts available for streaming request" + ) + host = valid_hosts[0] + url = self.build_url(host, path) + proxies = self.get_proxies(url) + + req = self._session.prepare_request( + Request( + method=verb, + url=url, + headers=request_options.headers, + data=request_options.data, + ) + ) + + connect_timeout = request_options.timeouts["connect"] / 1000 + read_timeout = self._timeout / 1000 + + resp = self._session.send( + req, + timeout=(connect_timeout, read_timeout), + proxies=proxies, + stream=True, + ) + + try: + if resp.status_code >= 400: + error_text = resp.text + raise RequestException(error_text, resp.status_code) + + for event in iter_sse_events(resp.iter_content(chunk_size=None)): + yield event + finally: + resp.close() + class EchoTransporterSync(TransporterSync): def __init__(self, config: BaseConfig) -> None: @@ -168,3 +227,30 @@ def request( data=request_options.data, raw_data=request_options.data, # type: ignore ) + + def request_stream( + self, + verb: Verb, + path: str, + request_options: RequestOptions, + use_read_transporter: bool, + ) -> Iterator[ServerSentEvent]: + self.prepare(request_options, verb == Verb.GET or use_read_transporter) + + data = dumps( + { + "verb": verb, + "path": path, + "status_code": 200, + "host": self._retry_strategy.valid_hosts(self._hosts)[0].url, + "timeouts": { + "connect": request_options.timeouts["connect"], + "response": self._timeout, + }, + "query_parameters": request_options.query_parameters, + "headers": dict(request_options.headers), + "data": request_options.data, + } + ) + + yield ServerSentEvent(data=data, event="", id=None, retry=None) diff --git a/clients/algoliasearch-client-python/requirements.txt b/clients/algoliasearch-client-python/requirements.txt index e69de29bb2d..b1783a65964 100644 --- a/clients/algoliasearch-client-python/requirements.txt +++ b/clients/algoliasearch-client-python/requirements.txt @@ -0,0 +1,6 @@ +argcomplete==3.6.3 +click==8.3.3 +packaging==26.2 +pipx==1.11.1 +platformdirs==4.9.6 +userpath==1.9.2 diff --git a/clients/algoliasearch-client-ruby/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-ruby/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-ruby/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-ruby/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-scala/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-scala/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-scala/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-scala/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/config/HttpRequest.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/config/HttpRequest.scala index 7236f1179aa..93e4c045368 100644 --- a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/config/HttpRequest.scala +++ b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/config/HttpRequest.scala @@ -100,6 +100,13 @@ object HttpRequest { this } + def withHeader(key: String, value: Option[Any]): HttpRequest.Builder = { + value match { + case Some(param) => withHeader(key, param) + case None => this + } + } + def withHeaders(headers: Map[String, Any]): HttpRequest.Builder = { for ((key, value) <- headers) withHeader(key, value) diff --git a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/internal/HttpRequester.scala b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/internal/HttpRequester.scala index e152972ba36..4482912d36c 100644 --- a/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/internal/HttpRequester.scala +++ b/clients/algoliasearch-client-scala/src/main/scala/algoliasearch/internal/HttpRequester.scala @@ -161,8 +161,8 @@ private[algoliasearch] class HttpRequester private ( // Handle unsuccessful responses. if (!response.isSuccessful) throw AlgoliaApiException(message = response.message, httpErrorCode = response.code) - // Deserialize and return the response. - jsonSerializer.deserialize[T](response.body.byteStream) + if (response.code == 204) null.asInstanceOf[T] + else jsonSerializer.deserialize[T](response.body.byteStream) } catch { case exception: IOException => throw AlgoliaClientException(cause = exception) case exception: AlgoliaApiException => diff --git a/clients/algoliasearch-client-swift/.github/ISSUE_TEMPLATE/Bug_report.yml b/clients/algoliasearch-client-swift/.github/ISSUE_TEMPLATE/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/clients/algoliasearch-client-swift/.github/ISSUE_TEMPLATE/Bug_report.yml +++ b/clients/algoliasearch-client-swift/.github/ISSUE_TEMPLATE/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/clients/algoliasearch-client-swift/AlgoliaSearchClient.podspec b/clients/algoliasearch-client-swift/AlgoliaSearchClient.podspec index 7924a701d6b..f747f83bd62 100644 --- a/clients/algoliasearch-client-swift/AlgoliaSearchClient.podspec +++ b/clients/algoliasearch-client-swift/AlgoliaSearchClient.podspec @@ -26,6 +26,10 @@ Pod::Spec.new do |s| subs.source_files = 'Sources/AbtestingV3/**/*.swift' subs.dependency 'AlgoliaSearchClient/Core' end + s.subspec 'AgentStudio' do |subs| + subs.source_files = 'Sources/AgentStudio/**/*.swift' + subs.dependency 'AlgoliaSearchClient/Core' + end s.subspec 'Analytics' do |subs| subs.source_files = 'Sources/Analytics/**/*.swift' subs.dependency 'AlgoliaSearchClient/Core' diff --git a/clients/algoliasearch-client-swift/Package.swift b/clients/algoliasearch-client-swift/Package.swift index 4a1e13c813c..99e33617a47 100644 --- a/clients/algoliasearch-client-swift/Package.swift +++ b/clients/algoliasearch-client-swift/Package.swift @@ -1,6 +1,5 @@ // swift-tools-version: 5.9 -// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on -// https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. import PackageDescription @@ -18,85 +17,86 @@ var products: [Product] = [] #if os(Linux) extraPackageDependencies.append(contentsOf: [ .package(url: "https://github.com/apple/swift-crypto.git", from: "3.2.0"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.5.4"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.5.4") ]) extraTargetDependencies.append(contentsOf: [ .product(name: "Crypto", package: "swift-crypto"), - .product(name: "Logging", package: "swift-log"), + .product(name: "Logging", package: "swift-log") ]) #endif #if os(Linux) - targets.append(contentsOf: [ - .systemLibrary(name: "zlib", pkgConfig: "zlib", providers: [.apt(["zlib1g-dev"])]), - .target( - name: "AlgoliaCore", - dependencies: [ - "zlib", - ] + extraTargetDependencies, - path: "Sources/Core" - ), - ]) + targets.append(contentsOf: [ + .systemLibrary(name: "zlib", pkgConfig: "zlib", providers: [.apt(["zlib1g-dev"])]), + .target( + name: "AlgoliaCore", + dependencies: [ + "zlib", + ] + extraTargetDependencies, + path: "Sources/Core" + ) + ]) #else - targets.append( - .target( - name: "AlgoliaCore", - dependencies: extraTargetDependencies, - path: "Sources/Core" - ) + targets.append( + .target( + name: "AlgoliaCore", + dependencies: extraTargetDependencies, + path: "Sources/Core" ) + ) #endif products.append( - .library( - name: "AlgoliaCore", - targets: ["AlgoliaCore"] - ) + .library( + name: "AlgoliaCore", + targets: ["AlgoliaCore"] + ) ) [ - "Abtesting", - "AbtestingV3", - "Analytics", - "Composition", - "Ingestion", - "Insights", - "Monitoring", - "Personalization", - "QuerySuggestions", - "Recommend", - "Search", -].enumerated().forEach { _, library in - targets.append( - .target( - name: "Algolia\(library)", - dependencies: [ - .target(name: "AlgoliaCore"), - ] + extraTargetDependencies, - path: "Sources/\(library)", - resources: [ - .copy("../../PrivacyInfo.xcprivacy"), - ] - ) + "Abtesting", + "AbtestingV3", + "AgentStudio", + "Analytics", + "Composition", + "Ingestion", + "Insights", + "Monitoring", + "Personalization", + "QuerySuggestions", + "Recommend", + "Search" +].enumerated().forEach { (_, library) in + targets.append( + .target( + name: "Algolia\(library)", + dependencies: [ + .target(name: "AlgoliaCore"), + ] + extraTargetDependencies, + path: "Sources/\(library)", + resources: [ + .copy("../../PrivacyInfo.xcprivacy") + ] ) + ) - products.append( - .library( - name: "Algolia\(library)", - targets: ["Algolia\(library)"] - ) + products.append( + .library( + name: "Algolia\(library)", + targets: ["Algolia\(library)"] ) + ) } let package = Package( - name: "AlgoliaSearchClient", - platforms: [ + name: "AlgoliaSearchClient", + platforms: [ .iOS(.v14), .macOS(macOSVersion), .tvOS(.v14), .watchOS(.v7), - ], - products: products, - dependencies: extraPackageDependencies, - targets: targets + ], + products: products, + dependencies: extraPackageDependencies, + targets: targets ) diff --git a/clients/algoliasearch-client-swift/Sources/Core/Helpers/Version.swift b/clients/algoliasearch-client-swift/Sources/Core/Helpers/Version.swift index e043a230afb..68dcbae17d9 100644 --- a/clients/algoliasearch-client-swift/Sources/Core/Helpers/Version.swift +++ b/clients/algoliasearch-client-swift/Sources/Core/Helpers/Version.swift @@ -1,52 +1,55 @@ -// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on -// https://github.com/algolia/api-clients-automation. DO NOT EDIT. +// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT. public struct Version { - public let major: Int - public let minor: Int - public let patch: Int - public let prereleaseIdentifier: String? - - public init(major: Int, minor: Int, patch: Int = 0, prereleaseIdentifier: String? = nil) { - self.major = major - self.minor = minor - self.patch = patch - self.prereleaseIdentifier = prereleaseIdentifier - } - public init(version: String) { - let components = version.components(separatedBy: ".") + public let major: Int + public let minor: Int + public let patch: Int + public let prereleaseIdentifier: String? + + public init(major: Int, minor: Int, patch: Int = 0, prereleaseIdentifier: String? = nil) { + self.major = major + self.minor = minor + self.patch = patch + self.prereleaseIdentifier = prereleaseIdentifier + } - guard components.count >= 3 else { - fatalError("version is not formatted correctly") - } + public init(version: String) { + let components = version.components(separatedBy: ".") + + guard components.count >= 3 else { + fatalError("version is not formatted correctly") + } - self.major = Int(components[0]) ?? 0 - self.minor = Int(components[1]) ?? 0 + self.major = Int(components[0]) ?? 0 + self.minor = Int(components[1]) ?? 0 - if components.count == 4 { - let prereleaseComponents = components[2].components(separatedBy: "-") + if components.count == 4 { + let prereleaseComponents = components[2].components(separatedBy: "-") - self.patch = Int(prereleaseComponents[0]) ?? 0 - self.prereleaseIdentifier = "\(prereleaseComponents[1]).\(components[3])" - } else { - self.patch = Int(components[2]) ?? 0 - self.prereleaseIdentifier = nil - } + self.patch = Int(prereleaseComponents[0]) ?? 0 + self.prereleaseIdentifier = "\(prereleaseComponents[1]).\(components[3])" + } else { + self.patch = Int(components[2]) ?? 0 + self.prereleaseIdentifier = nil } + } + } extension Version: CustomStringConvertible { - public var description: String { - let main = [major, minor, patch].map(String.init).joined(separator: ".") - if let prereleaseIdentifier { - return main + "-\(prereleaseIdentifier)" - } else { - return main - } + + public var description: String { + let main = [major, minor, patch].map(String.init).joined(separator: ".") + if let prereleaseIdentifier = prereleaseIdentifier { + return main + "-\(prereleaseIdentifier)" + } else { + return main } + } + } -public extension Version { - static let current: Version = .init(version: "9.43.1") +extension Version { + public static let current: Version = .init(version: "9.43.1") } diff --git a/config/clients.config.json b/config/clients.config.json index d9227dd9630..39224c6aa8a 100644 --- a/config/clients.config.json +++ b/config/clients.config.json @@ -4,6 +4,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -80,6 +81,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -114,6 +116,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -201,6 +204,11 @@ { "name": "search", "output": "clients/algoliasearch-client-javascript/packages/client-search" + }, + { + "name": "agent-studio", + "output": "clients/algoliasearch-client-javascript/packages/agent-studio", + "isStandaloneClient": true } ], "folder": "clients/algoliasearch-client-javascript", @@ -221,6 +229,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -250,6 +259,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -284,6 +294,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -320,6 +331,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -353,6 +365,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -382,6 +395,7 @@ "clients": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", diff --git a/config/clients.schema.json b/config/clients.schema.json index 8d68968aa8a..dc896048117 100644 --- a/config/clients.schema.json +++ b/config/clients.schema.json @@ -13,6 +13,7 @@ "enum": [ "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", @@ -37,6 +38,7 @@ "algoliasearch", "abtesting", "abtesting-v3", + "agent-studio", "analytics", "composition", "ingestion", diff --git a/config/generation.config.mjs b/config/generation.config.mjs index c2f950db91e..beee2e7c9f0 100644 --- a/config/generation.config.mjs +++ b/config/generation.config.mjs @@ -28,6 +28,7 @@ export const patterns = [ 'tests/output/csharp/src/Algolia.Search.Tests.csproj', '!tests/output/csharp/src/TimeoutIntegrationTests.cs', + '!tests/output/csharp/src/Utils/**', // Dart '!clients/algoliasearch-client-dart/**', @@ -64,6 +65,7 @@ export const patterns = [ 'tests/output/java/build.gradle', '!tests/output/java/src/test/java/com/algolia/manual/**', + '!tests/output/java/src/test/java/com/algolia/utils/**', // JavaScript '!clients/algoliasearch-client-javascript/*', @@ -92,6 +94,8 @@ export const patterns = [ 'clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/api/**', 'clients/algoliasearch-client-kotlin/client/src/commonMain/kotlin/com/algolia/client/model/**', + '!tests/output/kotlin/src/commonTest/kotlin/com/algolia/utils/TestHelpers.kt', + // PHP '!clients/algoliasearch-client-php/**', 'clients/algoliasearch-client-php/lib/Api/*', diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java index 788cba5aba3..85d3cfd08bd 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaGoGenerator.java @@ -143,6 +143,15 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List 1) { + op.pathParams.sort(Comparator.comparingInt(p -> op.path.indexOf("{" + p.baseName + "}"))); + } + } + return operations; } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java index 700c106f0f5..bde5e04270b 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaJavascriptGenerator.java @@ -182,10 +182,8 @@ private void setDefaultGeneratorOptions() { dependency.put("dependencyPackage", "@algolia/" + name); dependency.put("dependencyVersion", version); dependency.put("withInitMethod", !name.contains("search")); - dependency.put( - "dependencyHasRegionalHosts", - !name.contains("search") && !name.contains("recommend") && !name.contains("monitoring") && !name.startsWith("composition") - ); + var clientsWithoutRegion = List.of("search", "recommend", "monitoring", "composition", "agent-studio"); + dependency.put("dependencyHasRegionalHosts", clientsWithoutRegion.stream().noneMatch(name::contains)); dependencies.add(dependency); } diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java index c873bb585dc..edce34ab33d 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaKotlinGenerator.java @@ -168,9 +168,52 @@ private List extractSegments(String input) { return segments; } + private record SavedVarLists( + List vars, + List allVars, + List optionalVars, + List requiredVars, + List readOnlyVars, + List readWriteVars + ) { + SavedVarLists(CodegenModel model) { + this( + new ArrayList<>(model.vars), + new ArrayList<>(model.allVars), + new ArrayList<>(model.optionalVars), + new ArrayList<>(model.requiredVars), + new ArrayList<>(model.readOnlyVars), + new ArrayList<>(model.readWriteVars) + ); + } + } + @Override public Map postProcessAllModels(Map objs) { + // Save all var lists before super strips discriminator properties from child models + var savedModels = new HashMap(); + for (var entry : objs.entrySet()) { + var model = entry.getValue().getModels().get(0).getModel(); + savedModels.put(entry.getKey(), new SavedVarLists(model)); + } + Map models = super.postProcessAllModels(objs); + + // Restore discriminator properties stripped by KotlinClientCodegen + for (var entry : models.entrySet()) { + var model = entry.getValue().getModels().get(0).getModel(); + var saved = savedModels.get(entry.getKey()); + if (saved != null && model.vars.size() < saved.vars().size()) { + model.vars = saved.vars(); + model.allVars = saved.allVars(); + model.optionalVars = saved.optionalVars(); + model.requiredVars = saved.requiredVars(); + model.readOnlyVars = saved.readOnlyVars(); + model.readWriteVars = saved.readWriteVars(); + } + } + + replaceFreeFormMaps(models); OneOf.updateModelsOneOf(models, modelPackage); GenericPropagator.propagateGenericsToModels(models, true); OneOf.addOneOfMetadata(models); @@ -178,6 +221,43 @@ public Map postProcessAllModels(Map objs) return models; } + private static final String FREE_FORM_MAP = "Map"; + private static final String JSON_OBJECT = "JsonObject"; + + private static void replaceFreeFormMaps(Map models) { + for (ModelsMap modelContainer : models.values()) { + CodegenModel model = modelContainer.getModels().get(0).getModel(); + + if (model.vars != null) { + for (CodegenProperty prop : model.vars) { + replaceFreeFormType(prop); + } + } + + if (model.oneOf != null && model.oneOf.stream().anyMatch(t -> t.contains(FREE_FORM_MAP))) { + Set replaced = new LinkedHashSet<>(); + for (String t : model.oneOf) { + replaced.add(t.contains(FREE_FORM_MAP) ? t.replace(FREE_FORM_MAP, JSON_OBJECT) : t); + } + model.oneOf.clear(); + model.oneOf.addAll(replaced); + } + + if (model.getComposedSchemas() != null && model.getComposedSchemas().getOneOf() != null) { + for (CodegenProperty prop : model.getComposedSchemas().getOneOf()) { + replaceFreeFormType(prop); + } + } + } + } + + private static void replaceFreeFormType(CodegenProperty prop) { + if (prop.datatypeWithEnum != null && prop.datatypeWithEnum.contains(FREE_FORM_MAP)) { + prop.datatypeWithEnum = prop.datatypeWithEnum.replace(FREE_FORM_MAP, JSON_OBJECT); + prop.dataType = prop.dataType.replace(FREE_FORM_MAP, JSON_OBJECT); + } + } + private static void jsonParent(Map models) { for (ModelsMap modelContainer : models.values()) { CodegenModel model = modelContainer.getModels().get(0).getModel(); diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java index e8bd30af26f..7c8590e5abe 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaPhpGenerator.java @@ -50,6 +50,7 @@ public void processOpts() { supportingFiles.removeIf(file -> file.getTemplateFile().equals("ApiException.mustache")); supportingFiles.removeIf(file -> file.getTemplateFile().equals("ObjectSerializer.mustache")); supportingFiles.removeIf(file -> file.getTemplateFile().equals("ModelInterface.mustache")); + supportingFiles.removeIf(file -> file.getTemplateFile().equals("FormDataProcessor.mustache")); supportingFiles.add(new SupportingFile("client_config.mustache", "lib/Configuration", getClientName(client) + "Config.php")); supportingFiles.add(new SupportingFile("Algolia.mustache", "lib", "Algolia.php")); diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java index a0db215c3aa..1cf821a2511 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaPythonGenerator.java @@ -32,6 +32,9 @@ public void processOpts() { setApiNameSuffix(Helpers.API_SUFFIX); + // Rename models that conflict with pydantic's built-in names + modelNameMapping.put("ValidationError", "AgentStudioValidationError"); + String packageName = Helpers.toSnakeCase(CLIENT); setPackageName(packageName); setApiPackage(""); diff --git a/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java b/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java index 03ca579ce6f..03313ef03b3 100644 --- a/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/AlgoliaScalaGenerator.java @@ -87,6 +87,11 @@ public void processOpts() { super.processOpts(); setApiNameSuffix(Helpers.API_SUFFIX); + // 'wait' is a final method on Object — backtick escaping alone doesn't help, + // the field must be renamed to avoid the conflict entirely. + reservedWords.add("wait"); + reservedWordsMappings.put("wait", "_wait"); + // Generation notice, added on every generated files Helpers.setGenerationBanner(additionalProperties); @@ -257,7 +262,7 @@ public Map postProcessAllModels(Map objs) if (codegenModel.vars != null) { var hasUnescapedProperty = false; for (var property : codegenModel.vars) { - if (!property.name.equals(property.baseName) && !this.isReservedWord(property.baseName)) { + if (!property.name.equals(property.baseName) && !property.name.equals("`" + property.baseName + "`")) { property.vendorExtensions.put("x-unescaped-name", property.baseName); hasUnescapedProperty = true; } diff --git a/generators/src/main/java/com/algolia/codegen/cts/AlgoliaCTSGenerator.java b/generators/src/main/java/com/algolia/codegen/cts/AlgoliaCTSGenerator.java index a156d6a436d..9939bbd60b1 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/AlgoliaCTSGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/cts/AlgoliaCTSGenerator.java @@ -152,6 +152,7 @@ public Map postProcessSupportingFileData(Map obj if (hasRegionalHost) { bundle.put("defaultRegion", regionVariable.defaultValue); } + Helpers.generateServers(servers, bundle); bundle.put("isSyncClient", false); // special lambda for dynamic templates bundle.put("dynamicTemplate", new DynamicTemplateLambda(this)); diff --git a/generators/src/main/java/com/algolia/codegen/cts/manager/CTSManager.java b/generators/src/main/java/com/algolia/codegen/cts/manager/CTSManager.java index 07b001b723e..9ea4267c196 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/manager/CTSManager.java +++ b/generators/src/main/java/com/algolia/codegen/cts/manager/CTSManager.java @@ -43,4 +43,8 @@ public default void addDataToBundle(Map bundle) throws Generator public default void addMustacheLambdas(Map lambdas) { // NO-OP } + + public default String resolveMethodName(String operationId) { + return operationId; + } } diff --git a/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java b/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java index 2ee3bc8cd15..6398d227d08 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java +++ b/generators/src/main/java/com/algolia/codegen/cts/manager/GoCTSManager.java @@ -40,6 +40,14 @@ public void addTestsSupportingFiles(List supportingFiles) { supportingFiles.add(new SupportingFile("tests/go.mod.mustache", "tests/output/go", "go.mod")); } + @Override + public String resolveMethodName(String operationId) { + if (operationId.equals("getConfiguration")) { + return "getApplicationConfiguration"; + } + return operationId; + } + @Override public void addSnippetsSupportingFiles(List supportingFiles, String output) { supportingFiles.add(new SupportingFile("snippets/.golangci.mustache", output + "/go/.golangci.yml")); diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java b/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java index 2f7bdb38c08..82583792f06 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/ParametersWithDataType.java @@ -70,22 +70,39 @@ public void enhanceParameters(Map parameters, Map param : parameters.entrySet()) { - CodegenParameter specParam = null; - if (operation != null) { - for (CodegenParameter sp : operation.allParams) { - if (sp.paramName.equals(param.getKey())) { - specParam = sp; - break; - } - } - if (specParam == null) { - throw new CTSException("Parameter " + param.getKey() + " not found in the root parameter"); + if (operation != null) { + // Iterate in operation.allParams order to ensure generated method calls + // pass arguments in the correct positional order (matching the method signature), + // regardless of JSON key order in the CTS test definition. + int matched = 0; + for (CodegenParameter sp : operation.allParams) { + if (!parameters.containsKey(sp.paramName)) { + continue; } + matched++; + Object paramValue = parameters.get(sp.paramName); + Map paramWithType = traverseParams(sp.paramName, paramValue, sp, "", 0, false); + parametersWithDataType.add(paramWithType); + parametersWithDataTypeMap.put((String) paramWithType.get("key"), paramWithType); + } + if (matched != parameters.size()) { + Set specParamNames = operation.allParams + .stream() + .map(sp -> sp.paramName) + .collect(Collectors.toSet()); + String unknown = parameters + .keySet() + .stream() + .filter(p -> !specParamNames.contains(p)) + .collect(Collectors.joining(", ")); + throw new CTSException("Unknown parameter(s) [" + unknown + "] not found in the root parameter"); + } + } else { + for (Entry param : parameters.entrySet()) { + Map paramWithType = traverseParams(param.getKey(), param.getValue(), null, "", 0, false); + parametersWithDataType.add(paramWithType); + parametersWithDataTypeMap.put((String) paramWithType.get("key"), paramWithType); } - Map paramWithType = traverseParams(param.getKey(), param.getValue(), specParam, "", 0, false); - parametersWithDataType.add(paramWithType); - parametersWithDataTypeMap.put((String) paramWithType.get("key"), paramWithType); } } } else { @@ -174,7 +191,7 @@ private Map traverseParams( } else if (spec.getIsArray()) { handleArray(paramName, param, testOutput, spec, depth); } else if (spec.getIsEnum()) { - handleEnum(param, testOutput); + handleEnum(param, testOutput, spec); } else if (spec.getIsModel() || isCodegenModel) { // recursive object handleModel(paramName, param, testOutput, spec, baseType, parent, depth, isParentFreeFormObject, isRequired != null && isRequired); @@ -281,8 +298,9 @@ private void handleArray(String paramName, Object param, Map tes testOutput.put("value", values); } - private void handleEnum(Object param, Map testOutput) { + private void handleEnum(Object param, Map testOutput, IJsonSchemaValidationProperties spec) { testOutput.put("isEnum", true); + testOutput.put("isIntegerEnum", spec.getIsInteger() || spec.getIsNumber()); testOutput.put("value", param); } @@ -495,6 +513,19 @@ private void handleMap(String paramName, Object param, Map testO } testOutput.put("isFreeFormObject", true); + // isSimpleObject=true routes to buildJsonObject (Kotlin) instead of mapOf. + // In Kotlin, free-form model properties are rendered as JsonObject (via + // data_class_field_type.mustache), + // which requires buildJsonObject { ... }. But API method parameters (CodegenParameter) like + // query params + // are rendered as Map, which requires mapOf(...). Other languages (Scala, Go, C#) + // use Map + // types for both cases, so this is Kotlin-specific. + boolean isSimpleObject = getTypeName(spec).equals("Object"); + if (language.equals("kotlin") && spec instanceof CodegenProperty && Boolean.TRUE.equals(spec.getIsFreeFormObject())) { + isSimpleObject = true; + } + testOutput.put("isSimpleObject", isSimpleObject); testOutput.put("value", values); } @@ -706,12 +737,26 @@ private IJsonSchemaValidationProperties findMatchingOneOf(Object param, CodegenM return bestOneOf; } - for (CodegenProperty prop : model.getComposedSchemas().getOneOf()) { - // find the correct list - if (param instanceof List && prop.getIsArray()) { - return prop; + if (param instanceof List) { + List arrayVariants = model.getComposedSchemas().getOneOf().stream().filter(CodegenProperty::getIsArray).toList(); + + if (arrayVariants.size() == 1) { + // Single array variant - return it directly (existing behavior) + return arrayVariants.get(0); } + if (arrayVariants.size() > 1) { + // Multiple array variants - inspect first element to disambiguate + List list = (List) param; + if (!list.isEmpty() && list.get(0) instanceof Map) { + return findBestArrayVariant(arrayVariants, (Map) list.get(0)); + } + // Empty list or non-Map elements - fall back to first + return arrayVariants.get(0); + } + } + + for (CodegenProperty prop : model.getComposedSchemas().getOneOf()) { // find the correct enum if (param instanceof String && prop.getIsEnumOrRef() && couldMatchEnum(param, prop)) { return prop; @@ -752,6 +797,88 @@ private IJsonSchemaValidationProperties findMatchingOneOf(Object param, CodegenM return maybeMatch; } + /** + * When a oneOf has multiple array variants, score each variant by checking how well its items + * type matches a sample element from the data. Uses the same required-field and property-counting + * heuristic as the Map-based oneOf resolution. + */ + @SuppressWarnings("unchecked") + private CodegenProperty findBestArrayVariant(List arrayVariants, Map sampleElement) { + CodegenProperty bestVariant = arrayVariants.get(0); + int bestScore = -1; + + for (CodegenProperty variant : arrayVariants) { + CodegenProperty items = variant.getItems(); + if (items == null) continue; + + String itemTypeName = items.getDataType(); + if (itemTypeName == null) continue; + + CodegenModel itemModel = models.get(itemTypeName); + if (itemModel == null) { + // Try lowercase first char + String lower = itemTypeName.substring(0, 1).toLowerCase() + itemTypeName.substring(1); + itemModel = models.get(lower); + } + if (itemModel == null) continue; + + int score; + if (!itemModel.oneOf.isEmpty() && itemModel.interfaceModels != null) { + // Items type is itself a oneOf (e.g. MessageV4 = UserMessageV4 | AssistantMessageV4) + // Find best sub-match score + score = scoreOneOfModelMatch(sampleElement, itemModel); + } else { + // Items type is a direct model + score = scoreDirectModelMatch(sampleElement, itemModel); + } + + if (score > bestScore) { + bestScore = score; + bestVariant = variant; + } + } + + return bestVariant; + } + + /** + * Score how well a Map matches against the best sub-variant of a oneOf model. Returns -1 if no + * sub-variant matches (all have missing required fields). + */ + private int scoreOneOfModelMatch(Map data, CodegenModel oneOfModel) { + int bestScore = -1; + for (CodegenModel subVariant : oneOfModel.interfaceModels) { + if (subVariant.vars.isEmpty()) continue; + int score = scoreDirectModelMatch(data, subVariant); + if (score > bestScore) { + bestScore = score; + } + } + return bestScore; + } + + /** + * Score how well a Map matches a direct model: -1 if required fields are missing, otherwise the + * count of common properties. + */ + private int scoreDirectModelMatch(Map data, CodegenModel model) { + // If any required property is missing, this model doesn't match + for (CodegenProperty prop : model.requiredVars) { + if (!data.containsKey(prop.baseName)) { + return -1; + } + } + int commonCount = 0; + for (String key : data.keySet()) { + for (CodegenProperty prop : model.vars) { + if (key.equals(prop.baseName) && couldMatchEnum(data.get(key), prop)) { + commonCount++; + } + } + } + return commonCount; + } + // If the model is an enum and contains a valid list of allowed values, // it will check that 'value' is in the list. // Otherwise return true by default to avoid false negative. diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/Request.java b/generators/src/main/java/com/algolia/codegen/cts/tests/Request.java index cf15a7bbe7e..91b069865d2 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/Request.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/Request.java @@ -12,6 +12,7 @@ public class Request { public RequestProp request; public ResponseProp response; public List skipLanguages; + public Boolean isStreaming; @Override public String toString() { @@ -22,6 +23,7 @@ public String toString() { sb.append(" requestOptions: ").append(requestOptions).append("\n"); sb.append(" request: ").append(request).append("\n"); sb.append(" response: ").append(response).append("\n"); + sb.append(" isStreaming: ").append(isStreaming).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java b/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java index 01486d1fd7d..022a8821c7d 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/Snippet.java @@ -59,6 +59,7 @@ public void addMethodCall(Map context, ParametersWithDataType pa context.put("isAsyncMethod", (boolean) ope.vendorExtensions.getOrDefault("x-asynchronous-helper", true)); context.put("hasParams", ope.getHasParams()); context.put("isHelper", (boolean) ope.vendorExtensions.getOrDefault("x-helper", false)); + context.put("isStreaming", (boolean) ope.vendorExtensions.getOrDefault("x-streaming", false)); context.put("hasRequestOptions", requestOptions != null); if (requestOptions != null) { diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java b/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java index baa7f7cbf29..58c4d6ea3fa 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/SnippetsGenerator.java @@ -48,7 +48,7 @@ private Map loadSnippets(Map operat Map snippets = loadFullCTS(Snippet[].class); for (Map.Entry blockEntry : snippets.entrySet()) { for (Snippet test : blockEntry.getValue()) { - test.method = blockEntry.getKey(); + test.method = ctsManager.resolveMethodName(blockEntry.getKey()); } } diff --git a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java index 720281f36db..17077a6a513 100644 --- a/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java +++ b/generators/src/main/java/com/algolia/codegen/cts/tests/TestsRequest.java @@ -131,7 +131,7 @@ public void run(Map models, Map ); continue; } - test.put("method", operationId); + test.put("method", ctsManager.resolveMethodName(operationId)); test.put("testName", req.testName == null ? operationId : req.testName); test.put("testIndex", i == 0 ? "" : i); if (ope.returnType != null && ope.returnType.length() > 0) { @@ -142,6 +142,7 @@ public void run(Map models, Map test.put("isGeneric", (boolean) ope.vendorExtensions.getOrDefault("x-is-generic", false)); test.put("isReturnGeneric", (boolean) ope.vendorExtensions.getOrDefault("x-return-is-generic", false)); test.put("isCustomRequest", Helpers.CUSTOM_METHODS.contains(ope.operationIdOriginal)); + test.put("isStreaming", (boolean) ope.vendorExtensions.getOrDefault("x-streaming", false)); if (req.request != null && !isHelper) { // We check on the spec if body parameters should be present in the CTS @@ -170,6 +171,13 @@ public void run(Map models, Map test.put("hasParams", ope.getHasParams()); test.put("isHelper", isHelper); + boolean isStreamingTest = req.isStreaming != null && req.isStreaming; + test.put("isStreamingTest", isStreamingTest); + if (isStreamingTest) { + test.put("streamMethodSuffix", "StreamRaw"); + test.put("streamMethodSuffixSnake", "_stream_raw"); + } + addRequestOptions(paramsType, req.requestOptions, test); // Determines whether the endpoint is expected to return a response payload deserialized diff --git a/generators/src/main/java/com/algolia/codegen/utils/Helpers.java b/generators/src/main/java/com/algolia/codegen/utils/Helpers.java index a607cc0c6a0..19ece42c34a 100644 --- a/generators/src/main/java/com/algolia/codegen/utils/Helpers.java +++ b/generators/src/main/java/com/algolia/codegen/utils/Helpers.java @@ -163,11 +163,24 @@ public static void generateServers(List servers, Map models) { var oneOfs = getCodegenProperties(model); if (isMultiArrayOneOfs(oneOfs)) model.vendorExtensions.put("x-is-multi-array", true); if (isMultiMapOneOfs(oneOfs)) model.vendorExtensions.put("x-is-multi-map", true); - if (hasAtModelOrEnum(oneOfs)) model.vendorExtensions.put("x-has-model", true); + if (hasAtModelOrEnum(oneOfs, model, models)) model.vendorExtensions.put("x-has-model", true); inferDiscriminatorFields(models, oneOfs); if (hasDiscriminators(oneOfs)) model.vendorExtensions.put("x-has-discriminator", true); markOneOfModels(oneOfs); @@ -207,10 +207,16 @@ private static boolean isMultiArrayOneOfs(List oneOfs) { } /** Get true if a composed type has at least one model/enum. */ - private static boolean hasAtModelOrEnum(List oneOfs) { + private static boolean hasAtModelOrEnum(List oneOfs, CodegenModel model, Map models) { for (var prop : oneOfs) { if (prop.isModel || prop.isEnumRef) return true; } + // Fallback: check if any oneOf type name is a known model. + // This handles cases where additionalProperties on a schema causes + // OpenAPI Generator to set isModel=false (classifying it as a map instead). + for (String oneOf : model.oneOf) { + if (models.containsKey(oneOf)) return true; + } return false; } diff --git a/playground/csharp/Playground/Playgrounds/AgentStudio.cs b/playground/csharp/Playground/Playgrounds/AgentStudio.cs new file mode 100644 index 00000000000..114f50136b5 --- /dev/null +++ b/playground/csharp/Playground/Playgrounds/AgentStudio.cs @@ -0,0 +1,36 @@ +using Algolia.Search.Clients; +using Algolia.Utils; +using Microsoft.Extensions.Logging; + +namespace Algolia.Playgrounds; + +public class AgentStudioPlayground : IPlayground +{ + private readonly AgentStudioClient _client; + + public AgentStudioPlayground(Configuration configuration) + { + var loggerFactory = LoggerFactory.Create(i => i.AddFilter("Algolia", LogLevel.Information) + .AddConsole()); + var config = new AgentStudioConfig(configuration.AppId, configuration.AdminApiKey); + _client = new AgentStudioClient(config, loggerFactory); + } + + public async Task Run() + { + PlaygroundHelper.Hello("Starting Agent Studio API playground"); + try + { + var response = await _client.ListAgentsAsync().ConfigureAwait(false); + Console.WriteLine("List of agents:"); + foreach (var agent in response.Data) + { + Console.WriteLine($"- {agent.Name} (ID: {agent.Id})"); + } + } + catch (Exception e) + { + Console.WriteLine($"Error: {e.Message}"); + } + } +} diff --git a/playground/csharp/Playground/Program.cs b/playground/csharp/Playground/Program.cs index 0855389aebd..3294ecda34e 100644 --- a/playground/csharp/Playground/Program.cs +++ b/playground/csharp/Playground/Program.cs @@ -55,6 +55,10 @@ async Task StartPlayground(string s, Configuration configuration) var ingestion = new IngestionPlayground(configuration); await ingestion.Run(); break; + case "agent-studio": + var agentStudio = new AgentStudioPlayground(configuration); + await agentStudio.Run(); + break; case "all": await StartPlayground("abtesting", configuration); await StartPlayground("analytics", configuration); @@ -65,6 +69,7 @@ async Task StartPlayground(string s, Configuration configuration) await StartPlayground("recommend", configuration); await StartPlayground("search", configuration); await StartPlayground("ingestion", configuration); + await StartPlayground("agent-studio", configuration); break; } } diff --git a/playground/go/agent-studio.go b/playground/go/agent-studio.go new file mode 100644 index 00000000000..e708ca94e63 --- /dev/null +++ b/playground/go/agent-studio.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + + agentStudio "github.com/algolia/algoliasearch-client-go/v4/algolia/agent-studio" +) + +func testAgentStudio(appID, apiKey string) int { + client, err := agentStudio.NewClient(appID, apiKey) + if err != nil { + panic(err) + } + + response, err := client.ListAgents(client.NewApiListAgentsRequest()) + if err != nil { + fmt.Printf("request error: %v\n", err) + + return 1 + } + + fmt.Println("List of agents:") + for _, agent := range response.Data { + fmt.Printf("- %s (ID: %s)\n", agent.Name, agent.Id) + } + + return 0 +} diff --git a/playground/java/src/main/java/com/algolia/playground/AgentStudio.java b/playground/java/src/main/java/com/algolia/playground/AgentStudio.java new file mode 100644 index 00000000000..640670b726d --- /dev/null +++ b/playground/java/src/main/java/com/algolia/playground/AgentStudio.java @@ -0,0 +1,25 @@ +package com.algolia.playground; + +import com.algolia.api.AgentStudioClient; +import io.github.cdimascio.dotenv.Dotenv; + +public class AgentStudio { + + public static void main(String[] args) throws Exception { + var dotenv = Dotenv.configure().directory("../").load(); + var appId = dotenv.get("ALGOLIA_APPLICATION_ID"); + var apiKey = dotenv.get("ALGOLIA_ADMIN_KEY"); + + var client = new AgentStudioClient(appId, apiKey); + + try { + var response = client.listAgents(); + System.out.println("List of agents:"); + for (var agent : response.getData()) { + System.out.println("- " + agent.getName() + " (ID: " + agent.getId() + ")"); + } + } finally { + client.close(); + } + } +} diff --git a/playground/javascript/node/agentStudio.ts b/playground/javascript/node/agentStudio.ts new file mode 100644 index 00000000000..e095fc13749 --- /dev/null +++ b/playground/javascript/node/agentStudio.ts @@ -0,0 +1,27 @@ +import { ApiError } from '@algolia/client-common'; +import { agentStudioClient } from '@algolia/agent-studio'; + +const appId = process.env.ALGOLIA_APPLICATION_ID || '**** APP_ID *****'; +const apiKey = process.env.ALGOLIA_ADMIN_KEY || '**** ADMIN_KEY *****'; + +// Init client with appId and apiKey +const client = agentStudioClient(appId, apiKey); + +async function testAgentStudio() { + try { + const response = await client.listAgents(); + + console.log('List of agents:'); + for (const agent of response.data) { + console.log(`- ${agent.name} (ID: ${agent.id})`); + } + } catch (e) { + if (e instanceof ApiError) { + return console.log(`[${e.status}] ${e.message}`, e.stackTrace); + } + + console.log('[ERROR]', e); + } +} + +testAgentStudio(); diff --git a/playground/javascript/node/agentStudioStreaming.ts b/playground/javascript/node/agentStudioStreaming.ts new file mode 100644 index 00000000000..45b3cca96f5 --- /dev/null +++ b/playground/javascript/node/agentStudioStreaming.ts @@ -0,0 +1,131 @@ +import { ApiError } from '@algolia/client-common'; +import { agentStudioClient } from '@algolia/agent-studio'; + +// ─── Config ───────────────────────────────────────────────────────────────── +const appId = process.env.ALGOLIA_APPLICATION_ID || ''; +const apiKey = process.env.ALGOLIA_ADMIN_KEY || ''; + +if (!appId || !apiKey) { + console.error('[FATAL] ALGOLIA_APPLICATION_ID and ALGOLIA_ADMIN_KEY must be set in playground/.env'); + process.exit(1); +} + +console.log(`[CONFIG] appId=${appId}`); +console.log(`[CONFIG] apiKey=${apiKey.slice(0, 6)}...`); + +const client = agentStudioClient(appId, apiKey); + +// The generated JS clients set 'content-type: text/plain' by default, which works +// for traditional Algolia APIs but agent-studio (FastAPI) requires 'application/json'. +const jsonHeaders = { headers: { 'content-type': 'application/json' } }; + +let createdAgentId: string | undefined; + +async function testStreaming() { + try { + // 1. Find a provider to use + console.log('\n─── Step 1: Finding a provider ───'); + const providersResponse = await client.listProviders(); + console.log(`[PROVIDERS] Found ${providersResponse.data.length} provider(s):`); + for (const p of providersResponse.data) { + console.log(` - ${p.name} (ID: ${p.id})`); + } + + if (providersResponse.data.length === 0) { + console.error('[FATAL] No providers found. Configure one first.'); + return; + } + + const provider = providersResponse.data[0]; + console.log(`\n[SELECTED] Using provider: "${provider.name}" (${provider.id})`); + + // 2. Create a temporary agent with the provider + console.log('\n─── Step 2: Creating temporary agent ───'); + const agent = await client.createAgent({ + name: `streaming-test-${Date.now()}`, + instructions: 'You are a helpful assistant. Keep your answers short and concise.', + providerId: provider.id, + model: 'gpt-4.1-mini', + }, + jsonHeaders, + ); + createdAgentId = agent.id; + console.log(`[CREATED] Agent "${agent.name}" (ID: ${agent.id})`); + console.log(`[CREATED] Status: ${agent.status}`); + + // Publish the agent so it can serve completions + console.log('[PUBLISH] Publishing agent...'); + await client.publishAgent({ agentId: agent.id }, jsonHeaders); + console.log('[PUBLISH] Agent published'); + + // 3. Call the streaming endpoint + console.log('\n─── Step 3: Calling createAgentCompletionStream ───'); + console.log('[REQUEST] Sending message: "Hello, what can you do?"'); + console.log('[REQUEST] compatibilityMode: "ai-sdk-5"'); + console.log('[REQUEST] stream: true'); + + const startTime = Date.now(); + let eventCount = 0; + + const stream = client.createAgentCompletionStream({ + agentId: agent.id, + compatibilityMode: 'ai-sdk-5', + stream: true, + agentCompletionRequest: { + messages: [ + { + role: 'user', + parts: [{ type: 'text', text: 'Hello, what can you do? Keep it short.' }], + }, + ], + }, + }, + jsonHeaders, + ); + + console.log('[STREAM] Stream object created, iterating events...\n'); + + // 4. Iterate over SSE events (typed: StreamEvent) + console.log('─── Step 4: Receiving SSE events ───'); + for await (const event of stream) { + eventCount++; + const elapsed = ((Date.now() - startTime) / 1000).toFixed(2); + + console.log(`\n[EVENT #${eventCount}] (t+${elapsed}s)`); + console.log(` event: "${event.raw.event}"`); + console.log(` id: "${event.raw.id}"`); + + if (event.error) { + console.log(` [PARSE ERROR] ${event.error.message}`); + console.log(` [RAW DATA] ${event.raw.data.slice(0, 200)}`); + } else if (event.data) { + console.log(` [TYPED DATA] keys=[${Object.keys(event.data).join(', ')}]`); + } + } + + const totalTime = ((Date.now() - startTime) / 1000).toFixed(2); + console.log(`\n─── Done ───`); + console.log(`[SUMMARY] Received ${eventCount} events in ${totalTime}s`); + } catch (e) { + if (e instanceof ApiError) { + console.error(`\n[API ERROR] [${e.status}] ${e.message}`); + console.error('[STACK]', e.stackTrace); + return; + } + + console.error('\n[ERROR]', e); + } finally { + // 5. Teardown: delete the temporary agent + if (createdAgentId) { + console.log(`\n─── Teardown ───`); + try { + await client.deleteAgent({ agentId: createdAgentId }, jsonHeaders); + console.log(`[DELETED] Agent ${createdAgentId}`); + } catch (e) { + console.error(`[TEARDOWN ERROR] Failed to delete agent ${createdAgentId}:`, e); + } + } + } +} + +testStreaming(); diff --git a/playground/javascript/node/package.json b/playground/javascript/node/package.json index 55e5c20104c..061c759f43e 100644 --- a/playground/javascript/node/package.json +++ b/playground/javascript/node/package.json @@ -19,6 +19,7 @@ "@algolia/composition": "link:../../../clients/algoliasearch-client-javascript/packages/composition", "@algolia/ingestion": "link:../../../clients/algoliasearch-client-javascript/packages/ingestion", "@algolia/monitoring": "link:../../../clients/algoliasearch-client-javascript/packages/monitoring", + "@algolia/agent-studio": "link:../../../clients/algoliasearch-client-javascript/packages/agent-studio", "@algolia/recommend": "link:../../../clients/algoliasearch-client-javascript/packages/recommend", "@algolia/requester-node-http": "link:../../../clients/algoliasearch-client-javascript/packages/requester-node-http", "algoliasearch": "link:../../../clients/algoliasearch-client-javascript/packages/algoliasearch" diff --git a/playground/kotlin/src/main/kotlin/com/algolia/playground/AgentStudio.kt b/playground/kotlin/src/main/kotlin/com/algolia/playground/AgentStudio.kt new file mode 100644 index 00000000000..0389d8c22e5 --- /dev/null +++ b/playground/kotlin/src/main/kotlin/com/algolia/playground/AgentStudio.kt @@ -0,0 +1,22 @@ +package com.algolia.playground + +import com.algolia.client.api.AgentStudioClient +import io.github.cdimascio.dotenv.Dotenv +import kotlin.system.exitProcess + +suspend fun main() { + val dotenv = Dotenv.configure().directory("../").load() + + val client = AgentStudioClient( + appId = dotenv["ALGOLIA_APPLICATION_ID"], + apiKey = dotenv["ALGOLIA_ADMIN_KEY"], + ) + + val response = client.listAgents() + println("List of agents:") + for (agent in response.data) { + println("- ${agent.name} (ID: ${agent.id})") + } + + exitProcess(0) +} diff --git a/playground/php/src/agent-studio.php b/playground/php/src/agent-studio.php new file mode 100644 index 00000000000..e25496d21ae --- /dev/null +++ b/playground/php/src/agent-studio.php @@ -0,0 +1,18 @@ +listAgents(); + +echo 'List of agents:' . PHP_EOL; +foreach ($response['agents'] as $agent) { + echo '- ' . $agent['name'] . ' (ID: ' . $agent['id'] . ')' . PHP_EOL; +} diff --git a/playground/python/app/agent_studio.py b/playground/python/app/agent_studio.py new file mode 100644 index 00000000000..757593feac6 --- /dev/null +++ b/playground/python/app/agent_studio.py @@ -0,0 +1,29 @@ +from os import environ +from asyncio import run + +from algoliasearch.agent_studio.client import AgentStudioClient +from dotenv import load_dotenv + +load_dotenv("../.env") + + +async def main(): + client = AgentStudioClient( + environ.get("ALGOLIA_APPLICATION_ID"), environ.get("ALGOLIA_ADMIN_KEY") + ) + + print("client initialized", client) + + try: + response = await client.list_agents() + + print("List of agents:") + for agent in response.data: + print(f"- {agent.name} (ID: {agent.id})") + finally: + await client.close() + + print("client closed", client) + + +run(main()) diff --git a/playground/python/poetry.lock b/playground/python/poetry.lock index cd6810847e7..81522189402 100644 --- a/playground/python/poetry.lock +++ b/playground/python/poetry.lock @@ -142,7 +142,7 @@ frozenlist = ">=1.1.0" [[package]] name = "algoliasearch" -version = "4.29.1" +version = "4.37.0" description = "A fully-featured and blazing-fast Python API client to interact with Algolia." optional = false python-versions = ">= 3.8.1" diff --git a/playground/ruby/agent-studio.rb b/playground/ruby/agent-studio.rb new file mode 100644 index 00000000000..a5e81e3671c --- /dev/null +++ b/playground/ruby/agent-studio.rb @@ -0,0 +1,12 @@ +require 'dotenv' +require 'algolia' + +Dotenv.load('../.env') + +client = Algolia::AgentStudioClient.create(ENV['ALGOLIA_APPLICATION_ID'], ENV['ALGOLIA_ADMIN_KEY'], 'us') + +response = client.list_agents +puts 'List of agents:' +response.agents.each do |agent| + puts "- #{agent.name} (ID: #{agent.id})" +end diff --git a/playground/scala/src/main/scala/AgentStudio.scala b/playground/scala/src/main/scala/AgentStudio.scala new file mode 100644 index 00000000000..872d860506d --- /dev/null +++ b/playground/scala/src/main/scala/AgentStudio.scala @@ -0,0 +1,31 @@ +import algoliasearch.api.AgentStudioClient +import algoliasearch.agentstudio.JsonSupport +import io.github.cdimascio.dotenv.Dotenv +import org.json4s.Formats + +import scala.concurrent.duration.Duration +import scala.concurrent.{Await, ExecutionContextExecutor} + +object AgentStudio { + def main(args: Array[String]): Unit = { + implicit val ec: ExecutionContextExecutor = scala.concurrent.ExecutionContext.global + implicit val formats: Formats = JsonSupport.format + + val dotenv = Dotenv.configure.directory("../").load + val appId = dotenv.get("ALGOLIA_APPLICATION_ID") + val apiKey = dotenv.get("ALGOLIA_ADMIN_KEY") + + val client = AgentStudioClient( + appId = appId, + apiKey = apiKey, + ) + + val res = client.listAgents() + val response = Await.result(res, Duration(100, "sec")) + + println("List of agents:") + for (agent <- response.data) { + println(s"- ${agent.name} (ID: ${agent.id})") + } + } +} diff --git a/playground/swift/Sources/AgentStudio/main.swift b/playground/swift/Sources/AgentStudio/main.swift new file mode 100644 index 00000000000..496d43a22ac --- /dev/null +++ b/playground/swift/Sources/AgentStudio/main.swift @@ -0,0 +1,54 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +import Foundation + +import DotEnv + +import AlgoliaCore +@preconcurrency import AlgoliaAgentStudio + +do { + guard let currentFileURL = URL(string: #file) else { + fatalError("Unable to get current file URL") + } + + let packageDirectoryURL = currentFileURL + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + + let dotEnvURL = packageDirectoryURL + .appendingPathComponent(".env") + try DotEnv.load(path: dotEnvURL.absoluteString, encoding: .utf8, overwrite: true) +} catch { + fatalError("Unable to load .env file from playground folder") +} + +let env = ProcessInfo.processInfo.environment + +guard let applicationID = env["ALGOLIA_APPLICATION_ID"] else { + fatalError("Application ID not found in env") +} + +guard let apiKey = env["ALGOLIA_ADMIN_KEY"] else { + fatalError("Admin API key not found in env") +} + +guard applicationID != "" && apiKey != "" else { + fatalError("AppID and ApiKey must be filled in your .env file") +} + +do { + let client = try AgentStudioClient(appID: applicationID, apiKey: apiKey) + + let response = try await client.listAgents() + + print("List of agents:") + for agent in response.data { + print("- \(agent.name) (ID: \(agent.id))") + } +} catch { + dump(error.localizedDescription) +} diff --git a/redocly.yaml b/redocly.yaml index b902318c16d..779198aa33d 100644 --- a/redocly.yaml +++ b/redocly.yaml @@ -5,6 +5,8 @@ apis: root: specs/abtesting-v3/spec.yml advanced-personalization: root: specs/advanced-personalization/spec.yml + agent-studio: + root: specs/agent-studio/spec.yml analytics: root: specs/analytics/spec.yml composition: diff --git a/renovate.json b/renovate.json index ea51695293e..11de1c8f0ab 100644 --- a/renovate.json +++ b/renovate.json @@ -274,6 +274,7 @@ "ignorePaths": [ "**/algoliasearch/**", "**/client-abtesting/**", + "**/client-agent-studio/**", "**/client-analytics/**", "**/client-insights/**", "**/client_insights/**", diff --git a/scripts/cts/testServer/index.ts b/scripts/cts/testServer/index.ts index 6a3a0d47484..500b5aedf65 100644 --- a/scripts/cts/testServer/index.ts +++ b/scripts/cts/testServer/index.ts @@ -15,6 +15,7 @@ import { chunkWrapperServer } from './chunkWrapper.ts'; import { errorServer, errorServerRetriedOnce, errorServerRetriedTwice, neverCalledServer } from './error.ts'; import { gzipServer } from './gzip.ts'; import { gzipResponseServer } from './gzipResponse.ts'; +import { noContentServer } from './noContent.ts'; import { pushMockServer, pushMockServerRetriedOnce } from './pushMock.ts'; import { replaceAllObjectsServer } from './replaceAllObjects.ts'; import { replaceAllObjectsServerFailed } from './replaceAllObjectsFailed.ts'; @@ -49,6 +50,7 @@ export async function startTestServer(suites: Record): Promise pushMockServer(), pushMockServerRetriedOnce(), replaceAllObjectsWithTransformationServer(), + noContentServer(), ); } if (suites.benchmark) { @@ -72,10 +74,22 @@ export async function startTestServer(suites: Record): Promise }; } +const SERVER_PATH_PREFIXES = ['/agent-studio']; + export async function setupServer(name: string, port: number, addRoutes: (app: Express) => void): Promise { const spinner = createSpinner(`starting ${name} test server`); const app = express(); + app.use((req, _res, next) => { + for (const prefix of SERVER_PATH_PREFIXES) { + if (req.url.startsWith(prefix)) { + req.url = req.url.slice(prefix.length) || '/'; + break; + } + } + next(); + }); + addRoutes(app); // 404 handler diff --git a/scripts/cts/testServer/noContent.ts b/scripts/cts/testServer/noContent.ts new file mode 100644 index 00000000000..8d14a5d4c51 --- /dev/null +++ b/scripts/cts/testServer/noContent.ts @@ -0,0 +1,15 @@ +import type { Server } from 'http'; + +import type express from 'express'; + +import { setupServer } from './index.ts'; + +function addRoutes(app: express.Express): void { + app.all('/1/test/no-content', (_req, res) => { + res.status(204).end(); + }); +} + +export function noContentServer(): Promise { + return setupServer('noContent', 6692, addRoutes); +} diff --git a/specs/agent-studio/common/schemas/AdvancedSyntaxFeatures.yml b/specs/agent-studio/common/schemas/AdvancedSyntaxFeatures.yml new file mode 100644 index 00000000000..745631498b7 --- /dev/null +++ b/specs/agent-studio/common/schemas/AdvancedSyntaxFeatures.yml @@ -0,0 +1,7 @@ +AdvancedSyntaxFeatures: + type: string + enum: + - exactPhrase + - excludeWords + title: advancedSyntaxFeatures + description: AdvancedSyntaxFeatures. diff --git a/specs/agent-studio/common/schemas/AdvancedSyntaxFeaturesUnion.yml b/specs/agent-studio/common/schemas/AdvancedSyntaxFeaturesUnion.yml new file mode 100644 index 00000000000..8aee16c85e4 --- /dev/null +++ b/specs/agent-studio/common/schemas/AdvancedSyntaxFeaturesUnion.yml @@ -0,0 +1,6 @@ +AdvancedSyntaxFeaturesUnion: + oneOf: + - items: + $ref: AdvancedSyntaxFeatures.yml#/AdvancedSyntaxFeatures + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/AgentCompletionAlgoliaParams.yml b/specs/agent-studio/common/schemas/AgentCompletionAlgoliaParams.yml new file mode 100644 index 00000000000..51b02cc5e3e --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentCompletionAlgoliaParams.yml @@ -0,0 +1,17 @@ +AgentCompletionAlgoliaParams: + properties: + mcpServers: + oneOf: + - additionalProperties: + additionalProperties: + additionalProperties: + type: string + type: object + type: object + type: object + - type: 'null' + title: mcpservers + searchParameters: + $ref: SearchParametersOverridesMapNullable.yml#/SearchParametersOverridesMapNullable + type: object + title: agentCompletionAlgoliaParams diff --git a/specs/agent-studio/common/schemas/AgentCompletionAlgoliaParamsNullable.yml b/specs/agent-studio/common/schemas/AgentCompletionAlgoliaParamsNullable.yml new file mode 100644 index 00000000000..c1b9e2ca581 --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentCompletionAlgoliaParamsNullable.yml @@ -0,0 +1,4 @@ +AgentCompletionAlgoliaParamsNullable: + oneOf: + - $ref: AgentCompletionAlgoliaParams.yml#/AgentCompletionAlgoliaParams + - type: 'null' diff --git a/specs/agent-studio/common/schemas/AgentCompletionRequest.yml b/specs/agent-studio/common/schemas/AgentCompletionRequest.yml new file mode 100644 index 00000000000..633d0ce70c1 --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentCompletionRequest.yml @@ -0,0 +1,22 @@ +AgentCompletionRequest: + properties: + configuration: + $ref: AgentTestConfigurationNullable.yml#/AgentTestConfigurationNullable + messages: + $ref: MessagesNullableUnion.yml#/MessagesNullableUnion + id: + oneOf: + - type: string + maxLength: 128 + - type: 'null' + title: id + description: Optional conversation id. + algolia: + $ref: AgentCompletionAlgoliaParamsNullable.yml#/AgentCompletionAlgoliaParamsNullable + toolApprovals: + type: object + title: toolApprovals + description: Approval decisions for pending tool calls keyed by toolCallId. + type: object + title: agentCompletionRequest + description: Request model for creating a completion for an assistant. diff --git a/specs/agent-studio/common/schemas/AgentConfigCreate.yml b/specs/agent-studio/common/schemas/AgentConfigCreate.yml new file mode 100644 index 00000000000..5ea228692a5 --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentConfigCreate.yml @@ -0,0 +1,64 @@ +AgentConfigCreate: + properties: + name: + type: string + title: name + description: + oneOf: + - type: string + - type: 'null' + title: description + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + type: string + title: instructions + description: 'The agent prompt: defines the agent''s role, tone, and goals. + Guides how it answers using the provided context. Corresponds to the ''Agent + prompt'' field in the dashboard.' + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + description: 'The system prompt: defines system-level rules and constraints. + Guides how the agent uses tools, features, and generates context. Prepended + before `instructions` in the final prompt sent to the LLM. Typically injected + by an agent template — modify with caution, as changes may affect behavior, + tool usage, or response accuracy. Corresponds to the ''System prompt'' field + in the dashboard.' + templateType: + oneOf: + - type: string + - type: 'null' + title: templatetype + config: + additionalProperties: true + type: object + title: config + default: {} + example: + sendUsage: true + sendReasoning: true + temperature: 0.7 + max_tokens: 1500 + reasoning: '{summary: auto}' + features: '[memory]' + tools: + items: + $ref: ToolConfigInput.yml#/ToolConfigInput + type: array + title: tools + type: object + required: + - name + - instructions + title: agentConfigCreate diff --git a/specs/agent-studio/common/schemas/AgentConfigUpdate.yml b/specs/agent-studio/common/schemas/AgentConfigUpdate.yml new file mode 100644 index 00000000000..06d49be0929 --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentConfigUpdate.yml @@ -0,0 +1,61 @@ +AgentConfigUpdate: + properties: + name: + oneOf: + - type: string + - type: 'null' + title: name + description: + oneOf: + - type: string + - type: 'null' + title: description + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + oneOf: + - type: string + - type: 'null' + title: instructions + description: 'The agent prompt: defines the agent''s role, tone, and goals. + Guides how it answers using the provided context. Corresponds to the ''Agent + prompt'' field in the dashboard.' + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + description: 'The system prompt: defines system-level rules and constraints. + Guides how the agent uses tools, features, and generates context. Prepended + before `instructions` in the final prompt sent to the LLM. Typically injected + by an agent template — modify with caution, as changes may affect behavior, + tool usage, or response accuracy. Corresponds to the ''System prompt'' field + in the dashboard.' + config: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: config + tools: + oneOf: + - items: + $ref: ToolConfigInput.yml#/ToolConfigInput + type: array + - type: 'null' + title: tools + templateType: + oneOf: + - type: string + - type: 'null' + title: templatetype + type: object + title: agentConfigUpdate diff --git a/specs/agent-studio/common/schemas/AgentConsolidateRequest.yml b/specs/agent-studio/common/schemas/AgentConsolidateRequest.yml new file mode 100644 index 00000000000..2bb45b59d4a --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentConsolidateRequest.yml @@ -0,0 +1,47 @@ +AgentConsolidateRequest: + properties: + providerID: + type: string + title: providerid + description: Provider UUID for LLM credentials. + model: + type: string + title: model + description: Model name (e.g., gpt-4o-mini). + messages: + $ref: MessagesUnion.yml#/MessagesUnion + targetMemories: + type: integer + maximum: 50.0 + minimum: 1.0 + title: targetmemories + description: Maximum memories to extract/consolidate in this operation. + default: 5 + instructions: + oneOf: + - type: string + maxLength: 50000 + - type: 'null' + title: instructions + description: Optional domain-specific instructions to guide extraction or consolidation. + wait: + type: boolean + title: wait + description: Wait for Algolia indexing to complete before returning. + default: false + memoryType: + $ref: MemoryType.yml#/MemoryType + maxExisting: + type: integer + maximum: 500.0 + minimum: 1.0 + title: maxexisting + description: Maximum number of existing memories to load for consolidation. + default: 50 + type: object + required: + - providerID + - model + - messages + title: agentConsolidateRequest + description: Agent endpoint request for automatic memory consolidation. diff --git a/specs/agent-studio/common/schemas/AgentConsolidateResponse.yml b/specs/agent-studio/common/schemas/AgentConsolidateResponse.yml new file mode 100644 index 00000000000..6ee180b71cb --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentConsolidateResponse.yml @@ -0,0 +1,35 @@ +AgentConsolidateResponse: + properties: + savedMemories: + items: + $ref: MemoryRecord.yml#/MemoryRecord + type: array + title: savedmemories + description: Memories saved/updated. + deletedIds: + items: + type: string + type: array + title: deletedids + description: Memory object IDs deleted during consolidation. + saveTaskId: + oneOf: + - type: string + - type: 'null' + title: savetaskid + description: Algolia task ID for save batch. + deleteTaskIds: + items: + type: string + type: array + title: deletetaskids + description: Algolia task IDs for deletes. + message: + type: string + title: message + description: Summary message of consolidation results. + type: object + required: + - message + title: agentConsolidateResponse + description: Response model for agent consolidate endpoint. diff --git a/specs/agent-studio/common/schemas/AgentIdUnion.yml b/specs/agent-studio/common/schemas/AgentIdUnion.yml new file mode 100644 index 00000000000..2e03cc2b24f --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentIdUnion.yml @@ -0,0 +1,5 @@ +AgentIdUnion: + oneOf: + - type: string + - const: test + type: string diff --git a/specs/agent-studio/common/schemas/AgentMemorizeRequest.yml b/specs/agent-studio/common/schemas/AgentMemorizeRequest.yml new file mode 100644 index 00000000000..c3d0c7ddfdb --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentMemorizeRequest.yml @@ -0,0 +1,38 @@ +AgentMemorizeRequest: + properties: + providerID: + type: string + title: providerid + description: Provider UUID for LLM credentials. + model: + type: string + title: model + description: Model name (e.g., gpt-4o-mini). + messages: + $ref: MessagesUnion.yml#/MessagesUnion + targetMemories: + type: integer + maximum: 50.0 + minimum: 1.0 + title: targetmemories + description: Maximum memories to extract/consolidate in this operation. + default: 5 + instructions: + oneOf: + - type: string + maxLength: 50000 + - type: 'null' + title: instructions + description: Optional domain-specific instructions to guide extraction or consolidation. + wait: + type: boolean + title: wait + description: Wait for Algolia indexing to complete before returning. + default: false + type: object + required: + - providerID + - model + - messages + title: agentMemorizeRequest + description: Agent endpoint request that extracts and saves semantic memories. diff --git a/specs/agent-studio/common/schemas/AgentMemorizeResponse.yml b/specs/agent-studio/common/schemas/AgentMemorizeResponse.yml new file mode 100644 index 00000000000..7fab3c7277f --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentMemorizeResponse.yml @@ -0,0 +1,23 @@ +AgentMemorizeResponse: + properties: + savedMemories: + items: + $ref: MemoryRecord.yml#/MemoryRecord + type: array + title: savedmemories + description: Memories saved to Algolia. + taskId: + oneOf: + - type: string + - type: 'null' + title: taskid + description: Algolia task ID for the save operation. + message: + type: string + title: message + description: Summary of the operation outcome. + type: object + required: + - message + title: agentMemorizeResponse + description: Response model for agent memorize endpoint. diff --git a/specs/agent-studio/common/schemas/AgentPonderRequest.yml b/specs/agent-studio/common/schemas/AgentPonderRequest.yml new file mode 100644 index 00000000000..28104939ddb --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentPonderRequest.yml @@ -0,0 +1,38 @@ +AgentPonderRequest: + properties: + providerID: + type: string + title: providerid + description: Provider UUID for LLM credentials. + model: + type: string + title: model + description: Model name (e.g., gpt-4o-mini). + messages: + $ref: MessagesUnion.yml#/MessagesUnion + targetMemories: + type: integer + maximum: 5.0 + minimum: 1.0 + title: targetmemories + description: Maximum episodic memories to extract. + default: 1 + instructions: + oneOf: + - type: string + maxLength: 50000 + - type: 'null' + title: instructions + description: Optional domain-specific instructions to guide extraction or consolidation. + wait: + type: boolean + title: wait + description: Wait for Algolia indexing to complete before returning. + default: false + type: object + required: + - providerID + - model + - messages + title: agentPonderRequest + description: Agent endpoint request that extracts and saves episodic memories. diff --git a/specs/agent-studio/common/schemas/AgentPonderResponse.yml b/specs/agent-studio/common/schemas/AgentPonderResponse.yml new file mode 100644 index 00000000000..dc552c457e6 --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentPonderResponse.yml @@ -0,0 +1,23 @@ +AgentPonderResponse: + properties: + savedMemories: + items: + $ref: MemoryRecord.yml#/MemoryRecord + type: array + title: savedmemories + description: Memories saved to Algolia. + taskId: + oneOf: + - type: string + - type: 'null' + title: taskid + description: Algolia task ID for the save operation. + message: + type: string + title: message + description: Summary of the operation outcome. + type: object + required: + - message + title: agentPonderResponse + description: Response model for agent ponder endpoint (episodic save). diff --git a/specs/agent-studio/common/schemas/AgentStatus.yml b/specs/agent-studio/common/schemas/AgentStatus.yml new file mode 100644 index 00000000000..2883d4cd11b --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentStatus.yml @@ -0,0 +1,5 @@ +AgentStatus: + type: string + enum: + - draft + - published diff --git a/specs/agent-studio/common/schemas/AgentTestConfiguration.yml b/specs/agent-studio/common/schemas/AgentTestConfiguration.yml new file mode 100644 index 00000000000..f3c019821db --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentTestConfiguration.yml @@ -0,0 +1,41 @@ +AgentTestConfiguration: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + type: string + title: instructions + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + config: + additionalProperties: true + type: object + title: config + tools: + items: + $ref: ToolConfigInput.yml#/ToolConfigInput + type: array + title: tools + type: object + required: + - instructions + - config + - tools + title: agentTestConfiguration + description: Dynamic configuration for testing agents. diff --git a/specs/agent-studio/common/schemas/AgentTestConfigurationNullable.yml b/specs/agent-studio/common/schemas/AgentTestConfigurationNullable.yml new file mode 100644 index 00000000000..e4499424817 --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentTestConfigurationNullable.yml @@ -0,0 +1,4 @@ +AgentTestConfigurationNullable: + oneOf: + - $ref: AgentTestConfiguration.yml#/AgentTestConfiguration + - type: 'null' diff --git a/specs/agent-studio/common/schemas/AgentWithVersionResponse.yml b/specs/agent-studio/common/schemas/AgentWithVersionResponse.yml new file mode 100644 index 00000000000..b158370d6c2 --- /dev/null +++ b/specs/agent-studio/common/schemas/AgentWithVersionResponse.yml @@ -0,0 +1,73 @@ +AgentWithVersionResponse: + properties: + id: + type: string + title: id + name: + type: string + title: name + description: + oneOf: + - type: string + - type: 'null' + title: description + status: + $ref: AgentStatus.yml#/AgentStatus + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + type: string + title: instructions + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + config: + additionalProperties: true + type: object + title: config + tools: + items: + $ref: ToolConfigOutput.yml#/ToolConfigOutput + type: array + title: tools + templateType: + oneOf: + - type: string + - type: 'null' + title: templatetype + createdAt: + type: string + title: createdat + updatedAt: + oneOf: + - type: string + - type: 'null' + title: updatedat + lastUsedAt: + oneOf: + - type: string + - type: 'null' + title: lastusedat + type: object + required: + - id + - name + - description + - status + - providerId + - instructions + - config + - createdAt + - updatedAt + - lastUsedAt + title: agentWithVersionResponse diff --git a/specs/agent-studio/common/schemas/AlgoliaDisplayResultsToolConfig.yml b/specs/agent-studio/common/schemas/AlgoliaDisplayResultsToolConfig.yml new file mode 100644 index 00000000000..33bfd225260 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaDisplayResultsToolConfig.yml @@ -0,0 +1,37 @@ +AlgoliaDisplayResultsToolConfig: + properties: + name: + type: string + const: algolia_display_results + title: name + default: algolia_display_results + type: + type: string + const: algolia_display_results + title: type + default: algolia_display_results + minGroups: + type: integer + minimum: 1.0 + title: mingroups + default: 1 + maxGroups: + type: integer + minimum: 1.0 + title: maxgroups + default: 3 + minResultsPerGroup: + type: integer + minimum: 1.0 + title: minresultspergroup + default: 3 + maxResultsPerGroup: + type: integer + minimum: 1.0 + title: maxresultspergroup + default: 6 + type: object + title: algoliaDisplayResultsToolConfig + description: Configuration for the algolia_display_results tool. + required: + - type diff --git a/specs/agent-studio/common/schemas/AlgoliaRecommendToolConfigInput.yml b/specs/agent-studio/common/schemas/AlgoliaRecommendToolConfigInput.yml new file mode 100644 index 00000000000..20217911e86 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaRecommendToolConfigInput.yml @@ -0,0 +1,30 @@ +AlgoliaRecommendToolConfig-Input: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_recommend + title: type + default: algolia_recommend + allowedConfigs: + items: + $ref: AlgoliaRecommendToolIndexConfig.yml#/AlgoliaRecommendToolIndexConfig + type: array + title: allowedconfigs + default: [] + predefinedRecommendParameters: + additionalProperties: true + type: object + title: predefinedrecommendparameters + type: object + required: + - type + - name + title: algoliaRecommendToolConfig + description: |- + Configuration for the Algolia Recommend tool. + Allows specifying recommend models and related parameters. diff --git a/specs/agent-studio/common/schemas/AlgoliaRecommendToolConfigOutput.yml b/specs/agent-studio/common/schemas/AlgoliaRecommendToolConfigOutput.yml new file mode 100644 index 00000000000..16a185e54b6 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaRecommendToolConfigOutput.yml @@ -0,0 +1,35 @@ +AlgoliaRecommendToolConfig-Output: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_recommend + title: type + default: algolia_recommend + allowedConfigs: + items: + $ref: AlgoliaRecommendToolIndexConfig.yml#/AlgoliaRecommendToolIndexConfig + type: array + title: allowedconfigs + default: [] + predefinedRecommendParameters: + additionalProperties: true + type: object + title: predefinedrecommendparameters + description: + type: string + title: description + readOnly: true + type: object + required: + - type + - name + - description + title: algoliaRecommendToolConfig + description: |- + Configuration for the Algolia Recommend tool. + Allows specifying recommend models and related parameters. diff --git a/specs/agent-studio/common/schemas/AlgoliaRecommendToolIndexConfig.yml b/specs/agent-studio/common/schemas/AlgoliaRecommendToolIndexConfig.yml new file mode 100644 index 00000000000..5b2b160b06c --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaRecommendToolIndexConfig.yml @@ -0,0 +1,21 @@ +AlgoliaRecommendToolIndexConfig: + properties: + index: + type: string + maxLength: 100 + minLength: 1 + title: index + modelName: + type: string + maxLength: 100 + minLength: 1 + title: modelname + description: + type: string + title: description + default: '' + type: object + required: + - index + - modelName + title: algoliaRecommendToolIndexConfig diff --git a/specs/agent-studio/common/schemas/AlgoliaSearchToolConfigInput.yml b/specs/agent-studio/common/schemas/AlgoliaSearchToolConfigInput.yml new file mode 100644 index 00000000000..52e5c9d0508 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaSearchToolConfigInput.yml @@ -0,0 +1,23 @@ +AlgoliaSearchToolConfig-Input: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_search_index + title: type + default: algolia_search_index + indices: + items: + $ref: AlgoliaSearchToolIndexConfigInput.yml#/AlgoliaSearchToolIndexConfig-Input + type: array + title: indices + type: object + required: + - type + - name + - indices + title: algoliaSearchToolConfig diff --git a/specs/agent-studio/common/schemas/AlgoliaSearchToolConfigOutput.yml b/specs/agent-studio/common/schemas/AlgoliaSearchToolConfigOutput.yml new file mode 100644 index 00000000000..cf526cec02e --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaSearchToolConfigOutput.yml @@ -0,0 +1,28 @@ +AlgoliaSearchToolConfig-Output: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_search_index + title: type + default: algolia_search_index + indices: + items: + $ref: AlgoliaSearchToolIndexConfigOutput.yml#/AlgoliaSearchToolIndexConfig-Output + type: array + title: indices + description: + type: string + title: description + readOnly: true + type: object + required: + - type + - name + - indices + - description + title: algoliaSearchToolConfig diff --git a/specs/agent-studio/common/schemas/AlgoliaSearchToolIndexConfigInput.yml b/specs/agent-studio/common/schemas/AlgoliaSearchToolIndexConfigInput.yml new file mode 100644 index 00000000000..83bcd104e69 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaSearchToolIndexConfigInput.yml @@ -0,0 +1,25 @@ +AlgoliaSearchToolIndexConfig-Input: + properties: + index: + type: string + maxLength: 100 + minLength: 1 + title: index + description: + type: string + maxLength: 3000 + minLength: 1 + title: description + enhancedDescription: + type: string + title: enhanceddescription + default: '' + searchParameters: + $ref: SearchParametersInputNullable.yml#/SearchParametersInputNullable + searchControls: + $ref: IndexSearchParametersInputNullable.yml#/IndexSearchParametersInputNullable + type: object + required: + - index + - description + title: algoliaSearchToolIndexConfig diff --git a/specs/agent-studio/common/schemas/AlgoliaSearchToolIndexConfigOutput.yml b/specs/agent-studio/common/schemas/AlgoliaSearchToolIndexConfigOutput.yml new file mode 100644 index 00000000000..a0634ac7107 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlgoliaSearchToolIndexConfigOutput.yml @@ -0,0 +1,25 @@ +AlgoliaSearchToolIndexConfig-Output: + properties: + index: + type: string + maxLength: 100 + minLength: 1 + title: index + description: + type: string + maxLength: 3000 + minLength: 1 + title: description + enhancedDescription: + type: string + title: enhanceddescription + default: '' + searchParameters: + $ref: SearchParametersOutputNullable.yml#/SearchParametersOutputNullable + searchControls: + $ref: IndexSearchParametersOutputNullable.yml#/IndexSearchParametersOutputNullable + type: object + required: + - index + - description + title: algoliaSearchToolIndexConfig diff --git a/specs/agent-studio/common/schemas/AllowedDomainBulkDelete.yml b/specs/agent-studio/common/schemas/AllowedDomainBulkDelete.yml new file mode 100644 index 00000000000..5548e8bf17a --- /dev/null +++ b/specs/agent-studio/common/schemas/AllowedDomainBulkDelete.yml @@ -0,0 +1,13 @@ +AllowedDomainBulkDelete: + properties: + domainIds: + items: + type: string + type: array + title: domainids + description: IDs of allowed domain records to delete. + type: object + required: + - domainIds + title: allowedDomainBulkDelete + description: Request body for bulk delete by IDs. diff --git a/specs/agent-studio/common/schemas/AllowedDomainBulkInsert.yml b/specs/agent-studio/common/schemas/AllowedDomainBulkInsert.yml new file mode 100644 index 00000000000..9c284d7d97d --- /dev/null +++ b/specs/agent-studio/common/schemas/AllowedDomainBulkInsert.yml @@ -0,0 +1,13 @@ +AllowedDomainBulkInsert: + properties: + domains: + items: + type: string + type: array + title: domains + description: List of domain patterns to add. + type: object + required: + - domains + title: allowedDomainBulkInsert + description: Request body for bulk insert. diff --git a/specs/agent-studio/common/schemas/AllowedDomainCreate.yml b/specs/agent-studio/common/schemas/AllowedDomainCreate.yml new file mode 100644 index 00000000000..b3cbaab1747 --- /dev/null +++ b/specs/agent-studio/common/schemas/AllowedDomainCreate.yml @@ -0,0 +1,11 @@ +AllowedDomainCreate: + properties: + domain: + type: string + title: domain + description: Domain or pattern, e.g. https://app.example.com or *.example.com. + type: object + required: + - domain + title: allowedDomainCreate + description: Request body to add a single allowed domain. diff --git a/specs/agent-studio/common/schemas/AllowedDomainListResponse.yml b/specs/agent-studio/common/schemas/AllowedDomainListResponse.yml new file mode 100644 index 00000000000..bbe7bc2d594 --- /dev/null +++ b/specs/agent-studio/common/schemas/AllowedDomainListResponse.yml @@ -0,0 +1,12 @@ +AllowedDomainListResponse: + properties: + domains: + items: + $ref: AllowedDomainResponse.yml#/AllowedDomainResponse + type: array + title: domains + type: object + required: + - domains + title: allowedDomainListResponse + description: List of allowed domains for an application. diff --git a/specs/agent-studio/common/schemas/AllowedDomainResponse.yml b/specs/agent-studio/common/schemas/AllowedDomainResponse.yml new file mode 100644 index 00000000000..7847d9cb84a --- /dev/null +++ b/specs/agent-studio/common/schemas/AllowedDomainResponse.yml @@ -0,0 +1,30 @@ +AllowedDomainResponse: + properties: + id: + type: string + title: id + appId: + type: string + title: appid + agentId: + type: string + title: agentid + domain: + type: string + title: domain + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + type: object + required: + - id + - appId + - agentId + - domain + - createdAt + - updatedAt + title: allowedDomainResponse + description: Single allowed domain in API responses. diff --git a/specs/agent-studio/common/schemas/AllowedToolsUnion.yml b/specs/agent-studio/common/schemas/AllowedToolsUnion.yml new file mode 100644 index 00000000000..058abde3f23 --- /dev/null +++ b/specs/agent-studio/common/schemas/AllowedToolsUnion.yml @@ -0,0 +1,6 @@ +AllowedToolsUnion: + oneOf: + - additionalProperties: + $ref: ToolConfig.yml#/ToolConfig + type: object + - type: 'null' diff --git a/specs/agent-studio/common/schemas/AlternativesAsExact.yml b/specs/agent-studio/common/schemas/AlternativesAsExact.yml new file mode 100644 index 00000000000..7bc9eac8200 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlternativesAsExact.yml @@ -0,0 +1,9 @@ +AlternativesAsExact: + type: string + enum: + - ignorePlurals + - singleWordSynonym + - multiWordsSynonym + - ignoreConjugations + title: alternativesAsExact + description: AlternativesAsExact. diff --git a/specs/agent-studio/common/schemas/AlternativesAsExactUnion.yml b/specs/agent-studio/common/schemas/AlternativesAsExactUnion.yml new file mode 100644 index 00000000000..a2e61328de9 --- /dev/null +++ b/specs/agent-studio/common/schemas/AlternativesAsExactUnion.yml @@ -0,0 +1,6 @@ +AlternativesAsExactUnion: + oneOf: + - items: + $ref: AlternativesAsExact.yml#/AlternativesAsExact + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/AnthropicProviderInputInput.yml b/specs/agent-studio/common/schemas/AnthropicProviderInputInput.yml new file mode 100644 index 00000000000..6fc613434ed --- /dev/null +++ b/specs/agent-studio/common/schemas/AnthropicProviderInputInput.yml @@ -0,0 +1,17 @@ +AnthropicProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + maxLength: 2083 + minLength: 1 + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: anthropicProviderInput + description: Anthropic-specific provider input. diff --git a/specs/agent-studio/common/schemas/AnthropicProviderInputOutput.yml b/specs/agent-studio/common/schemas/AnthropicProviderInputOutput.yml new file mode 100644 index 00000000000..e9339ed2760 --- /dev/null +++ b/specs/agent-studio/common/schemas/AnthropicProviderInputOutput.yml @@ -0,0 +1,15 @@ +AnthropicProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: anthropicProviderInput + description: Anthropic-specific provider input. diff --git a/specs/agent-studio/common/schemas/ApplicationConfigPatch.yml b/specs/agent-studio/common/schemas/ApplicationConfigPatch.yml new file mode 100644 index 00000000000..c3d4ad55256 --- /dev/null +++ b/specs/agent-studio/common/schemas/ApplicationConfigPatch.yml @@ -0,0 +1,12 @@ +ApplicationConfigPatch: + properties: + maxRetentionDays: + oneOf: + - type: integer + - type: 'null' + title: maxretentiondays + description: 'Maximum number of days to retain data. Valid values: [0, 30, 60, + 90].' + default: 90 + type: object + title: applicationConfigPatch diff --git a/specs/agent-studio/common/schemas/ApplicationConfigResponse.yml b/specs/agent-studio/common/schemas/ApplicationConfigResponse.yml new file mode 100644 index 00000000000..a2bf7021f8c --- /dev/null +++ b/specs/agent-studio/common/schemas/ApplicationConfigResponse.yml @@ -0,0 +1,9 @@ +ApplicationConfigResponse: + properties: + maxRetentionDays: + type: integer + title: maxretentiondays + type: object + required: + - maxRetentionDays + title: applicationConfigResponse diff --git a/specs/agent-studio/common/schemas/AroundPrecisionUnion.yml b/specs/agent-studio/common/schemas/AroundPrecisionUnion.yml new file mode 100644 index 00000000000..1c6fe1814b1 --- /dev/null +++ b/specs/agent-studio/common/schemas/AroundPrecisionUnion.yml @@ -0,0 +1,9 @@ +AroundPrecisionUnion: + oneOf: + - type: integer + - items: + additionalProperties: + type: integer + type: object + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/AroundRadiusUnion.yml b/specs/agent-studio/common/schemas/AroundRadiusUnion.yml new file mode 100644 index 00000000000..7800e84e8d3 --- /dev/null +++ b/specs/agent-studio/common/schemas/AroundRadiusUnion.yml @@ -0,0 +1,5 @@ +AroundRadiusUnion: + oneOf: + - type: integer + - type: string + - type: 'null' diff --git a/specs/agent-studio/common/schemas/AssistantMessageV4.yml b/specs/agent-studio/common/schemas/AssistantMessageV4.yml new file mode 100644 index 00000000000..f683270810d --- /dev/null +++ b/specs/agent-studio/common/schemas/AssistantMessageV4.yml @@ -0,0 +1,27 @@ +AssistantMessageV4: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: assistant + title: role + default: assistant + content: + type: string + title: content + parts: + items: + $ref: AssistantPartV4.yml#/AssistantPartV4 + type: array + title: parts + toolInvocations: + $ref: ToolInvocationsUnion.yml#/ToolInvocationsUnion + type: object + required: + - role + - content + title: assistantMessageV4 diff --git a/specs/agent-studio/common/schemas/AssistantMessageV5.yml b/specs/agent-studio/common/schemas/AssistantMessageV5.yml new file mode 100644 index 00000000000..c9c965e441d --- /dev/null +++ b/specs/agent-studio/common/schemas/AssistantMessageV5.yml @@ -0,0 +1,21 @@ +AssistantMessageV5: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: assistant + title: role + default: assistant + parts: + items: + $ref: AssistantPartV5.yml#/AssistantPartV5 + type: array + title: parts + type: object + title: assistantMessageV5 + required: + - role diff --git a/specs/agent-studio/common/schemas/AssistantPartV4.yml b/specs/agent-studio/common/schemas/AssistantPartV4.yml new file mode 100644 index 00000000000..1d277cb1187 --- /dev/null +++ b/specs/agent-studio/common/schemas/AssistantPartV4.yml @@ -0,0 +1,6 @@ +AssistantPartV4: + oneOf: + - $ref: StepStartPartV4.yml#/StepStartPartV4 + - $ref: ReasoningPartV4.yml#/ReasoningPartV4 + - $ref: TextPartV4.yml#/TextPartV4 + - $ref: ToolInvocationPartV4.yml#/ToolInvocationPartV4 diff --git a/specs/agent-studio/common/schemas/AssistantPartV5.yml b/specs/agent-studio/common/schemas/AssistantPartV5.yml new file mode 100644 index 00000000000..8b477ae07a5 --- /dev/null +++ b/specs/agent-studio/common/schemas/AssistantPartV5.yml @@ -0,0 +1,6 @@ +AssistantPartV5: + oneOf: + - $ref: StepStartPartV5.yml#/StepStartPartV5 + - $ref: TextPartV5.yml#/TextPartV5 + - $ref: ReasoningPartV5.yml#/ReasoningPartV5 + - $ref: ToolPartV5.yml#/ToolPartV5 diff --git a/specs/agent-studio/common/schemas/AzureOpenAIProviderInputInput.yml b/specs/agent-studio/common/schemas/AzureOpenAIProviderInputInput.yml new file mode 100644 index 00000000000..a21c923e184 --- /dev/null +++ b/specs/agent-studio/common/schemas/AzureOpenAIProviderInputInput.yml @@ -0,0 +1,28 @@ +AzureOpenAIProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + azureEndpoint: + type: string + maxLength: 2083 + minLength: 1 + title: azureendpoint + azureDeployment: + type: string + minLength: 1 + title: azuredeployment + description: Azure model deployment name is required. + apiVersion: + oneOf: + - type: string + - type: 'null' + title: apiversion + default: 2024-12-01-preview + type: object + required: + - apiKey + - azureEndpoint + - azureDeployment + title: azureOpenAIProviderInput + description: Azure OpenAI-specific provider input. diff --git a/specs/agent-studio/common/schemas/AzureOpenAIProviderInputOutput.yml b/specs/agent-studio/common/schemas/AzureOpenAIProviderInputOutput.yml new file mode 100644 index 00000000000..022d424849c --- /dev/null +++ b/specs/agent-studio/common/schemas/AzureOpenAIProviderInputOutput.yml @@ -0,0 +1,26 @@ +AzureOpenAIProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + azureEndpoint: + type: string + title: azureendpoint + azureDeployment: + type: string + minLength: 1 + title: azuredeployment + description: Azure model deployment name is required. + apiVersion: + oneOf: + - type: string + - type: 'null' + title: apiversion + default: 2024-12-01-preview + type: object + required: + - apiKey + - azureEndpoint + - azureDeployment + title: azureOpenAIProviderInput + description: Azure OpenAI-specific provider input. diff --git a/specs/agent-studio/common/schemas/BaseProviderInput.yml b/specs/agent-studio/common/schemas/BaseProviderInput.yml new file mode 100644 index 00000000000..cba2e48e3bf --- /dev/null +++ b/specs/agent-studio/common/schemas/BaseProviderInput.yml @@ -0,0 +1,10 @@ +BaseProviderInput: + properties: + apiKey: + type: string + title: apikey + type: object + required: + - apiKey + title: baseProviderInput + description: Base input that all providers must have. diff --git a/specs/agent-studio/common/schemas/ClientSideToolConfig.yml b/specs/agent-studio/common/schemas/ClientSideToolConfig.yml new file mode 100644 index 00000000000..d3dd498dfa9 --- /dev/null +++ b/specs/agent-studio/common/schemas/ClientSideToolConfig.yml @@ -0,0 +1,26 @@ +ClientSideToolConfig: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: client_side + title: type + default: client_side + description: + type: string + maxLength: 200 + minLength: 1 + title: description + inputSchema: + $ref: ClientToolsArgsSchema.yml#/ClientToolsArgsSchema + type: object + required: + - type + - name + - description + - inputSchema + title: clientSideToolConfig diff --git a/specs/agent-studio/common/schemas/ClientToolsArgsSchema.yml b/specs/agent-studio/common/schemas/ClientToolsArgsSchema.yml new file mode 100644 index 00000000000..1f879764090 --- /dev/null +++ b/specs/agent-studio/common/schemas/ClientToolsArgsSchema.yml @@ -0,0 +1,18 @@ +ClientToolsArgsSchema: + properties: + type: + type: string + const: object + title: type + default: object + properties: + additionalProperties: true + type: object + title: properties + required: + items: + type: string + type: array + title: required + type: object + title: clientToolsArgsSchema diff --git a/specs/agent-studio/common/schemas/CompatibilityMode.yml b/specs/agent-studio/common/schemas/CompatibilityMode.yml new file mode 100644 index 00000000000..b5c5771f2c5 --- /dev/null +++ b/specs/agent-studio/common/schemas/CompatibilityMode.yml @@ -0,0 +1,7 @@ +CompatibilityMode: + type: string + enum: + - ai-sdk-4 + - ai-sdk-5 + title: compatibilityMode + description: Support Compatibility modes for the completion API. diff --git a/specs/agent-studio/common/schemas/ConversationBaseResponse.yml b/specs/agent-studio/common/schemas/ConversationBaseResponse.yml new file mode 100644 index 00000000000..43c7493d46d --- /dev/null +++ b/specs/agent-studio/common/schemas/ConversationBaseResponse.yml @@ -0,0 +1,61 @@ +ConversationBaseResponse: + properties: + id: + type: string + title: id + agentId: + type: string + title: agentid + title: + oneOf: + - type: string + - type: 'null' + title: title + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastActivityAt: + oneOf: + - type: string + - type: 'null' + title: lastactivityat + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + isFromDashboard: + type: boolean + title: isfromdashboard + default: false + messageCount: + type: integer + title: messagecount + default: 0 + totalInputTokens: + type: integer + title: totalinputtokens + default: 0 + totalOutputTokens: + type: integer + title: totaloutputtokens + default: 0 + totalTokens: + type: integer + title: totaltokens + default: 0 + conversationMetadata: + $ref: ConversationMetadataNullable.yml#/ConversationMetadataNullable + feedback: + $ref: FeedbackUnion.yml#/FeedbackUnion + type: object + required: + - id + - agentId + - createdAt + - updatedAt + title: conversationBaseResponse + description: Lightweight response model without its messages. diff --git a/specs/agent-studio/common/schemas/ConversationFullResponse.yml b/specs/agent-studio/common/schemas/ConversationFullResponse.yml new file mode 100644 index 00000000000..ee78b4f10a4 --- /dev/null +++ b/specs/agent-studio/common/schemas/ConversationFullResponse.yml @@ -0,0 +1,67 @@ +ConversationFullResponse: + properties: + id: + type: string + title: id + agentId: + type: string + title: agentid + title: + oneOf: + - type: string + - type: 'null' + title: title + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastActivityAt: + oneOf: + - type: string + - type: 'null' + title: lastactivityat + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + isFromDashboard: + type: boolean + title: isfromdashboard + default: false + messageCount: + type: integer + title: messagecount + default: 0 + totalInputTokens: + type: integer + title: totalinputtokens + default: 0 + totalOutputTokens: + type: integer + title: totaloutputtokens + default: 0 + totalTokens: + type: integer + title: totaltokens + default: 0 + conversationMetadata: + $ref: ConversationMetadataNullable.yml#/ConversationMetadataNullable + feedback: + $ref: FeedbackUnion.yml#/FeedbackUnion + messages: + items: + $ref: MessageResponse.yml#/MessageResponse + type: array + title: messages + type: object + required: + - id + - agentId + - createdAt + - updatedAt + - messages + title: conversationFullResponse + description: Response model for a conversation with all its messages. diff --git a/specs/agent-studio/common/schemas/ConversationMetadata.yml b/specs/agent-studio/common/schemas/ConversationMetadata.yml new file mode 100644 index 00000000000..cbb4232f171 --- /dev/null +++ b/specs/agent-studio/common/schemas/ConversationMetadata.yml @@ -0,0 +1,10 @@ +ConversationMetadata: + properties: + cachedAt: + oneOf: + - type: string + - type: 'null' + title: cachedat + type: object + title: conversationMetadata + description: Public metadata exposed on conversation responses. diff --git a/specs/agent-studio/common/schemas/ConversationMetadataNullable.yml b/specs/agent-studio/common/schemas/ConversationMetadataNullable.yml new file mode 100644 index 00000000000..3203338b51b --- /dev/null +++ b/specs/agent-studio/common/schemas/ConversationMetadataNullable.yml @@ -0,0 +1,4 @@ +ConversationMetadataNullable: + oneOf: + - $ref: ConversationMetadata.yml#/ConversationMetadata + - type: 'null' diff --git a/specs/agent-studio/common/schemas/DistinctUnion.yml b/specs/agent-studio/common/schemas/DistinctUnion.yml new file mode 100644 index 00000000000..737728ee8b1 --- /dev/null +++ b/specs/agent-studio/common/schemas/DistinctUnion.yml @@ -0,0 +1,5 @@ +DistinctUnion: + oneOf: + - type: boolean + - type: integer + - type: 'null' diff --git a/specs/agent-studio/common/schemas/Episode.yml b/specs/agent-studio/common/schemas/Episode.yml new file mode 100644 index 00000000000..561c4a97f5d --- /dev/null +++ b/specs/agent-studio/common/schemas/Episode.yml @@ -0,0 +1,47 @@ +Episode: + properties: + observation: + type: string + maxLength: 5000 + minLength: 1 + title: observation + description: What user wanted + key context (1-2 sentences). Include prior failed + attempts if they informed the approach. + thoughts: + type: string + maxLength: 5000 + minLength: 1 + title: thoughts + description: WHY this approach was chosen, which constraints/preferences drove + decisions (1-3 sentences). Capture reasoning that applies to similar future + scenarios. + action: + type: string + maxLength: 5000 + minLength: 1 + title: action + description: 'What was done with PRECISE details (1-3 sentences). WITH tool + calls: use arrow notation `tool(param:value) → feedback → tool(refined_param:new_value)`. + WITHOUT tool calls: capture communication/workflow pattern.' + result: + type: string + maxLength: 5000 + minLength: 1 + title: result + description: 'Learned pattern + effectiveness (1-3 sentences). What worked and + WHY it''s replicable. Note efficiency: multi-turn refinements, which results + were relevant, what made final attempt succeed. Use strict `param:value` syntax + for learnings. Format: ''For [context], use [param:value] because [reason]''.' + type: object + required: + - observation + - thoughts + - action + - result + title: episode + description: |- + Episodic memory schema following LangMem's OTAR pattern: + Observation → Thoughts → Action → Result + + Captures complete interaction experiences for agent learning. + See https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#episodic-memory-past-experiences. diff --git a/specs/agent-studio/common/schemas/EpisodeNullable.yml b/specs/agent-studio/common/schemas/EpisodeNullable.yml new file mode 100644 index 00000000000..70def7ad7cb --- /dev/null +++ b/specs/agent-studio/common/schemas/EpisodeNullable.yml @@ -0,0 +1,4 @@ +EpisodeNullable: + oneOf: + - $ref: Episode.yml#/Episode + - type: 'null' diff --git a/specs/agent-studio/common/schemas/ExactOnSingleWordQuery.yml b/specs/agent-studio/common/schemas/ExactOnSingleWordQuery.yml new file mode 100644 index 00000000000..54f9f94bc4c --- /dev/null +++ b/specs/agent-studio/common/schemas/ExactOnSingleWordQuery.yml @@ -0,0 +1,15 @@ +ExactOnSingleWordQuery: + type: string + enum: + - attribute + - none + - word + title: exactOnSingleWordQuery + description: Determines how the [Exact ranking criterion](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/adjust-exact-settings/#turn-off-exact-for-some-attributes) + is computed when the search query has only one word. - `attribute`. The Exact + ranking criterion is 1 if the query word and attribute value are the same. For + example, a search for "road" will match the value "road", but not "road trip". - + `none`. The Exact ranking criterion is ignored on single-word searches. - `word`. The + Exact ranking criterion is 1 if the query word is found in the attribute value. The + query word must have at least 3 characters and must not be a stop word. Only + exact matches will be highlighted, partial and prefix matches won't. diff --git a/specs/agent-studio/common/schemas/ExactOnSingleWordQueryNullable.yml b/specs/agent-studio/common/schemas/ExactOnSingleWordQueryNullable.yml new file mode 100644 index 00000000000..16eef1883c4 --- /dev/null +++ b/specs/agent-studio/common/schemas/ExactOnSingleWordQueryNullable.yml @@ -0,0 +1,4 @@ +ExactOnSingleWordQueryNullable: + oneOf: + - $ref: ExactOnSingleWordQuery.yml#/ExactOnSingleWordQuery + - type: 'null' diff --git a/specs/agent-studio/common/schemas/FacetFiltersInput.yml b/specs/agent-studio/common/schemas/FacetFiltersInput.yml new file mode 100644 index 00000000000..ade73223348 --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetFiltersInput.yml @@ -0,0 +1,2 @@ +FacetFilters-Input: + $ref: FacetFiltersInputUnion.yml#/FacetFilters-InputUnion diff --git a/specs/agent-studio/common/schemas/FacetFiltersInputNullable.yml b/specs/agent-studio/common/schemas/FacetFiltersInputNullable.yml new file mode 100644 index 00000000000..7ee05d1a8cf --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetFiltersInputNullable.yml @@ -0,0 +1,4 @@ +FacetFiltersInputNullable: + oneOf: + - $ref: FacetFiltersInputUnion.yml#/FacetFilters-InputUnion + - type: 'null' diff --git a/specs/agent-studio/common/schemas/FacetFiltersInputUnion.yml b/specs/agent-studio/common/schemas/FacetFiltersInputUnion.yml new file mode 100644 index 00000000000..eb8bc13af56 --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetFiltersInputUnion.yml @@ -0,0 +1,6 @@ +FacetFilters-InputUnion: + oneOf: + - items: + $ref: FacetFiltersInputUnion.yml#/FacetFilters-InputUnion + type: array + - type: string diff --git a/specs/agent-studio/common/schemas/FacetFiltersOutput.yml b/specs/agent-studio/common/schemas/FacetFiltersOutput.yml new file mode 100644 index 00000000000..3425935017c --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetFiltersOutput.yml @@ -0,0 +1,2 @@ +FacetFilters-Output: + $ref: FacetFiltersOutputUnion.yml#/FacetFilters-OutputUnion diff --git a/specs/agent-studio/common/schemas/FacetFiltersOutputNullable.yml b/specs/agent-studio/common/schemas/FacetFiltersOutputNullable.yml new file mode 100644 index 00000000000..9a5a43112b1 --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetFiltersOutputNullable.yml @@ -0,0 +1,4 @@ +FacetFiltersOutputNullable: + oneOf: + - $ref: FacetFiltersOutputUnion.yml#/FacetFilters-OutputUnion + - type: 'null' diff --git a/specs/agent-studio/common/schemas/FacetFiltersOutputUnion.yml b/specs/agent-studio/common/schemas/FacetFiltersOutputUnion.yml new file mode 100644 index 00000000000..e2133cfc7eb --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetFiltersOutputUnion.yml @@ -0,0 +1,7 @@ +FacetFilters-OutputUnion: + oneOf: + - items: + $ref: FacetFiltersOutputUnion.yml#/FacetFilters-OutputUnion + type: array + - type: string + - type: 'null' diff --git a/specs/agent-studio/common/schemas/Facets.yml b/specs/agent-studio/common/schemas/Facets.yml new file mode 100644 index 00000000000..3aab4b90c95 --- /dev/null +++ b/specs/agent-studio/common/schemas/Facets.yml @@ -0,0 +1,13 @@ +Facets: + properties: + order: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: order + additionalProperties: true + type: object + title: facets + description: Order of facet names. diff --git a/specs/agent-studio/common/schemas/FacetsParam.yml b/specs/agent-studio/common/schemas/FacetsParam.yml new file mode 100644 index 00000000000..eb41387444e --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetsParam.yml @@ -0,0 +1,17 @@ +FacetsParam: + properties: + exposed: + type: boolean + const: false + title: exposed + default: false + default: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: default + type: object + title: facetsParam + description: A facets parameter that is always hidden from the LLM. diff --git a/specs/agent-studio/common/schemas/FacetsParamNullable.yml b/specs/agent-studio/common/schemas/FacetsParamNullable.yml new file mode 100644 index 00000000000..d459de46c00 --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetsParamNullable.yml @@ -0,0 +1,4 @@ +FacetsParamNullable: + oneOf: + - $ref: FacetsParam.yml#/FacetsParam + - type: 'null' diff --git a/specs/agent-studio/common/schemas/FacetsUnion.yml b/specs/agent-studio/common/schemas/FacetsUnion.yml new file mode 100644 index 00000000000..65f19e165c1 --- /dev/null +++ b/specs/agent-studio/common/schemas/FacetsUnion.yml @@ -0,0 +1,7 @@ +FacetsUnion: + oneOf: + - items: + type: string + type: array + - $ref: Facets.yml#/Facets + - type: 'null' diff --git a/specs/agent-studio/common/schemas/FeedbackCreationRequest.yml b/specs/agent-studio/common/schemas/FeedbackCreationRequest.yml new file mode 100644 index 00000000000..8562e331406 --- /dev/null +++ b/specs/agent-studio/common/schemas/FeedbackCreationRequest.yml @@ -0,0 +1,32 @@ +FeedbackCreationRequest: + properties: + messageId: + type: string + title: messageid + agentId: + type: string + title: agentid + vote: + $ref: VoteEnum.yml#/VoteEnum + tags: + oneOf: + - items: + type: string + maxLength: 50 + type: array + maxItems: 10 + - type: 'null' + title: tags + notes: + oneOf: + - type: string + maxLength: 1000 + - type: 'null' + title: notes + type: object + required: + - messageId + - agentId + - vote + title: feedbackCreationRequest + description: Request model for creating a feedback entry. diff --git a/specs/agent-studio/common/schemas/FeedbackResponse.yml b/specs/agent-studio/common/schemas/FeedbackResponse.yml new file mode 100644 index 00000000000..82d88fc836a --- /dev/null +++ b/specs/agent-studio/common/schemas/FeedbackResponse.yml @@ -0,0 +1,45 @@ +FeedbackResponse: + properties: + id: + type: string + title: id + agentId: + type: string + title: agentid + messageId: + type: string + title: messageid + vote: + type: integer + title: vote + tags: + items: + type: string + type: array + title: tags + notes: + oneOf: + - type: string + - type: 'null' + title: notes + model: + oneOf: + - type: string + - type: 'null' + title: model + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + type: object + required: + - id + - agentId + - messageId + - vote + - tags + - createdAt + - updatedAt + title: feedbackResponse diff --git a/specs/agent-studio/common/schemas/FeedbackUnion.yml b/specs/agent-studio/common/schemas/FeedbackUnion.yml new file mode 100644 index 00000000000..aaa10d5e7c9 --- /dev/null +++ b/specs/agent-studio/common/schemas/FeedbackUnion.yml @@ -0,0 +1,6 @@ +FeedbackUnion: + oneOf: + - items: + $ref: FeedbackResponse.yml#/FeedbackResponse + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/HTTPValidationError.yml b/specs/agent-studio/common/schemas/HTTPValidationError.yml new file mode 100644 index 00000000000..04ed703ee8f --- /dev/null +++ b/specs/agent-studio/common/schemas/HTTPValidationError.yml @@ -0,0 +1,9 @@ +HTTPValidationError: + properties: + detail: + items: + $ref: ValidationError.yml#/ValidationError + type: array + title: detail + type: object + title: hTTPValidationError diff --git a/specs/agent-studio/common/schemas/IgnorePluralsUnion.yml b/specs/agent-studio/common/schemas/IgnorePluralsUnion.yml new file mode 100644 index 00000000000..d9d51c40e96 --- /dev/null +++ b/specs/agent-studio/common/schemas/IgnorePluralsUnion.yml @@ -0,0 +1,7 @@ +IgnorePluralsUnion: + oneOf: + - type: boolean + - items: + $ref: SupportedLanguage.yml#/SupportedLanguage + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/IgnorePluralsUnionSearchParametersOutput.yml b/specs/agent-studio/common/schemas/IgnorePluralsUnionSearchParametersOutput.yml new file mode 100644 index 00000000000..284845b63a5 --- /dev/null +++ b/specs/agent-studio/common/schemas/IgnorePluralsUnionSearchParametersOutput.yml @@ -0,0 +1,7 @@ +IgnorePluralsUnionSearchParametersOutput: + oneOf: + - type: boolean + - items: + type: string + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/IndexSearchParametersInput.yml b/specs/agent-studio/common/schemas/IndexSearchParametersInput.yml new file mode 100644 index 00000000000..3a38f882718 --- /dev/null +++ b/specs/agent-studio/common/schemas/IndexSearchParametersInput.yml @@ -0,0 +1,27 @@ +IndexSearchParameters-Input: + properties: + query: + $ref: TextParamNullable.yml#/TextParamNullable + hitsPerPage: + $ref: NumberParam.yml#/NumberParam + page: + $ref: NumberParam.yml#/NumberParam + attributesToRetrieve: + $ref: StringArrayParam.yml#/StringArrayParam + responseFields: + $ref: StringArrayParam.yml#/StringArrayParam + facets: + $ref: FacetsParamNullable.yml#/FacetsParamNullable + custom: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: custom + type: object + title: indexSearchParameters + description: |- + Structured search parameters configuration for an Algolia index. + + Each parameter controls whether it is exposed to the LLM, its default value, + optional constraints, and merge behavior. diff --git a/specs/agent-studio/common/schemas/IndexSearchParametersInputNullable.yml b/specs/agent-studio/common/schemas/IndexSearchParametersInputNullable.yml new file mode 100644 index 00000000000..9bed39b302b --- /dev/null +++ b/specs/agent-studio/common/schemas/IndexSearchParametersInputNullable.yml @@ -0,0 +1,4 @@ +IndexSearchParametersInputNullable: + oneOf: + - $ref: IndexSearchParametersInput.yml#/IndexSearchParameters-Input + - type: 'null' diff --git a/specs/agent-studio/common/schemas/IndexSearchParametersOutput.yml b/specs/agent-studio/common/schemas/IndexSearchParametersOutput.yml new file mode 100644 index 00000000000..1e7c8452804 --- /dev/null +++ b/specs/agent-studio/common/schemas/IndexSearchParametersOutput.yml @@ -0,0 +1,27 @@ +IndexSearchParameters-Output: + properties: + query: + $ref: TextParamNullable.yml#/TextParamNullable + hitsPerPage: + $ref: NumberParam.yml#/NumberParam + page: + $ref: NumberParam.yml#/NumberParam + attributesToRetrieve: + $ref: StringArrayParam.yml#/StringArrayParam + responseFields: + $ref: StringArrayParam.yml#/StringArrayParam + facets: + $ref: FacetsParamNullable.yml#/FacetsParamNullable + custom: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: custom + type: object + title: indexSearchParameters + description: |- + Structured search parameters configuration for an Algolia index. + + Each parameter controls whether it is exposed to the LLM, its default value, + optional constraints, and merge behavior. diff --git a/specs/agent-studio/common/schemas/IndexSearchParametersOutputNullable.yml b/specs/agent-studio/common/schemas/IndexSearchParametersOutputNullable.yml new file mode 100644 index 00000000000..80e9dc5762b --- /dev/null +++ b/specs/agent-studio/common/schemas/IndexSearchParametersOutputNullable.yml @@ -0,0 +1,4 @@ +IndexSearchParametersOutputNullable: + oneOf: + - $ref: IndexSearchParametersOutput.yml#/IndexSearchParameters-Output + - type: 'null' diff --git a/specs/agent-studio/common/schemas/InsideBoundingBoxUnion.yml b/specs/agent-studio/common/schemas/InsideBoundingBoxUnion.yml new file mode 100644 index 00000000000..6a5f29e0596 --- /dev/null +++ b/specs/agent-studio/common/schemas/InsideBoundingBoxUnion.yml @@ -0,0 +1,9 @@ +InsideBoundingBoxUnion: + oneOf: + - type: string + - items: + items: + type: number + type: array + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/InsidePolygonUnion.yml b/specs/agent-studio/common/schemas/InsidePolygonUnion.yml new file mode 100644 index 00000000000..df5f12689ec --- /dev/null +++ b/specs/agent-studio/common/schemas/InsidePolygonUnion.yml @@ -0,0 +1,9 @@ +InsidePolygonUnion: + oneOf: + - type: string + - items: + items: + type: number + type: array + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/LocationItemUnion.yml b/specs/agent-studio/common/schemas/LocationItemUnion.yml new file mode 100644 index 00000000000..70e03365bc3 --- /dev/null +++ b/specs/agent-studio/common/schemas/LocationItemUnion.yml @@ -0,0 +1,4 @@ +LocationItemUnion: + oneOf: + - type: string + - type: integer diff --git a/specs/agent-studio/common/schemas/McpServerToolConfigInput.yml b/specs/agent-studio/common/schemas/McpServerToolConfigInput.yml new file mode 100644 index 00000000000..c466dcf66e0 --- /dev/null +++ b/specs/agent-studio/common/schemas/McpServerToolConfigInput.yml @@ -0,0 +1,42 @@ +McpServerToolConfig-Input: + properties: + url: + type: string + maxLength: 512 + minLength: 1 + title: url + transport: + type: string + const: streamable_http + title: transport + default: streamable_http + headers: + additionalProperties: + type: string + type: object + title: headers + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: mcp_tools + title: type + default: mcp_tools + id: + oneOf: + - type: string + - type: 'null' + title: id + description: Stable unique identifier for this MCP tool. + allowedTools: + $ref: AllowedToolsUnion.yml#/AllowedToolsUnion + type: object + required: + - type + - url + - headers + - name + title: mcpServerToolConfig diff --git a/specs/agent-studio/common/schemas/McpServerToolConfigOutput.yml b/specs/agent-studio/common/schemas/McpServerToolConfigOutput.yml new file mode 100644 index 00000000000..61549a51e00 --- /dev/null +++ b/specs/agent-studio/common/schemas/McpServerToolConfigOutput.yml @@ -0,0 +1,42 @@ +McpServerToolConfig-Output: + properties: + url: + type: string + maxLength: 512 + minLength: 1 + title: url + transport: + type: string + const: streamable_http + title: transport + default: streamable_http + headers: + additionalProperties: + type: string + type: object + title: headers + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: mcp_tools + title: type + default: mcp_tools + id: + oneOf: + - type: string + - type: 'null' + title: id + description: Stable unique identifier for this MCP tool. + allowedTools: + $ref: AllowedToolsUnion.yml#/AllowedToolsUnion + type: object + required: + - type + - url + - headers + - name + title: mcpServerToolConfig diff --git a/specs/agent-studio/common/schemas/McpToolConfig.yml b/specs/agent-studio/common/schemas/McpToolConfig.yml new file mode 100644 index 00000000000..50f6ad8b45c --- /dev/null +++ b/specs/agent-studio/common/schemas/McpToolConfig.yml @@ -0,0 +1,17 @@ +McpToolConfig: + properties: + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + default: false + alias: + oneOf: + - type: string + maxLength: 32 + minLength: 3 + - type: 'null' + title: alias + type: object + title: mcpToolConfig diff --git a/specs/agent-studio/common/schemas/MemoryRecord.yml b/specs/agent-studio/common/schemas/MemoryRecord.yml new file mode 100644 index 00000000000..99febc3dabf --- /dev/null +++ b/specs/agent-studio/common/schemas/MemoryRecord.yml @@ -0,0 +1,91 @@ +MemoryRecord: + properties: + memoryType: + $ref: MemoryType.yml#/MemoryType + episode: + $ref: EpisodeNullable.yml#/EpisodeNullable + text: + type: string + maxLength: 2000 + minLength: 1 + title: text + description: Self-contained, first-person memory for long-term recall. + rawExtract: + type: string + maxLength: 5000 + minLength: 1 + title: rawextract + description: Verbatim conversation extract, not paraphrased. + keywords: + items: + type: string + type: array + title: keywords + description: '5-20 free-form keywords: entities, context, search terms (any + words).' + topics: + items: + type: string + type: array + title: topics + description: '2-4 topics ONLY from this list: [complaints, entertainment, family, + feedback, finance, food, goals, health, history, hobbies, learning, praise, + preferences, schedule, shopping, technical, travel, work].' + _tags: + items: + type: string + type: array + title: tags + description: Arbitrary labels/themes for flexible categorization (e.g., 'Q1-goals', + 'paris-trip', 'vip-customer'). + recallTriggers: + items: + type: string + type: array + title: recalltriggers + description: 3-5 natural phrases that should trigger this memory. + objectID: + oneOf: + - type: string + - type: 'null' + title: objectid + description: ObjectID of existing memory to update. Leave empty for new memory. + appId: + type: string + title: appid + description: Application ID. + default: '' + agentIDs: + items: + type: string + type: array + title: agentids + description: 'Agent IDs with access: [''agent1''], [''*''] for all, [''*'', + ''-agent1''] to exclude.' + userID: + type: string + title: userid + description: User ID. + default: '' + createdAt: + type: integer + title: createdat + description: Epoch seconds. + default: 0 + updatedAt: + type: integer + title: updatedat + description: Epoch seconds. + default: 0 + type: object + required: + - text + - rawExtract + title: memoryRecord + description: |- + Universal storage model for all memory types (semantic, episodic). + + This is the ONLY model that touches storage (Algolia). Domain models (SemanticMemory, EpisodicMemory) + are used for LLM extraction and converted to MemoryRecord before saving. + + See https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#memory-types for memory type definitions. diff --git a/specs/agent-studio/common/schemas/MemoryType.yml b/specs/agent-studio/common/schemas/MemoryType.yml new file mode 100644 index 00000000000..3557e5fde95 --- /dev/null +++ b/specs/agent-studio/common/schemas/MemoryType.yml @@ -0,0 +1,9 @@ +MemoryType: + type: string + enum: + - semantic + - episodic + title: memoryType + description: |- + Memory types implemented so far. + Follows LangMem's ontology: https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#memory-types. diff --git a/specs/agent-studio/common/schemas/MessagePart.yml b/specs/agent-studio/common/schemas/MessagePart.yml new file mode 100644 index 00000000000..c1244ec0e49 --- /dev/null +++ b/specs/agent-studio/common/schemas/MessagePart.yml @@ -0,0 +1,19 @@ +MessagePart: + oneOf: + - $ref: TextPart.yml#/TextPart + - $ref: ToolCallPart.yml#/ToolCallPart + - $ref: ToolResultPart.yml#/ToolResultPart + - $ref: StartPart.yml#/StartPart + - $ref: StartStepPart.yml#/StartStepPart + - $ref: ReasoningPart.yml#/ReasoningPart + - $ref: ToolApprovalRequestPart.yml#/ToolApprovalRequestPart + discriminator: + propertyName: type + mapping: + reasoning: ReasoningPart.yml#/ReasoningPart + start: StartPart.yml#/StartPart + start-step: StartStepPart.yml#/StartStepPart + text: TextPart.yml#/TextPart + tool-approval-request: ToolApprovalRequestPart.yml#/ToolApprovalRequestPart + tool-call: ToolCallPart.yml#/ToolCallPart + tool-result: ToolResultPart.yml#/ToolResultPart diff --git a/specs/agent-studio/common/schemas/MessageResponse.yml b/specs/agent-studio/common/schemas/MessageResponse.yml new file mode 100644 index 00000000000..54ae1278f8a --- /dev/null +++ b/specs/agent-studio/common/schemas/MessageResponse.yml @@ -0,0 +1,46 @@ +MessageResponse: + properties: + id: + type: string + title: id + conversationId: + type: string + title: conversationid + role: + $ref: MessageRole.yml#/MessageRole + parts: + items: + $ref: MessagePart.yml#/MessagePart + type: array + title: parts + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + model: + oneOf: + - type: string + - type: 'null' + title: model + inputTokens: + oneOf: + - type: integer + - type: 'null' + title: inputtokens + outputTokens: + oneOf: + - type: integer + - type: 'null' + title: outputtokens + type: object + required: + - id + - conversationId + - role + - parts + - createdAt + - updatedAt + title: messageResponse + description: Response model for a message. diff --git a/specs/agent-studio/common/schemas/MessageRole.yml b/specs/agent-studio/common/schemas/MessageRole.yml new file mode 100644 index 00000000000..ae35c719071 --- /dev/null +++ b/specs/agent-studio/common/schemas/MessageRole.yml @@ -0,0 +1,7 @@ +MessageRole: + type: string + enum: + - user + - assistant + title: messageRole + description: Role of a message in the conversation. diff --git a/specs/agent-studio/common/schemas/MessageV4.yml b/specs/agent-studio/common/schemas/MessageV4.yml new file mode 100644 index 00000000000..ed94c151a98 --- /dev/null +++ b/specs/agent-studio/common/schemas/MessageV4.yml @@ -0,0 +1,9 @@ +MessageV4: + oneOf: + - $ref: UserMessageV4.yml#/UserMessageV4 + - $ref: AssistantMessageV4.yml#/AssistantMessageV4 + discriminator: + propertyName: role + mapping: + assistant: AssistantMessageV4.yml#/AssistantMessageV4 + user: UserMessageV4.yml#/UserMessageV4 diff --git a/specs/agent-studio/common/schemas/MessageV5.yml b/specs/agent-studio/common/schemas/MessageV5.yml new file mode 100644 index 00000000000..d64ced4ded9 --- /dev/null +++ b/specs/agent-studio/common/schemas/MessageV5.yml @@ -0,0 +1,9 @@ +MessageV5: + oneOf: + - $ref: UserMessageV5.yml#/UserMessageV5 + - $ref: AssistantMessageV5.yml#/AssistantMessageV5 + discriminator: + propertyName: role + mapping: + assistant: AssistantMessageV5.yml#/AssistantMessageV5 + user: UserMessageV5.yml#/UserMessageV5 diff --git a/specs/agent-studio/common/schemas/MessagesNullableUnion.yml b/specs/agent-studio/common/schemas/MessagesNullableUnion.yml new file mode 100644 index 00000000000..766b00b1e46 --- /dev/null +++ b/specs/agent-studio/common/schemas/MessagesNullableUnion.yml @@ -0,0 +1,4 @@ +MessagesNullableUnion: + oneOf: + - $ref: MessagesNullableUnionUnion.yml#/MessagesNullableUnionUnion + - type: 'null' diff --git a/specs/agent-studio/common/schemas/MessagesNullableUnionUnion.yml b/specs/agent-studio/common/schemas/MessagesNullableUnionUnion.yml new file mode 100644 index 00000000000..ecb91364852 --- /dev/null +++ b/specs/agent-studio/common/schemas/MessagesNullableUnionUnion.yml @@ -0,0 +1,8 @@ +MessagesNullableUnionUnion: + oneOf: + - items: + $ref: MessageV4.yml#/MessageV4 + type: array + - items: + $ref: MessageV5.yml#/MessageV5 + type: array diff --git a/specs/agent-studio/common/schemas/MessagesUnion.yml b/specs/agent-studio/common/schemas/MessagesUnion.yml new file mode 100644 index 00000000000..30b4734b4e6 --- /dev/null +++ b/specs/agent-studio/common/schemas/MessagesUnion.yml @@ -0,0 +1,8 @@ +MessagesUnion: + oneOf: + - items: + $ref: MessageV4.yml#/MessageV4 + type: array + - items: + $ref: MessageV5.yml#/MessageV5 + type: array diff --git a/specs/agent-studio/common/schemas/NaturalLanguagesUnion.yml b/specs/agent-studio/common/schemas/NaturalLanguagesUnion.yml new file mode 100644 index 00000000000..2af1e421a6a --- /dev/null +++ b/specs/agent-studio/common/schemas/NaturalLanguagesUnion.yml @@ -0,0 +1,6 @@ +NaturalLanguagesUnion: + oneOf: + - items: + $ref: SupportedLanguage.yml#/SupportedLanguage + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/NumberParam.yml b/specs/agent-studio/common/schemas/NumberParam.yml new file mode 100644 index 00000000000..49ecffea313 --- /dev/null +++ b/specs/agent-studio/common/schemas/NumberParam.yml @@ -0,0 +1,17 @@ +NumberParam: + properties: + exposed: + type: boolean + title: exposed + default: + oneOf: + - type: integer + - type: 'null' + title: default + constraint: + $ref: NumberParamConstraintNullable.yml#/NumberParamConstraintNullable + type: object + required: + - exposed + title: numberParam + description: A number search parameter with exposure control and optional constraints. diff --git a/specs/agent-studio/common/schemas/NumberParamConstraint.yml b/specs/agent-studio/common/schemas/NumberParamConstraint.yml new file mode 100644 index 00000000000..d420c5c9746 --- /dev/null +++ b/specs/agent-studio/common/schemas/NumberParamConstraint.yml @@ -0,0 +1,15 @@ +NumberParamConstraint: + properties: + min: + oneOf: + - type: integer + - type: 'null' + title: min + max: + oneOf: + - type: integer + - type: 'null' + title: max + type: object + title: numberParamConstraint + description: Constraints for a number parameter. diff --git a/specs/agent-studio/common/schemas/NumberParamConstraintNullable.yml b/specs/agent-studio/common/schemas/NumberParamConstraintNullable.yml new file mode 100644 index 00000000000..b25e8b1c5fa --- /dev/null +++ b/specs/agent-studio/common/schemas/NumberParamConstraintNullable.yml @@ -0,0 +1,4 @@ +NumberParamConstraintNullable: + oneOf: + - $ref: NumberParamConstraint.yml#/NumberParamConstraint + - type: 'null' diff --git a/specs/agent-studio/common/schemas/NumericFiltersUnion.yml b/specs/agent-studio/common/schemas/NumericFiltersUnion.yml new file mode 100644 index 00000000000..ce1833787d1 --- /dev/null +++ b/specs/agent-studio/common/schemas/NumericFiltersUnion.yml @@ -0,0 +1,7 @@ +NumericFiltersUnion: + oneOf: + - type: string + - type: array + items: + $ref: NumericFiltersUnion.yml#/NumericFiltersUnion + - type: 'null' diff --git a/specs/agent-studio/common/schemas/OpenAICompatibleProviderInputInput.yml b/specs/agent-studio/common/schemas/OpenAICompatibleProviderInputInput.yml new file mode 100644 index 00000000000..c9dbaeeca5f --- /dev/null +++ b/specs/agent-studio/common/schemas/OpenAICompatibleProviderInputInput.yml @@ -0,0 +1,27 @@ +OpenAICompatibleProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + baseUrl: + type: string + maxLength: 2083 + minLength: 1 + title: baseurl + defaultModel: + type: string + minLength: 1 + title: defaultmodel + description: Default model for this provider. Used for validation and as fallback + when no model is specified at agent level. + type: object + required: + - apiKey + - baseUrl + - defaultModel + title: openAICompatibleProviderInput + description: |- + OpenAI-compatible provider input. + Contrary to the OpenAIProviderInput, the base_url is required. + A model is required to verify connectivity and get saved as the default model. + This can later be changed at the Agent level. diff --git a/specs/agent-studio/common/schemas/OpenAICompatibleProviderInputOutput.yml b/specs/agent-studio/common/schemas/OpenAICompatibleProviderInputOutput.yml new file mode 100644 index 00000000000..ad2a0c1ebe0 --- /dev/null +++ b/specs/agent-studio/common/schemas/OpenAICompatibleProviderInputOutput.yml @@ -0,0 +1,25 @@ +OpenAICompatibleProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + baseUrl: + type: string + title: baseurl + defaultModel: + type: string + minLength: 1 + title: defaultmodel + description: Default model for this provider. Used for validation and as fallback + when no model is specified at agent level. + type: object + required: + - apiKey + - baseUrl + - defaultModel + title: openAICompatibleProviderInput + description: |- + OpenAI-compatible provider input. + Contrary to the OpenAIProviderInput, the base_url is required. + A model is required to verify connectivity and get saved as the default model. + This can later be changed at the Agent level. diff --git a/specs/agent-studio/common/schemas/OpenAIProviderInputInput.yml b/specs/agent-studio/common/schemas/OpenAIProviderInputInput.yml new file mode 100644 index 00000000000..0490cb18fb5 --- /dev/null +++ b/specs/agent-studio/common/schemas/OpenAIProviderInputInput.yml @@ -0,0 +1,17 @@ +OpenAIProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + maxLength: 2083 + minLength: 1 + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: openAIProviderInput + description: OpenAI-specific provider input. diff --git a/specs/agent-studio/common/schemas/OpenAIProviderInputOutput.yml b/specs/agent-studio/common/schemas/OpenAIProviderInputOutput.yml new file mode 100644 index 00000000000..86a179737a4 --- /dev/null +++ b/specs/agent-studio/common/schemas/OpenAIProviderInputOutput.yml @@ -0,0 +1,15 @@ +OpenAIProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: openAIProviderInput + description: OpenAI-specific provider input. diff --git a/specs/agent-studio/common/schemas/OptionalFiltersUnion.yml b/specs/agent-studio/common/schemas/OptionalFiltersUnion.yml new file mode 100644 index 00000000000..ac7b016d6c7 --- /dev/null +++ b/specs/agent-studio/common/schemas/OptionalFiltersUnion.yml @@ -0,0 +1,7 @@ +OptionalFiltersUnion: + oneOf: + - type: string + - type: array + items: + $ref: OptionalFiltersUnion.yml#/OptionalFiltersUnion + - type: 'null' diff --git a/specs/agent-studio/common/schemas/OptionalWordsUnion.yml b/specs/agent-studio/common/schemas/OptionalWordsUnion.yml new file mode 100644 index 00000000000..854e15d1264 --- /dev/null +++ b/specs/agent-studio/common/schemas/OptionalWordsUnion.yml @@ -0,0 +1,7 @@ +OptionalWordsUnion: + oneOf: + - type: string + - items: + type: string + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/PaginatedAgentsResponse.yml b/specs/agent-studio/common/schemas/PaginatedAgentsResponse.yml new file mode 100644 index 00000000000..26763ec9cf4 --- /dev/null +++ b/specs/agent-studio/common/schemas/PaginatedAgentsResponse.yml @@ -0,0 +1,14 @@ +PaginatedAgentsResponse: + properties: + data: + items: + $ref: AgentWithVersionResponse.yml#/AgentWithVersionResponse + type: array + title: data + pagination: + $ref: PaginationMetadata.yml#/PaginationMetadata + type: object + required: + - data + - pagination + title: paginatedAgentsResponse diff --git a/specs/agent-studio/common/schemas/PaginatedConversationsResponse.yml b/specs/agent-studio/common/schemas/PaginatedConversationsResponse.yml new file mode 100644 index 00000000000..63f8b7a3257 --- /dev/null +++ b/specs/agent-studio/common/schemas/PaginatedConversationsResponse.yml @@ -0,0 +1,14 @@ +PaginatedConversationsResponse: + properties: + data: + items: + $ref: ConversationBaseResponse.yml#/ConversationBaseResponse + type: array + title: data + pagination: + $ref: PaginationMetadata.yml#/PaginationMetadata + type: object + required: + - data + - pagination + title: paginatedConversationsResponse diff --git a/specs/agent-studio/common/schemas/PaginatedProviderAuthenticationsResponse.yml b/specs/agent-studio/common/schemas/PaginatedProviderAuthenticationsResponse.yml new file mode 100644 index 00000000000..e2bc37968b7 --- /dev/null +++ b/specs/agent-studio/common/schemas/PaginatedProviderAuthenticationsResponse.yml @@ -0,0 +1,14 @@ +PaginatedProviderAuthenticationsResponse: + properties: + data: + items: + $ref: ProviderAuthenticationResponse.yml#/ProviderAuthenticationResponse + type: array + title: data + pagination: + $ref: PaginationMetadata.yml#/PaginationMetadata + type: object + required: + - data + - pagination + title: paginatedProviders diff --git a/specs/agent-studio/common/schemas/PaginatedSecretKeysResponse.yml b/specs/agent-studio/common/schemas/PaginatedSecretKeysResponse.yml new file mode 100644 index 00000000000..8c1bb5f9455 --- /dev/null +++ b/specs/agent-studio/common/schemas/PaginatedSecretKeysResponse.yml @@ -0,0 +1,14 @@ +PaginatedSecretKeysResponse: + properties: + data: + items: + $ref: SecretKeyResponse.yml#/SecretKeyResponse + type: array + title: data + pagination: + $ref: PaginationMetadata.yml#/PaginationMetadata + type: object + required: + - data + - pagination + title: paginatedSecretKeysResponse diff --git a/specs/agent-studio/common/schemas/PaginationMetadata.yml b/specs/agent-studio/common/schemas/PaginationMetadata.yml new file mode 100644 index 00000000000..06fa38b24fb --- /dev/null +++ b/specs/agent-studio/common/schemas/PaginationMetadata.yml @@ -0,0 +1,21 @@ +PaginationMetadata: + properties: + page: + type: integer + title: page + limit: + type: integer + title: limit + totalCount: + type: integer + title: totalcount + totalPages: + type: integer + title: totalpages + type: object + required: + - page + - limit + - totalCount + - totalPages + title: paginationMetadata diff --git a/specs/agent-studio/common/schemas/ProviderAuthenticationCreate.yml b/specs/agent-studio/common/schemas/ProviderAuthenticationCreate.yml new file mode 100644 index 00000000000..61a2e3ade7d --- /dev/null +++ b/specs/agent-studio/common/schemas/ProviderAuthenticationCreate.yml @@ -0,0 +1,18 @@ +ProviderAuthenticationCreate: + properties: + name: + type: string + maxLength: 128 + minLength: 1 + title: name + providerName: + $ref: ProviderName.yml#/ProviderName + input: + $ref: ProviderInput.yml#/ProviderInput + additionalProperties: false + type: object + required: + - name + - providerName + - input + title: providerAuthenticationCreate diff --git a/specs/agent-studio/common/schemas/ProviderAuthenticationPatch.yml b/specs/agent-studio/common/schemas/ProviderAuthenticationPatch.yml new file mode 100644 index 00000000000..892e3681722 --- /dev/null +++ b/specs/agent-studio/common/schemas/ProviderAuthenticationPatch.yml @@ -0,0 +1,14 @@ +ProviderAuthenticationPatch: + properties: + name: + oneOf: + - type: string + maxLength: 128 + minLength: 1 + - type: 'null' + title: name + input: + $ref: ProviderInputNullable.yml#/ProviderInputNullable + additionalProperties: false + type: object + title: providerPatch diff --git a/specs/agent-studio/common/schemas/ProviderAuthenticationResponse.yml b/specs/agent-studio/common/schemas/ProviderAuthenticationResponse.yml new file mode 100644 index 00000000000..5cf04ed42e4 --- /dev/null +++ b/specs/agent-studio/common/schemas/ProviderAuthenticationResponse.yml @@ -0,0 +1,33 @@ +ProviderAuthenticationResponse: + properties: + id: + type: string + title: id + name: + type: string + title: name + providerName: + type: string + title: providername + input: + $ref: ProviderInputProviderAuthenticationResponse.yml#/ProviderInputProviderAuthenticationResponse + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastUsedAt: + oneOf: + - type: string + - type: 'null' + title: lastusedat + type: object + required: + - id + - name + - providerName + - input + - createdAt + - updatedAt + title: provider diff --git a/specs/agent-studio/common/schemas/ProviderInput.yml b/specs/agent-studio/common/schemas/ProviderInput.yml new file mode 100644 index 00000000000..ccf3aab9d85 --- /dev/null +++ b/specs/agent-studio/common/schemas/ProviderInput.yml @@ -0,0 +1,7 @@ +ProviderInput: + oneOf: + - $ref: OpenAIProviderInputInput.yml#/OpenAIProviderInput-Input + - $ref: AzureOpenAIProviderInputInput.yml#/AzureOpenAIProviderInput-Input + - $ref: OpenAICompatibleProviderInputInput.yml#/OpenAICompatibleProviderInput-Input + - $ref: BaseProviderInput.yml#/BaseProviderInput + - $ref: AnthropicProviderInputInput.yml#/AnthropicProviderInput-Input diff --git a/specs/agent-studio/common/schemas/ProviderInputNullable.yml b/specs/agent-studio/common/schemas/ProviderInputNullable.yml new file mode 100644 index 00000000000..494101a0d5e --- /dev/null +++ b/specs/agent-studio/common/schemas/ProviderInputNullable.yml @@ -0,0 +1,8 @@ +ProviderInputNullable: + oneOf: + - $ref: OpenAIProviderInputInput.yml#/OpenAIProviderInput-Input + - $ref: AzureOpenAIProviderInputInput.yml#/AzureOpenAIProviderInput-Input + - $ref: OpenAICompatibleProviderInputInput.yml#/OpenAICompatibleProviderInput-Input + - $ref: BaseProviderInput.yml#/BaseProviderInput + - $ref: AnthropicProviderInputInput.yml#/AnthropicProviderInput-Input + - type: 'null' diff --git a/specs/agent-studio/common/schemas/ProviderInputProviderAuthenticationResponse.yml b/specs/agent-studio/common/schemas/ProviderInputProviderAuthenticationResponse.yml new file mode 100644 index 00000000000..86051bbc758 --- /dev/null +++ b/specs/agent-studio/common/schemas/ProviderInputProviderAuthenticationResponse.yml @@ -0,0 +1,7 @@ +ProviderInputProviderAuthenticationResponse: + oneOf: + - $ref: OpenAIProviderInputOutput.yml#/OpenAIProviderInput-Output + - $ref: AzureOpenAIProviderInputOutput.yml#/AzureOpenAIProviderInput-Output + - $ref: OpenAICompatibleProviderInputOutput.yml#/OpenAICompatibleProviderInput-Output + - $ref: BaseProviderInput.yml#/BaseProviderInput + - $ref: AnthropicProviderInputOutput.yml#/AnthropicProviderInput-Output diff --git a/specs/agent-studio/common/schemas/ProviderName.yml b/specs/agent-studio/common/schemas/ProviderName.yml new file mode 100644 index 00000000000..9d88ebee355 --- /dev/null +++ b/specs/agent-studio/common/schemas/ProviderName.yml @@ -0,0 +1,10 @@ +ProviderName: + type: string + enum: + - openai + - azure_openai + - google_genai + - deepseek + - openai_compatible + - anthropic + title: providerName diff --git a/specs/agent-studio/common/schemas/QueryLanguagesUnion.yml b/specs/agent-studio/common/schemas/QueryLanguagesUnion.yml new file mode 100644 index 00000000000..a118edbd054 --- /dev/null +++ b/specs/agent-studio/common/schemas/QueryLanguagesUnion.yml @@ -0,0 +1,6 @@ +QueryLanguagesUnion: + oneOf: + - items: + $ref: SupportedLanguage.yml#/SupportedLanguage + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/QueryType.yml b/specs/agent-studio/common/schemas/QueryType.yml new file mode 100644 index 00000000000..b4e27fe1d62 --- /dev/null +++ b/specs/agent-studio/common/schemas/QueryType.yml @@ -0,0 +1,12 @@ +QueryType: + type: string + enum: + - prefixLast + - prefixAll + - prefixNone + title: queryType + description: Determines if and how query words are interpreted as prefixes. By + default, only the last query word is treated as a prefix (`prefixLast`). To turn + off prefix search, use `prefixNone`. Avoid `prefixAll`, which treats all query + words as prefixes. This might lead to counterintuitive results and makes your + search slower. For more information, see [Prefix searching](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/prefix-searching). diff --git a/specs/agent-studio/common/schemas/QueryTypeNullable.yml b/specs/agent-studio/common/schemas/QueryTypeNullable.yml new file mode 100644 index 00000000000..395345c14de --- /dev/null +++ b/specs/agent-studio/common/schemas/QueryTypeNullable.yml @@ -0,0 +1,4 @@ +QueryTypeNullable: + oneOf: + - $ref: QueryType.yml#/QueryType + - type: 'null' diff --git a/specs/agent-studio/common/schemas/ReRankingApplyFilterUnion.yml b/specs/agent-studio/common/schemas/ReRankingApplyFilterUnion.yml new file mode 100644 index 00000000000..fe5c8146bc9 --- /dev/null +++ b/specs/agent-studio/common/schemas/ReRankingApplyFilterUnion.yml @@ -0,0 +1,8 @@ +ReRankingApplyFilterUnion: + oneOf: + - type: string + - items: + type: object + additionalProperties: true + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/ReasoningPart.yml b/specs/agent-studio/common/schemas/ReasoningPart.yml new file mode 100644 index 00000000000..3f7f6d99408 --- /dev/null +++ b/specs/agent-studio/common/schemas/ReasoningPart.yml @@ -0,0 +1,15 @@ +ReasoningPart: + properties: + type: + type: string + const: reasoning + title: type + default: reasoning + text: + type: string + title: text + type: object + required: + - type + - text + title: reasoningPart diff --git a/specs/agent-studio/common/schemas/ReasoningPartV4.yml b/specs/agent-studio/common/schemas/ReasoningPartV4.yml new file mode 100644 index 00000000000..309a68d4c37 --- /dev/null +++ b/specs/agent-studio/common/schemas/ReasoningPartV4.yml @@ -0,0 +1,14 @@ +ReasoningPartV4: + properties: + type: + type: string + const: reasoning + title: type + default: reasoning + reasoning: + type: string + title: reasoning + type: object + required: + - reasoning + title: reasoningPartV4 diff --git a/specs/agent-studio/common/schemas/ReasoningPartV5.yml b/specs/agent-studio/common/schemas/ReasoningPartV5.yml new file mode 100644 index 00000000000..ceddfa4de63 --- /dev/null +++ b/specs/agent-studio/common/schemas/ReasoningPartV5.yml @@ -0,0 +1,14 @@ +ReasoningPartV5: + properties: + type: + type: string + const: reasoning + title: type + default: reasoning + text: + type: string + title: text + type: object + required: + - text + title: reasoningPartV5 diff --git a/specs/agent-studio/common/schemas/RemoveStopWordsUnion.yml b/specs/agent-studio/common/schemas/RemoveStopWordsUnion.yml new file mode 100644 index 00000000000..615b2d4df52 --- /dev/null +++ b/specs/agent-studio/common/schemas/RemoveStopWordsUnion.yml @@ -0,0 +1,7 @@ +RemoveStopWordsUnion: + oneOf: + - type: boolean + - items: + $ref: SupportedLanguage.yml#/SupportedLanguage + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/RemoveStopWordsUnionSearchParametersOutput.yml b/specs/agent-studio/common/schemas/RemoveStopWordsUnionSearchParametersOutput.yml new file mode 100644 index 00000000000..f335e6ac4d2 --- /dev/null +++ b/specs/agent-studio/common/schemas/RemoveStopWordsUnionSearchParametersOutput.yml @@ -0,0 +1,7 @@ +RemoveStopWordsUnionSearchParametersOutput: + oneOf: + - type: boolean + - items: + type: string + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/RemoveWordsIfNoResults.yml b/specs/agent-studio/common/schemas/RemoveWordsIfNoResults.yml new file mode 100644 index 00000000000..e315aa91742 --- /dev/null +++ b/specs/agent-studio/common/schemas/RemoveWordsIfNoResults.yml @@ -0,0 +1,16 @@ +RemoveWordsIfNoResults: + type: string + enum: + - none + - lastWords + - firstWords + - allOptional + title: removeWordsIfNoResults + description: Strategy for removing words from the query when it doesn't return any + results. This helps to avoid returning empty search results. - `none`. No words + are removed when a query doesn't return results. - `lastWords`. Treat the last + (then second to last, then third to last) word as optional, until there are + results or at most 5 words have been removed. - `firstWords`. Treat the first + (then second, then third) word as optional, until there are results or at most + 5 words have been removed. - `allOptional`. Treat all words as optional. For + more information, see [Remove words to improve results](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/empty-or-insufficient-results/in-depth/why-use-remove-words-if-no-results). diff --git a/specs/agent-studio/common/schemas/RemoveWordsIfNoResultsNullable.yml b/specs/agent-studio/common/schemas/RemoveWordsIfNoResultsNullable.yml new file mode 100644 index 00000000000..790eefd6f8c --- /dev/null +++ b/specs/agent-studio/common/schemas/RemoveWordsIfNoResultsNullable.yml @@ -0,0 +1,4 @@ +RemoveWordsIfNoResultsNullable: + oneOf: + - $ref: RemoveWordsIfNoResults.yml#/RemoveWordsIfNoResults + - type: 'null' diff --git a/specs/agent-studio/common/schemas/SearchParametersInput.yml b/specs/agent-studio/common/schemas/SearchParametersInput.yml new file mode 100644 index 00000000000..d45e9dbd3c8 --- /dev/null +++ b/specs/agent-studio/common/schemas/SearchParametersInput.yml @@ -0,0 +1,290 @@ +SearchParameters-Input: + properties: + queryType: + $ref: QueryTypeNullable.yml#/QueryTypeNullable + similarQuery: + oneOf: + - type: string + - type: 'null' + title: similarquery + queryLanguages: + $ref: QueryLanguagesUnion.yml#/QueryLanguagesUnion + advancedSyntax: + oneOf: + - type: boolean + - type: 'null' + title: advancedsyntax + advancedSyntaxFeatures: + $ref: AdvancedSyntaxFeaturesUnion.yml#/AdvancedSyntaxFeaturesUnion + alternativesAsExact: + $ref: AlternativesAsExactUnion.yml#/AlternativesAsExactUnion + decompoundQuery: + oneOf: + - type: boolean + - type: 'null' + title: decompoundquery + typoTolerance: + $ref: TypoToleranceUnion.yml#/TypoToleranceUnion + allowTyposOnNumericTokens: + oneOf: + - type: boolean + - type: 'null' + title: allowtyposonnumerictokens + minWordSizeFor1Typo: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor1Typo + minWordSizeFor2Typos: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor2Typos + disableTypoToleranceOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disabletypotoleranceonattributes + filters: + oneOf: + - type: string + - type: 'null' + title: filters + facetFilters: + $ref: FacetFiltersInputNullable.yml#/FacetFiltersInputNullable + facets: + $ref: FacetsUnion.yml#/FacetsUnion + maxValuesPerFacet: + oneOf: + - type: integer + - type: 'null' + title: maxvaluesperfacet + maxFacetHits: + oneOf: + - type: integer + - type: 'null' + title: maxfacethits + facetingAfterDistinct: + oneOf: + - type: boolean + - type: 'null' + title: facetingafterdistinct + sortFacetValuesBy: + oneOf: + - type: string + - type: 'null' + title: sortfacetvaluesby + numericFilters: + $ref: NumericFiltersUnion.yml#/NumericFiltersUnion + tagFilters: + $ref: TagFiltersUnion.yml#/TagFiltersUnion + sumOrFiltersScores: + oneOf: + - type: boolean + - type: 'null' + title: sumorfiltersscores + aroundLatLng: + oneOf: + - type: string + - type: 'null' + title: aroundlatlng + aroundLatLngViaIp: + oneOf: + - type: boolean + - type: 'null' + title: aroundlatlngviaip + aroundRadius: + $ref: AroundRadiusUnion.yml#/AroundRadiusUnion + aroundPrecision: + $ref: AroundPrecisionUnion.yml#/AroundPrecisionUnion + minimumAroundRadius: + oneOf: + - type: integer + - type: 'null' + title: minimumaroundradius + insideBoundingBox: + $ref: InsideBoundingBoxUnion.yml#/InsideBoundingBoxUnion + insidePolygon: + $ref: InsidePolygonUnion.yml#/InsidePolygonUnion + attributesToRetrieve: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestoretrieve + attributesToSnippet: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestosnippet + snippetEllipsisText: + oneOf: + - type: string + - type: 'null' + title: snippetellipsistext + restrictHighlightAndSnippetArrays: + oneOf: + - type: boolean + - type: 'null' + title: restricthighlightandsnippetarrays + page: + oneOf: + - type: integer + - type: 'null' + title: page + offset: + oneOf: + - type: integer + - type: 'null' + title: offset + hitsPerPage: + oneOf: + - type: integer + - type: 'null' + title: hitsperpage + length: + oneOf: + - type: integer + - type: 'null' + title: length + getRankingInfo: + oneOf: + - type: boolean + - type: 'null' + title: getrankinginfo + relevancyStrictness: + oneOf: + - type: integer + - type: 'null' + title: relevancystrictness + minProximity: + oneOf: + - type: integer + - type: 'null' + title: minproximity + attributeCriteriaComputedByMinProximity: + oneOf: + - type: boolean + - type: 'null' + title: attributecriteriacomputedbyminproximity + distinct: + $ref: DistinctUnion.yml#/DistinctUnion + enableRules: + oneOf: + - type: boolean + - type: 'null' + title: enablerules + enablePersonalization: + oneOf: + - type: boolean + - type: 'null' + title: enablepersonalization + personalizationImpact: + oneOf: + - type: integer + - type: 'null' + title: personalizationimpact + enableAbTest: + oneOf: + - type: boolean + - type: 'null' + title: enableabtest + enableReRanking: + oneOf: + - type: boolean + - type: 'null' + title: enablereranking + reRankingApplyFilter: + $ref: ReRankingApplyFilterUnion.yml#/ReRankingApplyFilterUnion + ruleContexts: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: rulecontexts + removeStopWords: + $ref: RemoveStopWordsUnion.yml#/RemoveStopWordsUnion + ignorePlurals: + $ref: IgnorePluralsUnion.yml#/IgnorePluralsUnion + removeWordsIfNoResults: + $ref: RemoveWordsIfNoResultsNullable.yml#/RemoveWordsIfNoResultsNullable + optionalWords: + $ref: OptionalWordsUnion.yml#/OptionalWordsUnion + optionalFilters: + $ref: OptionalFiltersUnion.yml#/OptionalFiltersUnion + synonyms: + oneOf: + - type: boolean + - type: 'null' + title: synonyms + replaceSynonymsInHighlight: + oneOf: + - type: boolean + - type: 'null' + title: replacesynonymsinhighlight + analytics: + oneOf: + - type: boolean + - type: 'null' + title: analytics + analyticsTags: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: analyticstags + clickAnalytics: + oneOf: + - type: boolean + - type: 'null' + title: clickanalytics + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + restrictSearchableAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: restrictsearchableattributes + disableExactOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disableexactonattributes + exactOnSingleWordQuery: + $ref: ExactOnSingleWordQueryNullable.yml#/ExactOnSingleWordQueryNullable + naturalLanguages: + $ref: NaturalLanguagesUnion.yml#/NaturalLanguagesUnion + percentileComputation: + oneOf: + - type: boolean + - type: 'null' + title: percentilecomputation + explain: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: explain + type: object + title: searchParameters + description: |- + Algolia Search API parameters that can be predefined for the search tool. + Reference: https://www.algolia.com/doc/api-reference/search-api-parameters/ + + The parameters that seemed irrelevant for the search tool have been commented out. + Uses types from algoliasearch.search.models for better type safety. diff --git a/specs/agent-studio/common/schemas/SearchParametersInputNullable.yml b/specs/agent-studio/common/schemas/SearchParametersInputNullable.yml new file mode 100644 index 00000000000..675b02f4a72 --- /dev/null +++ b/specs/agent-studio/common/schemas/SearchParametersInputNullable.yml @@ -0,0 +1,4 @@ +SearchParametersInputNullable: + oneOf: + - $ref: SearchParametersInput.yml#/SearchParameters-Input + - type: 'null' diff --git a/specs/agent-studio/common/schemas/SearchParametersOutput.yml b/specs/agent-studio/common/schemas/SearchParametersOutput.yml new file mode 100644 index 00000000000..64b7d61c904 --- /dev/null +++ b/specs/agent-studio/common/schemas/SearchParametersOutput.yml @@ -0,0 +1,290 @@ +SearchParameters-Output: + properties: + queryType: + $ref: QueryTypeNullable.yml#/QueryTypeNullable + similarQuery: + oneOf: + - type: string + - type: 'null' + title: similarquery + queryLanguages: + $ref: QueryLanguagesUnion.yml#/QueryLanguagesUnion + advancedSyntax: + oneOf: + - type: boolean + - type: 'null' + title: advancedsyntax + advancedSyntaxFeatures: + $ref: AdvancedSyntaxFeaturesUnion.yml#/AdvancedSyntaxFeaturesUnion + alternativesAsExact: + $ref: AlternativesAsExactUnion.yml#/AlternativesAsExactUnion + decompoundQuery: + oneOf: + - type: boolean + - type: 'null' + title: decompoundquery + typoTolerance: + $ref: TypoToleranceUnionSearchParametersOutput.yml#/TypoToleranceUnionSearchParametersOutput + allowTyposOnNumericTokens: + oneOf: + - type: boolean + - type: 'null' + title: allowtyposonnumerictokens + minWordSizeFor1Typo: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor1Typo + minWordSizeFor2Typos: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor2Typos + disableTypoToleranceOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disabletypotoleranceonattributes + filters: + oneOf: + - type: string + - type: 'null' + title: filters + facetFilters: + $ref: FacetFiltersOutputNullable.yml#/FacetFiltersOutputNullable + facets: + $ref: FacetsUnion.yml#/FacetsUnion + maxValuesPerFacet: + oneOf: + - type: integer + - type: 'null' + title: maxvaluesperfacet + maxFacetHits: + oneOf: + - type: integer + - type: 'null' + title: maxfacethits + facetingAfterDistinct: + oneOf: + - type: boolean + - type: 'null' + title: facetingafterdistinct + sortFacetValuesBy: + oneOf: + - type: string + - type: 'null' + title: sortfacetvaluesby + numericFilters: + $ref: NumericFiltersUnion.yml#/NumericFiltersUnion + tagFilters: + $ref: TagFiltersUnion.yml#/TagFiltersUnion + sumOrFiltersScores: + oneOf: + - type: boolean + - type: 'null' + title: sumorfiltersscores + aroundLatLng: + oneOf: + - type: string + - type: 'null' + title: aroundlatlng + aroundLatLngViaIp: + oneOf: + - type: boolean + - type: 'null' + title: aroundlatlngviaip + aroundRadius: + $ref: AroundRadiusUnion.yml#/AroundRadiusUnion + aroundPrecision: + $ref: AroundPrecisionUnion.yml#/AroundPrecisionUnion + minimumAroundRadius: + oneOf: + - type: integer + - type: 'null' + title: minimumaroundradius + insideBoundingBox: + $ref: InsideBoundingBoxUnion.yml#/InsideBoundingBoxUnion + insidePolygon: + $ref: InsidePolygonUnion.yml#/InsidePolygonUnion + attributesToRetrieve: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestoretrieve + attributesToSnippet: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestosnippet + snippetEllipsisText: + oneOf: + - type: string + - type: 'null' + title: snippetellipsistext + restrictHighlightAndSnippetArrays: + oneOf: + - type: boolean + - type: 'null' + title: restricthighlightandsnippetarrays + page: + oneOf: + - type: integer + - type: 'null' + title: page + offset: + oneOf: + - type: integer + - type: 'null' + title: offset + hitsPerPage: + oneOf: + - type: integer + - type: 'null' + title: hitsperpage + length: + oneOf: + - type: integer + - type: 'null' + title: length + getRankingInfo: + oneOf: + - type: boolean + - type: 'null' + title: getrankinginfo + relevancyStrictness: + oneOf: + - type: integer + - type: 'null' + title: relevancystrictness + minProximity: + oneOf: + - type: integer + - type: 'null' + title: minproximity + attributeCriteriaComputedByMinProximity: + oneOf: + - type: boolean + - type: 'null' + title: attributecriteriacomputedbyminproximity + distinct: + $ref: DistinctUnion.yml#/DistinctUnion + enableRules: + oneOf: + - type: boolean + - type: 'null' + title: enablerules + enablePersonalization: + oneOf: + - type: boolean + - type: 'null' + title: enablepersonalization + personalizationImpact: + oneOf: + - type: integer + - type: 'null' + title: personalizationimpact + enableAbTest: + oneOf: + - type: boolean + - type: 'null' + title: enableabtest + enableReRanking: + oneOf: + - type: boolean + - type: 'null' + title: enablereranking + reRankingApplyFilter: + $ref: ReRankingApplyFilterUnion.yml#/ReRankingApplyFilterUnion + ruleContexts: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: rulecontexts + removeStopWords: + $ref: RemoveStopWordsUnionSearchParametersOutput.yml#/RemoveStopWordsUnionSearchParametersOutput + ignorePlurals: + $ref: IgnorePluralsUnionSearchParametersOutput.yml#/IgnorePluralsUnionSearchParametersOutput + removeWordsIfNoResults: + $ref: RemoveWordsIfNoResultsNullable.yml#/RemoveWordsIfNoResultsNullable + optionalWords: + $ref: OptionalWordsUnion.yml#/OptionalWordsUnion + optionalFilters: + $ref: OptionalFiltersUnion.yml#/OptionalFiltersUnion + synonyms: + oneOf: + - type: boolean + - type: 'null' + title: synonyms + replaceSynonymsInHighlight: + oneOf: + - type: boolean + - type: 'null' + title: replacesynonymsinhighlight + analytics: + oneOf: + - type: boolean + - type: 'null' + title: analytics + analyticsTags: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: analyticstags + clickAnalytics: + oneOf: + - type: boolean + - type: 'null' + title: clickanalytics + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + restrictSearchableAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: restrictsearchableattributes + disableExactOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disableexactonattributes + exactOnSingleWordQuery: + $ref: ExactOnSingleWordQueryNullable.yml#/ExactOnSingleWordQueryNullable + naturalLanguages: + $ref: NaturalLanguagesUnion.yml#/NaturalLanguagesUnion + percentileComputation: + oneOf: + - type: boolean + - type: 'null' + title: percentilecomputation + explain: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: explain + type: object + title: searchParameters + description: |- + Algolia Search API parameters that can be predefined for the search tool. + Reference: https://www.algolia.com/doc/api-reference/search-api-parameters/ + + The parameters that seemed irrelevant for the search tool have been commented out. + Uses types from algoliasearch.search.models for better type safety. diff --git a/specs/agent-studio/common/schemas/SearchParametersOutputNullable.yml b/specs/agent-studio/common/schemas/SearchParametersOutputNullable.yml new file mode 100644 index 00000000000..7c340b61524 --- /dev/null +++ b/specs/agent-studio/common/schemas/SearchParametersOutputNullable.yml @@ -0,0 +1,4 @@ +SearchParametersOutputNullable: + oneOf: + - $ref: SearchParametersOutput.yml#/SearchParameters-Output + - type: 'null' diff --git a/specs/agent-studio/common/schemas/SearchParametersOverrides.yml b/specs/agent-studio/common/schemas/SearchParametersOverrides.yml new file mode 100644 index 00000000000..bcdef5acaa7 --- /dev/null +++ b/specs/agent-studio/common/schemas/SearchParametersOverrides.yml @@ -0,0 +1,48 @@ +SearchParametersOverrides: + properties: + filters: + oneOf: + - type: string + - type: 'null' + title: filters + attributesToRetrieve: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestoretrieve + restrictSearchableAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: restrictsearchableattributes + distinct: + $ref: DistinctUnion.yml#/DistinctUnion + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + enablePersonalization: + oneOf: + - type: boolean + - type: 'null' + title: enablepersonalization + personalizationImpact: + oneOf: + - type: integer + - type: 'null' + title: personalizationimpact + optionalFilters: + $ref: OptionalFiltersUnion.yml#/OptionalFiltersUnion + additionalProperties: false + type: object + title: searchParametersOverrides + description: |- + Algolia Search API parameters that can be predefined for the search tool. + Reference: https://www.algolia.com/doc/api-reference/search-api-parameters/ + + A subset of SearchParameters of specific params we allow for runtime override. diff --git a/specs/agent-studio/common/schemas/SearchParametersOverridesMapNullable.yml b/specs/agent-studio/common/schemas/SearchParametersOverridesMapNullable.yml new file mode 100644 index 00000000000..1c4f0fd1446 --- /dev/null +++ b/specs/agent-studio/common/schemas/SearchParametersOverridesMapNullable.yml @@ -0,0 +1,6 @@ +SearchParametersOverridesMapNullable: + oneOf: + - additionalProperties: + $ref: SearchParametersOverrides.yml#/SearchParametersOverrides + type: object + - type: 'null' diff --git a/specs/agent-studio/common/schemas/SecretKeyCreate.yml b/specs/agent-studio/common/schemas/SecretKeyCreate.yml new file mode 100644 index 00000000000..b81a78edae4 --- /dev/null +++ b/specs/agent-studio/common/schemas/SecretKeyCreate.yml @@ -0,0 +1,19 @@ +SecretKeyCreate: + properties: + name: + type: string + maxLength: 128 + minLength: 1 + title: name + description: The name of the secret key. + agentIds: + items: + type: string + type: array + title: agentids + description: List of agent IDs this secret key is associated with. + type: object + required: + - name + title: secretKeyCreate + description: Secret key creation payload. diff --git a/specs/agent-studio/common/schemas/SecretKeyPatch.yml b/specs/agent-studio/common/schemas/SecretKeyPatch.yml new file mode 100644 index 00000000000..e250cd808e6 --- /dev/null +++ b/specs/agent-studio/common/schemas/SecretKeyPatch.yml @@ -0,0 +1,21 @@ +SecretKeyPatch: + properties: + name: + oneOf: + - type: string + maxLength: 128 + minLength: 1 + - type: 'null' + title: name + description: The new name of the secret key. + agentIds: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: agentids + description: Updated list of agent IDs this secret key is associated with. + type: object + title: secretKeyPatch + description: Secret key patch payload. diff --git a/specs/agent-studio/common/schemas/SecretKeyResponse.yml b/specs/agent-studio/common/schemas/SecretKeyResponse.yml new file mode 100644 index 00000000000..a723db5e7f6 --- /dev/null +++ b/specs/agent-studio/common/schemas/SecretKeyResponse.yml @@ -0,0 +1,41 @@ +SecretKeyResponse: + properties: + id: + type: string + title: id + name: + type: string + title: name + value: + type: string + title: value + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastUsedAt: + oneOf: + - type: string + - type: 'null' + title: lastusedat + isDefault: + type: boolean + title: isdefault + default: false + agentIds: + items: + type: string + type: array + title: agentids + type: object + required: + - id + - name + - value + - createdAt + - updatedAt + - lastUsedAt + - agentIds + title: secretKeyResponse diff --git a/specs/agent-studio/common/schemas/StartPart.yml b/specs/agent-studio/common/schemas/StartPart.yml new file mode 100644 index 00000000000..4862a99a4b9 --- /dev/null +++ b/specs/agent-studio/common/schemas/StartPart.yml @@ -0,0 +1,11 @@ +StartPart: + properties: + type: + type: string + const: start + title: type + default: start + type: object + title: startPart + required: + - type diff --git a/specs/agent-studio/common/schemas/StartStepPart.yml b/specs/agent-studio/common/schemas/StartStepPart.yml new file mode 100644 index 00000000000..e886f1c4dc2 --- /dev/null +++ b/specs/agent-studio/common/schemas/StartStepPart.yml @@ -0,0 +1,11 @@ +StartStepPart: + properties: + type: + type: string + const: start-step + title: type + default: start-step + type: object + title: startStepPart + required: + - type diff --git a/specs/agent-studio/common/schemas/StepStartPartV4.yml b/specs/agent-studio/common/schemas/StepStartPartV4.yml new file mode 100644 index 00000000000..0e54f608cf1 --- /dev/null +++ b/specs/agent-studio/common/schemas/StepStartPartV4.yml @@ -0,0 +1,9 @@ +StepStartPartV4: + properties: + type: + type: string + const: step-start + title: type + default: step-start + type: object + title: stepStartPartV4 diff --git a/specs/agent-studio/common/schemas/StepStartPartV5.yml b/specs/agent-studio/common/schemas/StepStartPartV5.yml new file mode 100644 index 00000000000..34a97634146 --- /dev/null +++ b/specs/agent-studio/common/schemas/StepStartPartV5.yml @@ -0,0 +1,9 @@ +StepStartPartV5: + properties: + type: + type: string + const: step-start + title: type + default: step-start + type: object + title: stepStartPartV5 diff --git a/specs/agent-studio/common/schemas/StringArrayParam.yml b/specs/agent-studio/common/schemas/StringArrayParam.yml new file mode 100644 index 00000000000..87bd00da522 --- /dev/null +++ b/specs/agent-studio/common/schemas/StringArrayParam.yml @@ -0,0 +1,25 @@ +StringArrayParam: + properties: + exposed: + type: boolean + title: exposed + default: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: default + constraint: + $ref: StringArrayParamConstraintNullable.yml#/StringArrayParamConstraintNullable + merge: + oneOf: + - type: boolean + - type: 'null' + title: merge + type: object + required: + - exposed + title: stringArrayParam + description: A string array search parameter with exposure control, constraints, + and merge behavior. diff --git a/specs/agent-studio/common/schemas/StringArrayParamConstraint.yml b/specs/agent-studio/common/schemas/StringArrayParamConstraint.yml new file mode 100644 index 00000000000..22bef70d420 --- /dev/null +++ b/specs/agent-studio/common/schemas/StringArrayParamConstraint.yml @@ -0,0 +1,12 @@ +StringArrayParamConstraint: + properties: + values: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: values + type: object + title: stringArrayParamConstraint + description: Constraints for a string array parameter. diff --git a/specs/agent-studio/common/schemas/StringArrayParamConstraintNullable.yml b/specs/agent-studio/common/schemas/StringArrayParamConstraintNullable.yml new file mode 100644 index 00000000000..c655f303988 --- /dev/null +++ b/specs/agent-studio/common/schemas/StringArrayParamConstraintNullable.yml @@ -0,0 +1,4 @@ +StringArrayParamConstraintNullable: + oneOf: + - $ref: StringArrayParamConstraint.yml#/StringArrayParamConstraint + - type: 'null' diff --git a/specs/agent-studio/common/schemas/SupportedLanguage.yml b/specs/agent-studio/common/schemas/SupportedLanguage.yml new file mode 100644 index 00000000000..a763378c0c6 --- /dev/null +++ b/specs/agent-studio/common/schemas/SupportedLanguage.yml @@ -0,0 +1,73 @@ +SupportedLanguage: + type: string + enum: + - af + - ar + - az + - bg + - bn + - ca + - cs + - cy + - da + - de + - el + - en + - eo + - es + - et + - eu + - fa + - fi + - fo + - fr + - ga + - gl + - he + - hi + - hu + - hy + - id + - is + - it + - ja + - ka + - kk + - ko + - ku + - ky + - lt + - lv + - mi + - mn + - mr + - ms + - mt + - nb + - nl + - 'no' + - ns + - pl + - ps + - pt + - pt-br + - qu + - ro + - ru + - sk + - sq + - sv + - sw + - ta + - te + - th + - tl + - tn + - tr + - tt + - uk + - ur + - uz + - zh + title: supportedLanguage + description: ISO code for a supported language. diff --git a/specs/agent-studio/common/schemas/TagFiltersUnion.yml b/specs/agent-studio/common/schemas/TagFiltersUnion.yml new file mode 100644 index 00000000000..7348e4fb0ca --- /dev/null +++ b/specs/agent-studio/common/schemas/TagFiltersUnion.yml @@ -0,0 +1,7 @@ +TagFiltersUnion: + oneOf: + - type: string + - type: array + items: + $ref: TagFiltersUnion.yml#/TagFiltersUnion + - type: 'null' diff --git a/specs/agent-studio/common/schemas/TextParam.yml b/specs/agent-studio/common/schemas/TextParam.yml new file mode 100644 index 00000000000..63b3b15f2b9 --- /dev/null +++ b/specs/agent-studio/common/schemas/TextParam.yml @@ -0,0 +1,15 @@ +TextParam: + properties: + exposed: + type: boolean + title: exposed + default: + oneOf: + - type: string + - type: 'null' + title: default + type: object + required: + - exposed + title: textParam + description: A text search parameter with exposure control. diff --git a/specs/agent-studio/common/schemas/TextParamNullable.yml b/specs/agent-studio/common/schemas/TextParamNullable.yml new file mode 100644 index 00000000000..27f934a14bb --- /dev/null +++ b/specs/agent-studio/common/schemas/TextParamNullable.yml @@ -0,0 +1,4 @@ +TextParamNullable: + oneOf: + - $ref: TextParam.yml#/TextParam + - type: 'null' diff --git a/specs/agent-studio/common/schemas/TextPart.yml b/specs/agent-studio/common/schemas/TextPart.yml new file mode 100644 index 00000000000..db3ad3a150e --- /dev/null +++ b/specs/agent-studio/common/schemas/TextPart.yml @@ -0,0 +1,15 @@ +TextPart: + properties: + type: + type: string + const: text + title: type + default: text + text: + type: string + title: text + type: object + required: + - type + - text + title: textPart diff --git a/specs/agent-studio/common/schemas/TextPartV4.yml b/specs/agent-studio/common/schemas/TextPartV4.yml new file mode 100644 index 00000000000..859fe4f6e8f --- /dev/null +++ b/specs/agent-studio/common/schemas/TextPartV4.yml @@ -0,0 +1,14 @@ +TextPartV4: + properties: + type: + type: string + const: text + title: type + default: text + text: + type: string + title: text + type: object + required: + - text + title: textPartV4 diff --git a/specs/agent-studio/common/schemas/TextPartV5.yml b/specs/agent-studio/common/schemas/TextPartV5.yml new file mode 100644 index 00000000000..5476f25f668 --- /dev/null +++ b/specs/agent-studio/common/schemas/TextPartV5.yml @@ -0,0 +1,14 @@ +TextPartV5: + properties: + type: + type: string + const: text + title: type + default: text + text: + type: string + title: text + type: object + required: + - text + title: textPartV5 diff --git a/specs/agent-studio/common/schemas/ToolApprovalRequestPart.yml b/specs/agent-studio/common/schemas/ToolApprovalRequestPart.yml new file mode 100644 index 00000000000..b344310d1ed --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolApprovalRequestPart.yml @@ -0,0 +1,38 @@ +ToolApprovalRequestPart: + properties: + type: + type: string + const: tool-approval-request + title: type + default: tool-approval-request + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + args: + title: args + description: + oneOf: + - type: string + - type: 'null' + title: description + providerOptions: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: provideroptions + argsHash: + oneOf: + - type: string + - type: 'null' + title: argshash + type: object + required: + - type + - toolCallId + - toolName + - args + title: toolApprovalRequestPart diff --git a/specs/agent-studio/common/schemas/ToolCallPart.yml b/specs/agent-studio/common/schemas/ToolCallPart.yml new file mode 100644 index 00000000000..cc54e7e1c47 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolCallPart.yml @@ -0,0 +1,33 @@ +ToolCallPart: + properties: + type: + type: string + const: tool-call + title: type + default: tool-call + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + args: + title: args + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + providerOptions: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: provideroptions + type: object + required: + - type + - toolCallId + - toolName + - args + title: toolCallPart diff --git a/specs/agent-studio/common/schemas/ToolConfig.yml b/specs/agent-studio/common/schemas/ToolConfig.yml new file mode 100644 index 00000000000..9c0139cc249 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolConfig.yml @@ -0,0 +1,4 @@ +ToolConfig: + oneOf: + - $ref: McpToolConfig.yml#/McpToolConfig + - type: boolean diff --git a/specs/agent-studio/common/schemas/ToolConfigInput.yml b/specs/agent-studio/common/schemas/ToolConfigInput.yml new file mode 100644 index 00000000000..2f98d68a951 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolConfigInput.yml @@ -0,0 +1,17 @@ +ToolConfigInput: + oneOf: + - $ref: ClientSideToolConfig.yml#/ClientSideToolConfig + - $ref: AlgoliaSearchToolConfigInput.yml#/AlgoliaSearchToolConfig-Input + - $ref: AlgoliaRecommendToolConfigInput.yml#/AlgoliaRecommendToolConfig-Input + - $ref: AlgoliaDisplayResultsToolConfig.yml#/AlgoliaDisplayResultsToolConfig + - $ref: McpServerToolConfigInput.yml#/McpServerToolConfig-Input + - $ref: UnknownToolConfig.yml#/UnknownToolConfig + discriminator: + propertyName: type + mapping: + algolia_display_results: AlgoliaDisplayResultsToolConfig.yml#/AlgoliaDisplayResultsToolConfig + algolia_recommend: AlgoliaRecommendToolConfigInput.yml#/AlgoliaRecommendToolConfig-Input + algolia_search_index: AlgoliaSearchToolConfigInput.yml#/AlgoliaSearchToolConfig-Input + client_side: ClientSideToolConfig.yml#/ClientSideToolConfig + mcp_tools: McpServerToolConfigInput.yml#/McpServerToolConfig-Input + unknown: UnknownToolConfig.yml#/UnknownToolConfig diff --git a/specs/agent-studio/common/schemas/ToolConfigOutput.yml b/specs/agent-studio/common/schemas/ToolConfigOutput.yml new file mode 100644 index 00000000000..b25ed580186 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolConfigOutput.yml @@ -0,0 +1,17 @@ +ToolConfigOutput: + oneOf: + - $ref: ClientSideToolConfig.yml#/ClientSideToolConfig + - $ref: AlgoliaSearchToolConfigOutput.yml#/AlgoliaSearchToolConfig-Output + - $ref: AlgoliaRecommendToolConfigOutput.yml#/AlgoliaRecommendToolConfig-Output + - $ref: AlgoliaDisplayResultsToolConfig.yml#/AlgoliaDisplayResultsToolConfig + - $ref: McpServerToolConfigOutput.yml#/McpServerToolConfig-Output + - $ref: UnknownToolConfig.yml#/UnknownToolConfig + discriminator: + propertyName: type + mapping: + algolia_display_results: AlgoliaDisplayResultsToolConfig.yml#/AlgoliaDisplayResultsToolConfig + algolia_recommend: AlgoliaRecommendToolConfigOutput.yml#/AlgoliaRecommendToolConfig-Output + algolia_search_index: AlgoliaSearchToolConfigOutput.yml#/AlgoliaSearchToolConfig-Output + client_side: ClientSideToolConfig.yml#/ClientSideToolConfig + mcp_tools: McpServerToolConfigOutput.yml#/McpServerToolConfig-Output + unknown: UnknownToolConfig.yml#/UnknownToolConfig diff --git a/specs/agent-studio/common/schemas/ToolInvocationPartV4.yml b/specs/agent-studio/common/schemas/ToolInvocationPartV4.yml new file mode 100644 index 00000000000..8ed5fab068c --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolInvocationPartV4.yml @@ -0,0 +1,13 @@ +ToolInvocationPartV4: + properties: + type: + type: string + const: tool-invocation + title: type + default: tool-invocation + toolInvocation: + $ref: ToolInvocationV4.yml#/ToolInvocationV4 + type: object + required: + - toolInvocation + title: toolInvocationPartV4 diff --git a/specs/agent-studio/common/schemas/ToolInvocationV4.yml b/specs/agent-studio/common/schemas/ToolInvocationV4.yml new file mode 100644 index 00000000000..d22864e0773 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolInvocationV4.yml @@ -0,0 +1,55 @@ +ToolInvocationV4: + properties: + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + args: + additionalProperties: true + type: object + title: args + result: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: result + step: + oneOf: + - type: integer + - type: 'null' + title: step + state: + oneOf: + - type: string + - type: 'null' + title: state + providerOptions: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: provideroptions + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + description: + oneOf: + - type: string + - type: 'null' + title: description + argsHash: + oneOf: + - type: string + - type: 'null' + title: argshash + type: object + required: + - toolCallId + - toolName + title: toolInvocationV4 + description: Model for tool invocation in a Message. diff --git a/specs/agent-studio/common/schemas/ToolInvocationsUnion.yml b/specs/agent-studio/common/schemas/ToolInvocationsUnion.yml new file mode 100644 index 00000000000..3724f5d1d89 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolInvocationsUnion.yml @@ -0,0 +1,6 @@ +ToolInvocationsUnion: + oneOf: + - items: + $ref: ToolInvocationV4.yml#/ToolInvocationV4 + type: array + - type: 'null' diff --git a/specs/agent-studio/common/schemas/ToolPartV5.yml b/specs/agent-studio/common/schemas/ToolPartV5.yml new file mode 100644 index 00000000000..c80c7a70a0a --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolPartV5.yml @@ -0,0 +1,52 @@ +ToolPartV5: + properties: + type: + type: string + title: type + toolCallId: + type: string + title: toolcallid + state: + $ref: ToolState.yml#/ToolState + input: + additionalProperties: true + type: object + title: input + output: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: output + errorText: + oneOf: + - type: string + - type: 'null' + title: errortext + providerOptions: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: provideroptions + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + description: + oneOf: + - type: string + - type: 'null' + title: description + argsHash: + oneOf: + - type: string + - type: 'null' + title: argshash + type: object + required: + - type + - toolCallId + title: toolPartV5 + description: Model for tool invocation in a Message. diff --git a/specs/agent-studio/common/schemas/ToolResultOutput.yml b/specs/agent-studio/common/schemas/ToolResultOutput.yml new file mode 100644 index 00000000000..6d1b798a7a3 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolResultOutput.yml @@ -0,0 +1,11 @@ +ToolResultOutput: + properties: + type: + $ref: ToolResultOutputType.yml#/ToolResultOutputType + value: + title: value + type: object + required: + - type + - value + title: toolResultOutput diff --git a/specs/agent-studio/common/schemas/ToolResultOutputType.yml b/specs/agent-studio/common/schemas/ToolResultOutputType.yml new file mode 100644 index 00000000000..af431727ded --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolResultOutputType.yml @@ -0,0 +1,10 @@ +ToolResultOutputType: + type: string + enum: + - text + - json + - error-text + - error-json + - content + title: toolResultOutputType + description: The valid 'type' of tool results. diff --git a/specs/agent-studio/common/schemas/ToolResultPart.yml b/specs/agent-studio/common/schemas/ToolResultPart.yml new file mode 100644 index 00000000000..db0a26add24 --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolResultPart.yml @@ -0,0 +1,28 @@ +ToolResultPart: + properties: + type: + type: string + const: tool-result + title: type + default: tool-result + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + output: + $ref: ToolResultOutput.yml#/ToolResultOutput + providerOptions: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: provideroptions + type: object + required: + - type + - toolCallId + - toolName + - output + title: toolResultPart diff --git a/specs/agent-studio/common/schemas/ToolState.yml b/specs/agent-studio/common/schemas/ToolState.yml new file mode 100644 index 00000000000..f54ffe2158d --- /dev/null +++ b/specs/agent-studio/common/schemas/ToolState.yml @@ -0,0 +1,7 @@ +ToolState: + type: string + enum: + - input-streaming + - input-available + - output-available + - output-error diff --git a/specs/agent-studio/common/schemas/TypoToleranceEnum.yml b/specs/agent-studio/common/schemas/TypoToleranceEnum.yml new file mode 100644 index 00000000000..e83bc340511 --- /dev/null +++ b/specs/agent-studio/common/schemas/TypoToleranceEnum.yml @@ -0,0 +1,13 @@ +TypoToleranceEnum: + type: string + enum: + - min + - strict + - 'true' + - 'false' + title: typoToleranceEnum + description: '- `min`. Return matches with the lowest number of typos. For example, + if you have matches without typos, only include those. But if there are no matches + without typos (with 1 typo), include matches with 1 typo (2 typos). - `strict`. + Return matches with the two lowest numbers of typos. With `strict`, the Typo + ranking criterion is applied first in the `ranking` setting.' diff --git a/specs/agent-studio/common/schemas/TypoToleranceUnion.yml b/specs/agent-studio/common/schemas/TypoToleranceUnion.yml new file mode 100644 index 00000000000..9d9ebe1521d --- /dev/null +++ b/specs/agent-studio/common/schemas/TypoToleranceUnion.yml @@ -0,0 +1,5 @@ +TypoToleranceUnion: + oneOf: + - type: boolean + - $ref: TypoToleranceEnum.yml#/TypoToleranceEnum + - type: 'null' diff --git a/specs/agent-studio/common/schemas/TypoToleranceUnionSearchParametersOutput.yml b/specs/agent-studio/common/schemas/TypoToleranceUnionSearchParametersOutput.yml new file mode 100644 index 00000000000..36d283ec07e --- /dev/null +++ b/specs/agent-studio/common/schemas/TypoToleranceUnionSearchParametersOutput.yml @@ -0,0 +1,5 @@ +TypoToleranceUnionSearchParametersOutput: + oneOf: + - type: boolean + - type: string + - type: 'null' diff --git a/specs/agent-studio/common/schemas/UnknownToolConfig.yml b/specs/agent-studio/common/schemas/UnknownToolConfig.yml new file mode 100644 index 00000000000..6f20548ab2c --- /dev/null +++ b/specs/agent-studio/common/schemas/UnknownToolConfig.yml @@ -0,0 +1,20 @@ +UnknownToolConfig: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: unknown + title: type + default: unknown + additionalProperties: true + type: object + required: + - type + - name + title: unknownToolConfig + description: Exists only to ensure that when you change branch from toolX to feat/toolY, + your config stays valid. diff --git a/specs/agent-studio/common/schemas/UserDataResponse.yml b/specs/agent-studio/common/schemas/UserDataResponse.yml new file mode 100644 index 00000000000..a4b112d7a0a --- /dev/null +++ b/specs/agent-studio/common/schemas/UserDataResponse.yml @@ -0,0 +1,17 @@ +UserDataResponse: + properties: + conversations: + items: + $ref: ConversationFullResponse.yml#/ConversationFullResponse + type: array + title: conversations + memories: + items: + $ref: MemoryRecord.yml#/MemoryRecord + type: array + title: memories + type: object + required: + - conversations + - memories + title: userDataResponse diff --git a/specs/agent-studio/common/schemas/UserMessageV4.yml b/specs/agent-studio/common/schemas/UserMessageV4.yml new file mode 100644 index 00000000000..bfff68f2f34 --- /dev/null +++ b/specs/agent-studio/common/schemas/UserMessageV4.yml @@ -0,0 +1,25 @@ +UserMessageV4: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: user + title: role + default: user + content: + type: string + title: content + parts: + items: + $ref: TextPartV4.yml#/TextPartV4 + type: array + title: parts + type: object + required: + - role + - content + title: userMessageV4 diff --git a/specs/agent-studio/common/schemas/UserMessageV5.yml b/specs/agent-studio/common/schemas/UserMessageV5.yml new file mode 100644 index 00000000000..b62ce3612e8 --- /dev/null +++ b/specs/agent-studio/common/schemas/UserMessageV5.yml @@ -0,0 +1,21 @@ +UserMessageV5: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: user + title: role + default: user + parts: + items: + $ref: TextPartV5.yml#/TextPartV5 + type: array + title: parts + type: object + title: userMessageV5 + required: + - role diff --git a/specs/agent-studio/common/schemas/ValidationError.yml b/specs/agent-studio/common/schemas/ValidationError.yml new file mode 100644 index 00000000000..3924ab475e6 --- /dev/null +++ b/specs/agent-studio/common/schemas/ValidationError.yml @@ -0,0 +1,24 @@ +ValidationError: + properties: + loc: + items: + $ref: LocationItemUnion.yml#/LocationItemUnion + type: array + title: location + msg: + type: string + title: message + type: + type: string + title: errorType + input: + title: input + ctx: + type: object + title: context + type: object + required: + - loc + - msg + - type + title: validationError diff --git a/specs/agent-studio/common/schemas/VoteEnum.yml b/specs/agent-studio/common/schemas/VoteEnum.yml new file mode 100644 index 00000000000..f2d65219792 --- /dev/null +++ b/specs/agent-studio/common/schemas/VoteEnum.yml @@ -0,0 +1,8 @@ +VoteEnum: + type: integer + enum: + - 0 + - 1 + x-enum-varnames: + - downvote + - upvote diff --git a/specs/agent-studio/paths/agents/agent.yml b/specs/agent-studio/paths/agents/agent.yml new file mode 100644 index 00000000000..d65cdf72f4a --- /dev/null +++ b/specs/agent-studio/paths/agents/agent.yml @@ -0,0 +1,89 @@ +get: + tags: + - Agents + operationId: getAgent + x-acl: + - settings + summary: Get Agent + description: Retrieve details of the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AgentWithVersionResponse.yml#/AgentWithVersionResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +patch: + tags: + - Agents + operationId: updateAgent + x-acl: + - editSettings + summary: Update Agent + description: Update the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/AgentConfigUpdate.yml#/AgentConfigUpdate + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AgentWithVersionResponse.yml#/AgentWithVersionResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - Agents + operationId: deleteAgent + x-acl: + - editSettings + summary: Delete Agent + description: Delete the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/agentAllowedDomains.yml b/specs/agent-studio/paths/agents/agentAllowedDomains.yml new file mode 100644 index 00000000000..1c3f38af01d --- /dev/null +++ b/specs/agent-studio/paths/agents/agentAllowedDomains.yml @@ -0,0 +1,65 @@ +get: + tags: + - Allowed Domains + operationId: listAgentAllowedDomains + x-acl: + - settings + summary: List Allowed Domains + description: List all allowed domain patterns for this agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AllowedDomainListResponse.yml#/AllowedDomainListResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +post: + tags: + - Allowed Domains + operationId: createAgentAllowedDomain + x-acl: + - editSettings + summary: Create Allowed Domain + description: Add a single allowed domain pattern (e.g. https://app.example.com or + *.example.com). + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/AllowedDomainCreate.yml#/AllowedDomainCreate + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AllowedDomainResponse.yml#/AllowedDomainResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/agentCache.yml b/specs/agent-studio/paths/agents/agentCache.yml new file mode 100644 index 00000000000..71c73b044c3 --- /dev/null +++ b/specs/agent-studio/paths/agents/agentCache.yml @@ -0,0 +1,36 @@ +delete: + tags: + - Agents + operationId: invalidateAgentCache + x-acl: + - editSettings + summary: Invalidate Agent Cache + description: Invalidate cached completions for this agent. Filter with `before` + (exclusive). + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + - name: before + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: Delete entries strictly before this date (exclusive, YYYY-MM-DD). + title: before + description: Delete entries strictly before this date (exclusive, YYYY-MM-DD). + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/agentCompletions.yml b/specs/agent-studio/paths/agents/agentCompletions.yml new file mode 100644 index 00000000000..3b0d417ab26 --- /dev/null +++ b/specs/agent-studio/paths/agents/agentCompletions.yml @@ -0,0 +1,101 @@ +post: + tags: + - Completions + - Completions + operationId: createAgentCompletion + x-acl: + - search + x-streaming: true + summary: Create Completion + description: |- + Create a completion for the specified agent. + + This endpoint handles two types of requests: + 1. Normal completion request: User message -> Agent response + 2. Tool approval response: User approval -> Execute tool -> Agent response + + Tool Approval Flow (for MCP tools with requiresApproval: true): + - Request 1: User sends message -> Agent requests tool call -> Return approval request + - Request 2: User approves -> Execute tool -> Agent continues with result. + parameters: + - name: agentId + in: path + required: true + schema: + $ref: ../../common/schemas/AgentIdUnion.yml#/AgentIdUnion + description: The agentId. + - name: compatibilityMode + in: query + required: true + schema: + $ref: ../../common/schemas/CompatibilityMode.yml#/CompatibilityMode + description: Compatibility mode for the completion API. + - name: stream + in: query + required: false + schema: + type: boolean + description: Whether to stream the response or not. + default: true + title: stream + description: Whether to stream the response or not. + - name: cache + in: query + required: false + schema: + type: boolean + description: Use cached responses if available. + default: true + title: cache + description: Use cached responses if available. + - name: memory + in: query + required: false + schema: + oneOf: + - const: false + type: boolean + - type: 'null' + description: Set to false to disable memory (enabled by default). + title: memory + description: Set to false to disable memory (enabled by default). + - name: analytics + in: query + required: false + schema: + type: boolean + description: 'Set to false to skip Agent Studio analytics for this completion + (default: true).' + default: true + title: analytics + description: 'Set to false to skip Agent Studio analytics for this completion + (default: true).' + - name: X-Algolia-Secure-User-Token + in: header + required: false + schema: + oneOf: + - type: string + - type: 'null' + title: x-Algolia-Secure-User-Token + description: The X-Algolia-Secure-User-Token. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/AgentCompletionRequest.yml#/AgentCompletionRequest + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + type: object + additionalProperties: true + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/agentPublish.yml b/specs/agent-studio/paths/agents/agentPublish.yml new file mode 100644 index 00000000000..8c34a0eeeda --- /dev/null +++ b/specs/agent-studio/paths/agents/agentPublish.yml @@ -0,0 +1,29 @@ +post: + tags: + - Agents + operationId: publishAgent + x-acl: + - editSettings + summary: Publish Agent + description: Publish the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AgentWithVersionResponse.yml#/AgentWithVersionResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/agentUnpublish.yml b/specs/agent-studio/paths/agents/agentUnpublish.yml new file mode 100644 index 00000000000..ff3e2d736df --- /dev/null +++ b/specs/agent-studio/paths/agents/agentUnpublish.yml @@ -0,0 +1,29 @@ +post: + tags: + - Agents + operationId: unpublishAgent + x-acl: + - editSettings + summary: Unpublish Agent + description: Unpublish the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AgentWithVersionResponse.yml#/AgentWithVersionResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/agents.yml b/specs/agent-studio/paths/agents/agents.yml new file mode 100644 index 00000000000..9fa9597d1ba --- /dev/null +++ b/specs/agent-studio/paths/agents/agents.yml @@ -0,0 +1,79 @@ +get: + tags: + - Agents + operationId: listAgents + x-acl: + - settings + summary: List Agents + description: List all agents with pagination and filtering. + parameters: + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + minimum: 1 + description: Items per page. + default: 10 + title: limit + description: Items per page. + - name: providerId + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: Filter by provider id. + title: providerid + description: Filter by provider id. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/PaginatedAgentsResponse.yml#/PaginatedAgentsResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +post: + tags: + - Agents + operationId: createAgent + x-acl: + - editSettings + summary: Create Agent + description: Create a new agent. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/AgentConfigCreate.yml#/AgentConfigCreate + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AgentWithVersionResponse.yml#/AgentWithVersionResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/allowedDomain.yml b/specs/agent-studio/paths/agents/allowedDomain.yml new file mode 100644 index 00000000000..8a3559cf615 --- /dev/null +++ b/specs/agent-studio/paths/agents/allowedDomain.yml @@ -0,0 +1,68 @@ +get: + tags: + - Allowed Domains + operationId: getAllowedDomain + x-acl: + - settings + summary: Get Allowed Domain + description: Get a single allowed domain by id. + parameters: + - name: domainId + in: path + required: true + schema: + type: string + title: domainId + description: The domainId. + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AllowedDomainResponse.yml#/AllowedDomainResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - Allowed Domains + operationId: deleteAllowedDomain + x-acl: + - editSettings + summary: Delete Allowed Domain + description: Remove an allowed domain by id. + parameters: + - name: domainId + in: path + required: true + schema: + type: string + title: domainId + description: The domainId. + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/agents/allowedDomainsBulk.yml b/specs/agent-studio/paths/agents/allowedDomainsBulk.yml new file mode 100644 index 00000000000..fa3f27eb525 --- /dev/null +++ b/specs/agent-studio/paths/agents/allowedDomainsBulk.yml @@ -0,0 +1,66 @@ +post: + tags: + - Allowed Domains + operationId: bulkCreateAllowedDomains + x-acl: + - editSettings + summary: Bulk Insert Allowed Domains + description: Add multiple allowed domain patterns. Duplicates are skipped. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/AllowedDomainBulkInsert.yml#/AllowedDomainBulkInsert + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/AllowedDomainListResponse.yml#/AllowedDomainListResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - Allowed Domains + operationId: bulkDeleteAllowedDomains + x-acl: + - editSettings + summary: Bulk Delete Allowed Domains + description: Delete allowed domains by id list. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/AllowedDomainBulkDelete.yml#/AllowedDomainBulkDelete + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/configuration/configuration.yml b/specs/agent-studio/paths/configuration/configuration.yml new file mode 100644 index 00000000000..d46792bd260 --- /dev/null +++ b/specs/agent-studio/paths/configuration/configuration.yml @@ -0,0 +1,48 @@ +get: + tags: + - Configurations + operationId: getConfiguration + x-acl: + - logs + summary: Get Configuration + description: Get Configuration + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/ApplicationConfigResponse.yml#/ApplicationConfigResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +patch: + tags: + - Configurations + operationId: updateConfiguration + x-acl: + - logs + summary: Patch Configuration + description: Patch Configuration + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/ApplicationConfigPatch.yml#/ApplicationConfigPatch + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/ApplicationConfigResponse.yml#/ApplicationConfigResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/conversations/conversation.yml b/specs/agent-studio/paths/conversations/conversation.yml new file mode 100644 index 00000000000..71a52571c24 --- /dev/null +++ b/specs/agent-studio/paths/conversations/conversation.yml @@ -0,0 +1,85 @@ +get: + tags: + - Conversations + operationId: getConversation + x-acl: + - logs + summary: Get Conversation + description: Retrieves the conversation and its messages for the given ID. + parameters: + - name: conversationId + in: path + required: true + schema: + type: string + title: conversationId + description: The conversationId. + - name: agentId + in: path + required: true + schema: + $ref: ../../common/schemas/AgentIdUnion.yml#/AgentIdUnion + description: The agentId. + - name: includeFeedback + in: query + required: false + schema: + type: boolean + description: Include feedback for the conversation. + default: false + title: includefeedback + description: Include feedback for the conversation. + - name: X-Algolia-Secure-User-Token + in: header + required: false + schema: + oneOf: + - type: string + - type: 'null' + title: x-Algolia-Secure-User-Token + description: The X-Algolia-Secure-User-Token. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/ConversationFullResponse.yml#/ConversationFullResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - Conversations + operationId: deleteConversation + x-acl: + - logs + summary: Delete Conversation + description: Deletes the conversation with the given ID. + parameters: + - name: conversationId + in: path + required: true + schema: + type: string + title: conversationId + description: The conversationId. + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/conversations/conversations.yml b/specs/agent-studio/paths/conversations/conversations.yml new file mode 100644 index 00000000000..1f1ff7d67b7 --- /dev/null +++ b/specs/agent-studio/paths/conversations/conversations.yml @@ -0,0 +1,146 @@ +get: + tags: + - Conversations + operationId: listAgentConversations + x-acl: + - logs + summary: List Conversations + description: Retrieves the conversations for the given agent ID. + parameters: + - name: agentId + in: path + required: true + schema: + $ref: ../../common/schemas/AgentIdUnion.yml#/AgentIdUnion + description: The agentId. + - name: startDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + title: startdate + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + - name: endDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + title: enddate + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + - name: includeFeedback + in: query + required: false + schema: + oneOf: + - type: boolean + - type: 'null' + description: Include feedback per conversation. + default: false + title: includefeedback + description: Include feedback per conversation. + - name: feedbackVote + in: query + required: false + schema: + oneOf: + - type: integer + maximum: 1 + minimum: 0 + - type: 'null' + description: Filter by feedback value (requires includeFeedback=true). + title: feedbackvote + description: Filter by feedback value (requires includeFeedback=true). + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + maximum: 100 + minimum: 1 + description: Items per page. + default: 20 + title: limit + description: Items per page. + - name: X-Algolia-Secure-User-Token + in: header + required: false + schema: + oneOf: + - type: string + - type: 'null' + title: x-Algolia-Secure-User-Token + description: The X-Algolia-Secure-User-Token. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/PaginatedConversationsResponse.yml#/PaginatedConversationsResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - Conversations + operationId: deleteAgentConversations + x-acl: + - logs + summary: Delete Conversations + description: Deletes the conversations matching the given filers. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + - name: startDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + title: startdate + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + - name: endDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + title: enddate + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/conversations/conversationsExport.yml b/specs/agent-studio/paths/conversations/conversationsExport.yml new file mode 100644 index 00000000000..40e574d26ab --- /dev/null +++ b/specs/agent-studio/paths/conversations/conversationsExport.yml @@ -0,0 +1,51 @@ +get: + tags: + - Conversations + operationId: exportConversations + x-acl: + - logs + summary: Export Conversations + description: Exports all conversations based on the passed filters. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + - name: startDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + title: startdate + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + - name: endDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + title: enddate + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + type: array + items: + $ref: ../../common/schemas/ConversationFullResponse.yml#/ConversationFullResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/feedback/feedback.yml b/specs/agent-studio/paths/feedback/feedback.yml new file mode 100644 index 00000000000..cad059f337c --- /dev/null +++ b/specs/agent-studio/paths/feedback/feedback.yml @@ -0,0 +1,27 @@ +post: + tags: + - Feedback + operationId: createFeedback + x-acl: + - search + summary: Create Feedback + description: Create new feedback entry. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/FeedbackCreationRequest.yml#/FeedbackCreationRequest + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/FeedbackResponse.yml#/FeedbackResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/providers/models.yml b/specs/agent-studio/paths/providers/models.yml new file mode 100644 index 00000000000..51cc281b8e7 --- /dev/null +++ b/specs/agent-studio/paths/providers/models.yml @@ -0,0 +1,26 @@ +get: + tags: + - Providers + operationId: listModels + x-acl: + - settings + summary: Get Provider Models + description: Get Provider Models + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + type: object + additionalProperties: + type: array + items: + type: string + title: responseGetProviderModels1ProvidersModelsGet + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/providers/provider.yml b/specs/agent-studio/paths/providers/provider.yml new file mode 100644 index 00000000000..cf1eddcf9cd --- /dev/null +++ b/specs/agent-studio/paths/providers/provider.yml @@ -0,0 +1,89 @@ +get: + tags: + - Providers + operationId: getProvider + x-acl: + - settings + summary: Get Provider + description: Get Provider + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/ProviderAuthenticationResponse.yml#/ProviderAuthenticationResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +patch: + tags: + - Providers + operationId: updateProvider + x-acl: + - editSettings + summary: Update Provider + description: Update Provider + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/ProviderAuthenticationPatch.yml#/ProviderAuthenticationPatch + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/ProviderAuthenticationResponse.yml#/ProviderAuthenticationResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - Providers + operationId: deleteProvider + x-acl: + - editSettings + summary: Delete Provider + description: Delete Provider + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/providers/providerModels.yml b/specs/agent-studio/paths/providers/providerModels.yml new file mode 100644 index 00000000000..eb72bc08e41 --- /dev/null +++ b/specs/agent-studio/paths/providers/providerModels.yml @@ -0,0 +1,32 @@ +get: + tags: + - Providers + operationId: listProviderModels + x-acl: + - settings + summary: Get Provider Models By Id + description: Get available models for a specific provider. + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + type: array + items: + type: string + title: responseGetProviderModelsById1ProvidersProviderIdModelsGet + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/providers/providers.yml b/specs/agent-studio/paths/providers/providers.yml new file mode 100644 index 00000000000..1aeb87d5855 --- /dev/null +++ b/specs/agent-studio/paths/providers/providers.yml @@ -0,0 +1,69 @@ +get: + tags: + - Providers + operationId: listProviders + x-acl: + - settings + summary: List Providers + description: List Providers + parameters: + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + minimum: 1 + description: Items per page. + default: 10 + title: limit + description: Items per page. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/PaginatedProviderAuthenticationsResponse.yml#/PaginatedProviderAuthenticationsResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +post: + tags: + - Providers + operationId: createProvider + x-acl: + - editSettings + summary: Create Provider + description: Create Provider + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/ProviderAuthenticationCreate.yml#/ProviderAuthenticationCreate + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/ProviderAuthenticationResponse.yml#/ProviderAuthenticationResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/secret-keys/secretKey.yml b/specs/agent-studio/paths/secret-keys/secretKey.yml new file mode 100644 index 00000000000..d6719694372 --- /dev/null +++ b/specs/agent-studio/paths/secret-keys/secretKey.yml @@ -0,0 +1,89 @@ +get: + tags: + - Secret Keys + operationId: getSecretKey + x-acl: + - settings + summary: Get Secret Key + description: Get Secret Key + parameters: + - name: secretKeyId + in: path + required: true + schema: + type: string + title: secretKeyId + description: The secretKeyId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/SecretKeyResponse.yml#/SecretKeyResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +patch: + tags: + - Secret Keys + operationId: updateSecretKey + x-acl: + - admin + summary: Patch Secret Key + description: Patch Secret Key + parameters: + - name: secretKeyId + in: path + required: true + schema: + type: string + title: secretKeyId + description: The secretKeyId. + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/SecretKeyPatch.yml#/SecretKeyPatch + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/SecretKeyResponse.yml#/SecretKeyResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - Secret Keys + operationId: deleteSecretKey + x-acl: + - admin + summary: Delete Secret Key + description: Delete Secret Key + parameters: + - name: secretKeyId + in: path + required: true + schema: + type: string + title: secretKeyId + description: The secretKeyId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/secret-keys/secretKeys.yml b/specs/agent-studio/paths/secret-keys/secretKeys.yml new file mode 100644 index 00000000000..97d505f9d8f --- /dev/null +++ b/specs/agent-studio/paths/secret-keys/secretKeys.yml @@ -0,0 +1,69 @@ +get: + tags: + - Secret Keys + operationId: listSecretKeys + x-acl: + - settings + summary: List Secret Keys + description: List Secret Keys + parameters: + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + minimum: 1 + description: Items per page. + default: 10 + title: limit + description: Items per page. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/PaginatedSecretKeysResponse.yml#/PaginatedSecretKeysResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +post: + tags: + - Secret Keys + operationId: createSecretKey + x-acl: + - admin + summary: Create Secret Key + description: Create Secret Key + requestBody: + required: true + content: + application/json: + schema: + $ref: ../../common/schemas/SecretKeyCreate.yml#/SecretKeyCreate + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/SecretKeyResponse.yml#/SecretKeyResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/paths/user-data/userData.yml b/specs/agent-studio/paths/user-data/userData.yml new file mode 100644 index 00000000000..fbc99b963ca --- /dev/null +++ b/specs/agent-studio/paths/user-data/userData.yml @@ -0,0 +1,56 @@ +get: + tags: + - User Data + operationId: getUserData + x-acl: + - logs + summary: Get Data By User Token + description: Retrieves all memories, conversations and their messages for the given + user token. + parameters: + - name: userToken + in: path + required: true + schema: + type: string + title: userToken + description: The userToken. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: ../../common/schemas/UserDataResponse.yml#/UserDataResponse + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError +delete: + tags: + - User Data + operationId: deleteUserData + x-acl: + - logs + summary: Delete Data By User Token + description: Permanently deletes all messages for the given user token. Does not + delete conversations. + parameters: + - name: userToken + in: path + required: true + schema: + type: string + title: userToken + description: The userToken. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: ../../common/schemas/HTTPValidationError.yml#/HTTPValidationError diff --git a/specs/agent-studio/spec.yml b/specs/agent-studio/spec.yml new file mode 100644 index 00000000000..bc6a0e98c99 --- /dev/null +++ b/specs/agent-studio/spec.yml @@ -0,0 +1,101 @@ +openapi: 3.0.2 +info: + title: Agent Studio API + description: |- + Algolia's GenAI Toolkit API, building blocks for your dynamic LLM-enabled experiences. + + Build generative AI experiences with your Algolia data. Combine large language models with Algolia’s Search API using Retrieval Augmented Generation (RAG) techniques. + version: 0.1.0 +components: + securitySchemes: + appId: + $ref: ../common/securitySchemes.yml#/appId + apiKey: + $ref: ../common/securitySchemes.yml#/apiKey +servers: +- url: https://{APPLICATION_ID}.algolia.net/agent-studio + description: Agent Studio API. + variables: + APPLICATION_ID: + default: EXAMPLE + description: Your Algolia application ID. +security: +- appId: [] + apiKey: [] +tags: +- name: Agents + description: Manage your agents. +- name: Allowed Domains + description: Restrict where an agent's API key may be used. +- name: Completions + description: Generate completions. +- name: Configurations + description: Manage Agent Studio related application configurations. +- name: Conversations + description: View, export, and delete conversation history. +- name: Feedback + description: Capture user feedback on agent responses. +- name: Internal + description: Endpoints that are needed for internal or integration logic. +- name: Providers + description: Manage your LLM providers. +- name: Secret Keys + description: Manage secret keys used to authenticate requests. +- name: User Data + description: Manage end-user data associated with conversations. +x-timeouts: + browser: + connect: 25000 + read: 25000 + write: 25000 + server: + connect: 25000 + read: 25000 + write: 25000 +paths: + /1/agents: + $ref: paths/agents/agents.yml + /1/agents/{agentId}: + $ref: paths/agents/agent.yml + /1/agents/{agentId}/allowed-domains: + $ref: paths/agents/agentAllowedDomains.yml + /1/agents/{agentId}/allowed-domains/bulk: + $ref: paths/agents/allowedDomainsBulk.yml + /1/agents/{agentId}/allowed-domains/{domainId}: + $ref: paths/agents/allowedDomain.yml + /1/agents/{agentId}/cache: + $ref: paths/agents/agentCache.yml + /1/agents/{agentId}/completions: + $ref: paths/agents/agentCompletions.yml + /1/agents/{agentId}/conversations: + $ref: paths/conversations/conversations.yml + /1/agents/{agentId}/conversations/export: + $ref: paths/conversations/conversationsExport.yml + /1/agents/{agentId}/conversations/{conversationId}: + $ref: paths/conversations/conversation.yml + /1/agents/{agentId}/publish: + $ref: paths/agents/agentPublish.yml + /1/agents/{agentId}/unpublish: + $ref: paths/agents/agentUnpublish.yml + /1/configuration: + $ref: paths/configuration/configuration.yml + /1/feedback: + $ref: paths/feedback/feedback.yml + /1/providers: + $ref: paths/providers/providers.yml + /1/providers/models: + $ref: paths/providers/models.yml + /1/providers/{providerId}: + $ref: paths/providers/provider.yml + /1/providers/{providerId}/models: + $ref: paths/providers/providerModels.yml + /1/secret-keys: + $ref: paths/secret-keys/secretKeys.yml + /1/secret-keys/{secretKeyId}: + $ref: paths/secret-keys/secretKey.yml + /1/user-data/{userToken}: + $ref: paths/user-data/userData.yml + /{path}: + $ref: ../common/paths/customRequest.yml + /setClientApiKey: + $ref: ../common/helpers/setClientApiKey.yml#/method diff --git a/specs/bundled/agent-studio.yml b/specs/bundled/agent-studio.yml new file mode 100644 index 00000000000..1c6693abe4a --- /dev/null +++ b/specs/bundled/agent-studio.yml @@ -0,0 +1,5130 @@ +openapi: 3.0.2 +info: + title: Agent Studio API + description: >- + Algolia's GenAI Toolkit API, building blocks for your dynamic LLM-enabled + experiences. + + + Build generative AI experiences with your Algolia data. Combine large + language models with Algolia’s Search API using Retrieval Augmented + Generation (RAG) techniques. + version: 0.1.0 +servers: + - url: https://{APPLICATION_ID}.algolia.net/agent-studio + description: Agent Studio API. + variables: + APPLICATION_ID: + default: EXAMPLE + description: Your Algolia application ID. +security: + - appId: [] + apiKey: [] +tags: + - name: Agents + description: Manage your agents. + - name: Completions + description: Generate completions. + - name: Configurations + description: Manage Agent Studio related application configurations. + - name: Internal + description: Endpoints that are needed for internal or integration logic. + - name: Providers + description: Manage your LLM providers. + - name: Secret Keys + description: Manage secret keys used to authenticate requests. +paths: + /1/agents: + get: + tags: + - agent-studio + operationId: listAgents + x-acl: + - settings + summary: List Agents + description: List all agents with pagination and filtering. + parameters: + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + minimum: 1 + description: Items per page. + default: 10 + title: limit + description: Items per page. + - name: providerId + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: Filter by provider id. + title: providerid + description: Filter by provider id. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedAgentsResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + post: + tags: + - agent-studio + operationId: createAgent + x-acl: + - editSettings + summary: Create Agent + description: Create a new agent. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AgentConfigCreate' + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentWithVersionResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}: + get: + tags: + - agent-studio + operationId: getAgent + x-acl: + - settings + summary: Get Agent + description: Retrieve details of the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentWithVersionResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - agent-studio + operationId: updateAgent + x-acl: + - editSettings + summary: Update Agent + description: Update the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AgentConfigUpdate' + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentWithVersionResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteAgent + x-acl: + - editSettings + summary: Delete Agent + description: Delete the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/allowed-domains: + get: + tags: + - agent-studio + operationId: listAgentAllowed-Domains + x-acl: + - settings + summary: List Allowed Domains + description: List all allowed domain patterns for this agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AllowedDomainListResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + post: + tags: + - agent-studio + operationId: createAgentAllowed-Domain + x-acl: + - editSettings + summary: Create Allowed Domain + description: >- + Add a single allowed domain pattern (e.g. https://app.example.com or + *.example.com). + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AllowedDomainCreate' + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AllowedDomainResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/allowed-domains/bulk: + post: + tags: + - agent-studio + operationId: createBulk + x-acl: + - editSettings + summary: Bulk Insert Allowed Domains + description: Add multiple allowed domain patterns. Duplicates are skipped. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AllowedDomainBulkInsert' + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AllowedDomainListResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteBulk + x-acl: + - editSettings + summary: Bulk Delete Allowed Domains + description: Delete allowed domains by id list. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AllowedDomainBulkDelete' + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/allowed-domains/{domainId}: + get: + tags: + - agent-studio + operationId: getAllowedDomain + x-acl: + - settings + summary: Get Allowed Domain + description: Get a single allowed domain by id. + parameters: + - name: domainId + in: path + required: true + schema: + type: string + title: domainId + description: The domainId. + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AllowedDomainResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteAllowedDomain + x-acl: + - editSettings + summary: Delete Allowed Domain + description: Remove an allowed domain by id. + parameters: + - name: domainId + in: path + required: true + schema: + type: string + title: domainId + description: The domainId. + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/cache: + delete: + tags: + - agent-studio + operationId: invalidateAgentCache + x-acl: + - editSettings + summary: Invalidate Agent Cache + description: >- + Invalidate cached completions for this agent. Filter with `before` + (exclusive). + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + - name: before + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: Delete entries strictly before this date (exclusive, YYYY-MM-DD). + title: before + description: Delete entries strictly before this date (exclusive, YYYY-MM-DD). + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/completions: + post: + tags: + - agent-studio + operationId: createAgentCompletion + x-acl: + - search + summary: Create Completion + description: >- + Create a completion for the specified agent. + + + This endpoint handles two types of requests: + + 1. Normal completion request: User message -> Agent response + + 2. Tool approval response: User approval -> Execute tool -> Agent + response + + + Tool Approval Flow (for MCP tools with requiresApproval: true): + + - Request 1: User sends message -> Agent requests tool call -> Return + approval request + + - Request 2: User approves -> Execute tool -> Agent continues with + result. + parameters: + - name: agentId + in: path + required: true + schema: + $ref: '#/components/schemas/AgentIdUnion' + description: The agentId. + - name: compatibilityMode + in: query + required: true + schema: + $ref: '#/components/schemas/CompatibilityMode' + description: Compatibility mode for the completion API. + - name: stream + in: query + required: false + schema: + type: boolean + description: Whether to stream the response or not. + default: true + title: stream + description: Whether to stream the response or not. + - name: cache + in: query + required: false + schema: + type: boolean + description: Use cached responses if available. + default: true + title: cache + description: Use cached responses if available. + - name: memory + in: query + required: false + schema: + oneOf: + - const: false + type: boolean + - type: 'null' + description: Set to false to disable memory (enabled by default). + title: memory + description: Set to false to disable memory (enabled by default). + - name: analytics + in: query + required: false + schema: + type: boolean + description: >- + Set to false to skip Agent Studio analytics for this completion + (default: true). + default: true + title: analytics + description: >- + Set to false to skip Agent Studio analytics for this completion + (default: true). + - name: X-Algolia-Secure-User-Token + in: header + required: false + schema: + oneOf: + - type: string + - type: 'null' + title: x-Algolia-Secure-User-Token + description: The X-Algolia-Secure-User-Token. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AgentCompletionRequest' + responses: + '200': + description: Successful Response. + content: + application/json: + schema: {} + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/conversations: + get: + tags: + - agent-studio + operationId: listAgentConversations + x-acl: + - logs + summary: List Conversations + description: Retrieves the conversations for the given agent ID. + parameters: + - name: agentId + in: path + required: true + schema: + $ref: '#/components/schemas/AgentIdUnion' + description: The agentId. + - name: startDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + title: startdate + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + - name: endDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: >- + Filter conversations created before this date (format: + YYYY-MM-DD). + title: enddate + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + - name: includeFeedback + in: query + required: false + schema: + oneOf: + - type: boolean + - type: 'null' + description: Include feedback per conversation. + default: false + title: includefeedback + description: Include feedback per conversation. + - name: feedbackVote + in: query + required: false + schema: + oneOf: + - type: integer + maximum: 1 + minimum: 0 + - type: 'null' + description: Filter by feedback value (requires includeFeedback=true). + title: feedbackvote + description: Filter by feedback value (requires includeFeedback=true). + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + maximum: 100 + minimum: 1 + description: Items per page. + default: 20 + title: limit + description: Items per page. + - name: X-Algolia-Secure-User-Token + in: header + required: false + schema: + oneOf: + - type: string + - type: 'null' + title: x-Algolia-Secure-User-Token + description: The X-Algolia-Secure-User-Token. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedConversationsResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteAgentConversations + x-acl: + - logs + summary: Delete Conversations + description: Deletes the conversations matching the given filers. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + - name: startDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + title: startdate + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + - name: endDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: >- + Filter conversations created before this date (format: + YYYY-MM-DD). + title: enddate + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/conversations/export: + get: + tags: + - agent-studio + operationId: exportConversations + x-acl: + - logs + summary: Export Conversations + description: Exports all conversations based on the passed filters. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + - name: startDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + title: startdate + description: 'Filter conversations created after this date (format: YYYY-MM-DD).' + - name: endDate + in: query + required: false + schema: + oneOf: + - type: string + - type: 'null' + description: >- + Filter conversations created before this date (format: + YYYY-MM-DD). + title: enddate + description: 'Filter conversations created before this date (format: YYYY-MM-DD).' + responses: + '200': + description: Successful Response. + content: + application/json: + schema: {} + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/conversations/{conversationId}: + get: + tags: + - agent-studio + operationId: getConversation + x-acl: + - logs + summary: Get Conversation + description: Retrieves the conversation and its messages for the given ID. + parameters: + - name: conversationId + in: path + required: true + schema: + type: string + title: conversationId + description: The conversationId. + - name: agentId + in: path + required: true + schema: + $ref: '#/components/schemas/AgentIdUnion' + description: The agentId. + - name: includeFeedback + in: query + required: false + schema: + type: boolean + description: Include feedback for the conversation. + default: false + title: includefeedback + description: Include feedback for the conversation. + - name: X-Algolia-Secure-User-Token + in: header + required: false + schema: + oneOf: + - type: string + - type: 'null' + title: x-Algolia-Secure-User-Token + description: The X-Algolia-Secure-User-Token. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/ConversationFullResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteConversation + x-acl: + - logs + summary: Delete Conversation + description: Deletes the conversation with the given ID. + parameters: + - name: conversationId + in: path + required: true + schema: + type: string + title: conversationId + description: The conversationId. + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/publish: + post: + tags: + - agent-studio + operationId: publishAgent + x-acl: + - editSettings + summary: Publish Agent + description: Publish the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentWithVersionResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/agents/{agentId}/unpublish: + post: + tags: + - agent-studio + operationId: unpublishAgent + x-acl: + - editSettings + summary: Unpublish Agent + description: Unpublish the specified agent. + parameters: + - name: agentId + in: path + required: true + schema: + type: string + title: agentId + description: The agentId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/AgentWithVersionResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/configuration: + get: + tags: + - agent-studio + operationId: getApplicationConfiguration + x-acl: + - logs + summary: Get Configuration + description: Get Configuration. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationConfigResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - agent-studio + operationId: updateApplicationConfiguration + x-acl: + - logs + summary: Patch Configuration + description: Patch Configuration. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationConfigPatch' + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/ApplicationConfigResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/feedback: + post: + tags: + - agent-studio + operationId: createFeedback + x-acl: + - search + summary: Create Feedback + description: Create new feedback entry. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/FeedbackCreationRequest' + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/FeedbackResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/providers: + get: + tags: + - agent-studio + operationId: listProviders + x-acl: + - settings + summary: List Providers + description: List Providers. + parameters: + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + minimum: 1 + description: Items per page. + default: 10 + title: limit + description: Items per page. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedProviderAuthenticationsResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + post: + tags: + - agent-studio + operationId: createProvider + x-acl: + - editSettings + summary: Create Provider + description: Create Provider. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProviderAuthenticationCreate' + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/ProviderAuthenticationResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/providers/models: + get: + tags: + - agent-studio + operationId: listModels + x-acl: + - settings + summary: Get Provider Models + description: Get Provider Models. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + type: object + additionalProperties: + type: array + items: + type: string + title: responseGetProviderModels1ProvidersModelsGet + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/providers/{providerId}: + get: + tags: + - agent-studio + operationId: getProvider + x-acl: + - settings + summary: Get Provider + description: Get Provider. + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/ProviderAuthenticationResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - agent-studio + operationId: updateProvider + x-acl: + - editSettings + summary: Update Provider + description: Update Provider. + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProviderAuthenticationPatch' + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/ProviderAuthenticationResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteProvider + x-acl: + - editSettings + summary: Delete Provider + description: Delete Provider. + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/providers/{providerId}/models: + get: + tags: + - agent-studio + operationId: listProviderModels + x-acl: + - settings + summary: Get Provider Models By Id + description: Get available models for a specific provider. + parameters: + - name: providerId + in: path + required: true + schema: + type: string + title: providerId + description: The providerId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + type: array + items: + type: string + title: responseGetProviderModelsById1ProvidersProviderIdModelsGet + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/secret-keys: + get: + tags: + - agent-studio + operationId: listSecretKeys + x-acl: + - settings + summary: List Secret Keys + description: List Secret Keys. + parameters: + - name: page + in: query + required: false + schema: + type: integer + minimum: 1 + description: Page number. + default: 1 + title: page + description: Page number. + - name: limit + in: query + required: false + schema: + type: integer + minimum: 1 + description: Items per page. + default: 10 + title: limit + description: Items per page. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedSecretKeysResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + post: + tags: + - agent-studio + operationId: createSecretKey + x-acl: + - settings + summary: Create Secret Key + description: Create Secret Key. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SecretKeyCreate' + responses: + '201': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/SecretKeyResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/secret-keys/{secretKeyId}: + get: + tags: + - agent-studio + operationId: getSecretKey + x-acl: + - settings + summary: Get Secret Key + description: Get Secret Key. + parameters: + - name: secretKeyId + in: path + required: true + schema: + type: string + title: secretKeyId + description: The secretKeyId. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/SecretKeyResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + patch: + tags: + - agent-studio + operationId: updateSecretKey + x-acl: + - settings + summary: Patch Secret Key + description: Patch Secret Key. + parameters: + - name: secretKeyId + in: path + required: true + schema: + type: string + title: secretKeyId + description: The secretKeyId. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SecretKeyPatch' + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/SecretKeyResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteSecretKey + x-acl: + - settings + summary: Delete Secret Key + description: Delete Secret Key. + parameters: + - name: secretKeyId + in: path + required: true + schema: + type: string + title: secretKeyId + description: The secretKeyId. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /1/user-data/{userToken}: + get: + tags: + - agent-studio + operationId: getUserData + x-acl: + - logs + summary: Get Data By User Token + description: >- + Retrieves all memories, conversations and their messages for the given + user token. + parameters: + - name: userToken + in: path + required: true + schema: + type: string + title: userToken + description: The userToken. + responses: + '200': + description: Successful Response. + content: + application/json: + schema: + $ref: '#/components/schemas/UserDataResponse' + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + delete: + tags: + - agent-studio + operationId: deleteUserData + x-acl: + - logs + summary: Delete Data By User Token + description: >- + Permanently deletes all messages for the given user token. Does not + delete conversations. + parameters: + - name: userToken + in: path + required: true + schema: + type: string + title: userToken + description: The userToken. + responses: + '204': + description: Successful Response. + '422': + description: Validation Error. + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + /{path}: + get: + operationId: customGet + summary: Send requests to the Algolia REST API + description: This method lets you send requests to the Algolia REST API. + parameters: + - $ref: '#/components/parameters/PathInPath' + - $ref: '#/components/parameters/Parameters' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + '400': + $ref: '#/components/responses/BadRequest' + '402': + $ref: '#/components/responses/FeatureNotEnabled' + '403': + $ref: '#/components/responses/MethodNotAllowed' + '404': + $ref: '#/components/responses/IndexNotFound' + tags: + - agent-studio + post: + operationId: customPost + requestBody: + description: Parameters to send with the custom request. + content: + application/json: + schema: + type: object + summary: Send requests to the Algolia REST API + description: This method lets you send requests to the Algolia REST API. + parameters: + - $ref: '#/components/parameters/PathInPath' + - $ref: '#/components/parameters/Parameters' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + '400': + $ref: '#/components/responses/BadRequest' + '402': + $ref: '#/components/responses/FeatureNotEnabled' + '403': + $ref: '#/components/responses/MethodNotAllowed' + '404': + $ref: '#/components/responses/IndexNotFound' + tags: + - agent-studio + put: + operationId: customPut + requestBody: + description: Parameters to send with the custom request. + content: + application/json: + schema: + type: object + summary: Send requests to the Algolia REST API + description: This method lets you send requests to the Algolia REST API. + parameters: + - $ref: '#/components/parameters/PathInPath' + - $ref: '#/components/parameters/Parameters' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + '400': + $ref: '#/components/responses/BadRequest' + '402': + $ref: '#/components/responses/FeatureNotEnabled' + '403': + $ref: '#/components/responses/MethodNotAllowed' + '404': + $ref: '#/components/responses/IndexNotFound' + tags: + - agent-studio + delete: + operationId: customDelete + summary: Send requests to the Algolia REST API + description: This method lets you send requests to the Algolia REST API. + parameters: + - $ref: '#/components/parameters/PathInPath' + - $ref: '#/components/parameters/Parameters' + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + '400': + $ref: '#/components/responses/BadRequest' + '402': + $ref: '#/components/responses/FeatureNotEnabled' + '403': + $ref: '#/components/responses/MethodNotAllowed' + '404': + $ref: '#/components/responses/IndexNotFound' + tags: + - agent-studio + /setClientApiKey: + get: + x-helper: true + x-asynchronous-helper: false + x-acl: [] + tags: + - agent-studio + operationId: setClientApiKey + summary: Switch the API key used to authenticate requests + description: | + Switch the API key used to authenticate requests. + parameters: + - in: query + name: apiKey + description: API key to use for subsequent requests. + required: true + schema: + type: string + responses: + '204': + description: No content. +components: + securitySchemes: + appId: + type: apiKey + in: header + name: x-algolia-application-id + description: Your Algolia application ID. + apiKey: + type: apiKey + in: header + name: x-algolia-api-key + description: > + Your Algolia API key with the necessary permissions to make the request. + + Permissions are controlled through access control lists (ACL) and access + restrictions. + + The required ACL to make a request is listed in each endpoint's + reference. + schemas: + AgentStatus: + type: string + enum: + - draft + - published + ClientToolsArgsSchema: + properties: + type: + type: string + const: object + title: type + default: object + properties: + additionalProperties: true + type: object + title: properties + required: + items: + type: string + type: array + title: required + type: object + title: clientToolsArgsSchema + ClientSideToolConfig: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: client_side + title: type + default: client_side + description: + type: string + maxLength: 200 + minLength: 1 + title: description + inputSchema: + $ref: '#/components/schemas/ClientToolsArgsSchema' + type: object + required: + - name + - description + - inputSchema + title: clientSideToolConfig + QueryType: + type: string + enum: + - prefixLast + - prefixAll + - prefixNone + title: queryType + description: >- + Determines if and how query words are interpreted as prefixes. By + default, only the last query word is treated as a prefix (`prefixLast`). + To turn off prefix search, use `prefixNone`. Avoid `prefixAll`, which + treats all query words as prefixes. This might lead to counterintuitive + results and makes your search slower. For more information, see [Prefix + searching](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/prefix-searching). + QueryTypeNullable: + oneOf: + - $ref: '#/components/schemas/QueryType' + - type: 'null' + SupportedLanguage: + type: string + enum: + - af + - ar + - az + - bg + - bn + - ca + - cs + - cy + - da + - de + - el + - en + - eo + - es + - et + - eu + - fa + - fi + - fo + - fr + - ga + - gl + - he + - hi + - hu + - hy + - id + - is + - it + - ja + - ka + - kk + - ko + - ku + - ky + - lt + - lv + - mi + - mn + - mr + - ms + - mt + - nb + - nl + - 'no' + - ns + - pl + - ps + - pt + - pt-br + - qu + - ro + - ru + - sk + - sq + - sv + - sw + - ta + - te + - th + - tl + - tn + - tr + - tt + - uk + - ur + - uz + - zh + title: supportedLanguage + description: ISO code for a supported language. + QueryLanguagesUnion: + oneOf: + - items: + $ref: '#/components/schemas/SupportedLanguage' + type: array + - type: 'null' + AdvancedSyntaxFeatures: + type: string + enum: + - exactPhrase + - excludeWords + title: advancedSyntaxFeatures + description: AdvancedSyntaxFeatures. + AdvancedSyntaxFeaturesUnion: + oneOf: + - items: + $ref: '#/components/schemas/AdvancedSyntaxFeatures' + type: array + - type: 'null' + AlternativesAsExact: + type: string + enum: + - ignorePlurals + - singleWordSynonym + - multiWordsSynonym + - ignoreConjugations + title: alternativesAsExact + description: AlternativesAsExact. + AlternativesAsExactUnion: + oneOf: + - items: + $ref: '#/components/schemas/AlternativesAsExact' + type: array + - type: 'null' + TypoToleranceUnionSearchParametersOutput: + oneOf: + - type: boolean + - type: string + - type: 'null' + FacetFilters-OutputUnion: + oneOf: + - items: + $ref: '#/components/schemas/FacetFilters-OutputUnion' + type: array + - type: string + - type: 'null' + FacetFiltersOutputNullable: + oneOf: + - $ref: '#/components/schemas/FacetFilters-OutputUnion' + - type: 'null' + Facets: + properties: + order: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: order + additionalProperties: true + type: object + title: facets + description: Order of facet names. + FacetsUnion: + oneOf: + - items: + type: string + type: array + - $ref: '#/components/schemas/Facets' + - type: 'null' + NumericFiltersUnion: + oneOf: + - type: string + - items: + $ref: '#/components/schemas/NumericFiltersUnion' + type: array + - type: 'null' + TagFiltersUnion: + oneOf: + - type: string + - items: + $ref: '#/components/schemas/TagFiltersUnion' + type: array + - type: 'null' + AroundRadiusUnion: + oneOf: + - type: integer + - type: string + - type: 'null' + AroundPrecisionUnion: + oneOf: + - type: integer + - items: + additionalProperties: + type: integer + type: object + type: array + - type: 'null' + InsideBoundingBoxUnion: + oneOf: + - type: string + - items: + items: + type: number + type: array + type: array + - type: 'null' + InsidePolygonUnion: + oneOf: + - type: string + - items: + items: + type: number + type: array + type: array + - type: 'null' + DistinctUnion: + oneOf: + - type: boolean + - type: integer + - type: 'null' + ReRankingApplyFilterUnion: + oneOf: + - type: string + - items: + $ref: '#/components/schemas/ReRankingApplyFilterUnion' + type: array + - type: 'null' + RemoveStopWordsUnionSearchParametersOutput: + oneOf: + - type: boolean + - items: + type: string + type: array + - type: 'null' + IgnorePluralsUnionSearchParametersOutput: + oneOf: + - type: boolean + - items: + type: string + type: array + - type: 'null' + RemoveWordsIfNoResults: + type: string + enum: + - none + - lastWords + - firstWords + - allOptional + title: removeWordsIfNoResults + description: >- + Strategy for removing words from the query when it doesn't return any + results. This helps to avoid returning empty search results. - + `none`. No words are removed when a query doesn't return results. - + `lastWords`. Treat the last (then second to last, then third to last) + word as optional, until there are results or at most 5 words have been + removed. - `firstWords`. Treat the first (then second, then third) + word as optional, until there are results or at most 5 words have been + removed. - `allOptional`. Treat all words as optional. For more + information, see [Remove words to improve + results](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/empty-or-insufficient-results/in-depth/why-use-remove-words-if-no-results). + RemoveWordsIfNoResultsNullable: + oneOf: + - $ref: '#/components/schemas/RemoveWordsIfNoResults' + - type: 'null' + OptionalWordsUnion: + oneOf: + - type: string + - items: + type: string + type: array + - type: 'null' + OptionalFiltersUnion: + oneOf: + - type: string + - items: + $ref: '#/components/schemas/OptionalFiltersUnion' + type: array + - type: 'null' + ExactOnSingleWordQuery: + type: string + enum: + - attribute + - none + - word + title: exactOnSingleWordQuery + description: >- + Determines how the [Exact ranking + criterion](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/override-search-engine-defaults/in-depth/adjust-exact-settings/#turn-off-exact-for-some-attributes) + is computed when the search query has only one word. - `attribute`. + The Exact ranking criterion is 1 if the query word and attribute value + are the same. For example, a search for "road" will match the value + "road", but not "road trip". - `none`. The Exact ranking criterion is + ignored on single-word searches. - `word`. The Exact ranking + criterion is 1 if the query word is found in the attribute value. The + query word must have at least 3 characters and must not be a stop + word. Only exact matches will be highlighted, partial and prefix + matches won't. + ExactOnSingleWordQueryNullable: + oneOf: + - $ref: '#/components/schemas/ExactOnSingleWordQuery' + - type: 'null' + NaturalLanguagesUnion: + oneOf: + - items: + $ref: '#/components/schemas/SupportedLanguage' + type: array + - type: 'null' + SearchParameters-Output: + properties: + queryType: + $ref: '#/components/schemas/QueryTypeNullable' + similarQuery: + oneOf: + - type: string + - type: 'null' + title: similarquery + queryLanguages: + $ref: '#/components/schemas/QueryLanguagesUnion' + advancedSyntax: + oneOf: + - type: boolean + - type: 'null' + title: advancedsyntax + advancedSyntaxFeatures: + $ref: '#/components/schemas/AdvancedSyntaxFeaturesUnion' + alternativesAsExact: + $ref: '#/components/schemas/AlternativesAsExactUnion' + decompoundQuery: + oneOf: + - type: boolean + - type: 'null' + title: decompoundquery + typoTolerance: + $ref: '#/components/schemas/TypoToleranceUnionSearchParametersOutput' + allowTyposOnNumericTokens: + oneOf: + - type: boolean + - type: 'null' + title: allowtyposonnumerictokens + minWordSizeFor1Typo: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor1Typo + minWordSizeFor2Typos: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor2Typos + disableTypoToleranceOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disabletypotoleranceonattributes + filters: + oneOf: + - type: string + - type: 'null' + title: filters + facetFilters: + $ref: '#/components/schemas/FacetFiltersOutputNullable' + facets: + $ref: '#/components/schemas/FacetsUnion' + maxValuesPerFacet: + oneOf: + - type: integer + - type: 'null' + title: maxvaluesperfacet + maxFacetHits: + oneOf: + - type: integer + - type: 'null' + title: maxfacethits + facetingAfterDistinct: + oneOf: + - type: boolean + - type: 'null' + title: facetingafterdistinct + sortFacetValuesBy: + oneOf: + - type: string + - type: 'null' + title: sortfacetvaluesby + numericFilters: + $ref: '#/components/schemas/NumericFiltersUnion' + tagFilters: + $ref: '#/components/schemas/TagFiltersUnion' + sumOrFiltersScores: + oneOf: + - type: boolean + - type: 'null' + title: sumorfiltersscores + aroundLatLng: + oneOf: + - type: string + - type: 'null' + title: aroundlatlng + aroundLatLngViaIp: + oneOf: + - type: boolean + - type: 'null' + title: aroundlatlngviaip + aroundRadius: + $ref: '#/components/schemas/AroundRadiusUnion' + aroundPrecision: + $ref: '#/components/schemas/AroundPrecisionUnion' + minimumAroundRadius: + oneOf: + - type: integer + - type: 'null' + title: minimumaroundradius + insideBoundingBox: + $ref: '#/components/schemas/InsideBoundingBoxUnion' + insidePolygon: + $ref: '#/components/schemas/InsidePolygonUnion' + attributesToRetrieve: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestoretrieve + attributesToSnippet: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestosnippet + snippetEllipsisText: + oneOf: + - type: string + - type: 'null' + title: snippetellipsistext + restrictHighlightAndSnippetArrays: + oneOf: + - type: boolean + - type: 'null' + title: restricthighlightandsnippetarrays + page: + oneOf: + - type: integer + - type: 'null' + title: page + offset: + oneOf: + - type: integer + - type: 'null' + title: offset + hitsPerPage: + oneOf: + - type: integer + - type: 'null' + title: hitsperpage + length: + oneOf: + - type: integer + - type: 'null' + title: length + getRankingInfo: + oneOf: + - type: boolean + - type: 'null' + title: getrankinginfo + relevancyStrictness: + oneOf: + - type: integer + - type: 'null' + title: relevancystrictness + minProximity: + oneOf: + - type: integer + - type: 'null' + title: minproximity + attributeCriteriaComputedByMinProximity: + oneOf: + - type: boolean + - type: 'null' + title: attributecriteriacomputedbyminproximity + distinct: + $ref: '#/components/schemas/DistinctUnion' + enableRules: + oneOf: + - type: boolean + - type: 'null' + title: enablerules + enablePersonalization: + oneOf: + - type: boolean + - type: 'null' + title: enablepersonalization + personalizationImpact: + oneOf: + - type: integer + - type: 'null' + title: personalizationimpact + enableAbTest: + oneOf: + - type: boolean + - type: 'null' + title: enableabtest + enableReRanking: + oneOf: + - type: boolean + - type: 'null' + title: enablereranking + reRankingApplyFilter: + $ref: '#/components/schemas/ReRankingApplyFilterUnion' + ruleContexts: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: rulecontexts + removeStopWords: + $ref: '#/components/schemas/RemoveStopWordsUnionSearchParametersOutput' + ignorePlurals: + $ref: '#/components/schemas/IgnorePluralsUnionSearchParametersOutput' + removeWordsIfNoResults: + $ref: '#/components/schemas/RemoveWordsIfNoResultsNullable' + optionalWords: + $ref: '#/components/schemas/OptionalWordsUnion' + optionalFilters: + $ref: '#/components/schemas/OptionalFiltersUnion' + synonyms: + oneOf: + - type: boolean + - type: 'null' + title: synonyms + replaceSynonymsInHighlight: + oneOf: + - type: boolean + - type: 'null' + title: replacesynonymsinhighlight + analytics: + oneOf: + - type: boolean + - type: 'null' + title: analytics + analyticsTags: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: analyticstags + clickAnalytics: + oneOf: + - type: boolean + - type: 'null' + title: clickanalytics + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + restrictSearchableAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: restrictsearchableattributes + disableExactOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disableexactonattributes + exactOnSingleWordQuery: + $ref: '#/components/schemas/ExactOnSingleWordQueryNullable' + naturalLanguages: + $ref: '#/components/schemas/NaturalLanguagesUnion' + percentileComputation: + oneOf: + - type: boolean + - type: 'null' + title: percentilecomputation + explain: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: explain + type: object + title: searchParameters + description: >- + Algolia Search API parameters that can be predefined for the search + tool. + + Reference: + https://www.algolia.com/doc/api-reference/search-api-parameters/ + + + The parameters that seemed irrelevant for the search tool have been + commented out. + + Uses types from algoliasearch.search.models for better type safety. + SearchParametersOutputNullable: + oneOf: + - $ref: '#/components/schemas/SearchParameters-Output' + - type: 'null' + TextParam: + properties: + exposed: + type: boolean + title: exposed + default: + oneOf: + - type: string + - type: 'null' + title: default + type: object + required: + - exposed + title: textParam + description: A text search parameter with exposure control. + TextParamNullable: + oneOf: + - $ref: '#/components/schemas/TextParam' + - type: 'null' + NumberParamConstraint: + properties: + min: + oneOf: + - type: integer + - type: 'null' + title: min + max: + oneOf: + - type: integer + - type: 'null' + title: max + type: object + title: numberParamConstraint + description: Constraints for a number parameter. + NumberParamConstraintNullable: + oneOf: + - $ref: '#/components/schemas/NumberParamConstraint' + - type: 'null' + NumberParam: + properties: + exposed: + type: boolean + title: exposed + default: + oneOf: + - type: integer + - type: 'null' + title: default + constraint: + $ref: '#/components/schemas/NumberParamConstraintNullable' + type: object + required: + - exposed + title: numberParam + description: >- + A number search parameter with exposure control and optional + constraints. + NumberParamNullable: + oneOf: + - $ref: '#/components/schemas/NumberParam' + - type: 'null' + StringArrayParamConstraint: + properties: + values: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: values + type: object + title: stringArrayParamConstraint + description: Constraints for a string array parameter. + StringArrayParamConstraintNullable: + oneOf: + - $ref: '#/components/schemas/StringArrayParamConstraint' + - type: 'null' + StringArrayParam: + properties: + exposed: + type: boolean + title: exposed + default: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: default + constraint: + $ref: '#/components/schemas/StringArrayParamConstraintNullable' + merge: + oneOf: + - type: boolean + - type: 'null' + title: merge + type: object + required: + - exposed + title: stringArrayParam + description: >- + A string array search parameter with exposure control, constraints, and + merge behavior. + StringArrayParamNullable: + oneOf: + - $ref: '#/components/schemas/StringArrayParam' + - type: 'null' + FacetsParam: + properties: + exposed: + type: boolean + const: false + title: exposed + default: false + default: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: default + type: object + title: facetsParam + description: A facets parameter that is always hidden from the LLM. + FacetsParamNullable: + oneOf: + - $ref: '#/components/schemas/FacetsParam' + - type: 'null' + IndexSearchParameters-Output: + properties: + query: + $ref: '#/components/schemas/TextParamNullable' + hitsPerPage: + $ref: '#/components/schemas/NumberParamNullable' + page: + $ref: '#/components/schemas/NumberParamNullable' + attributesToRetrieve: + $ref: '#/components/schemas/StringArrayParamNullable' + responseFields: + $ref: '#/components/schemas/StringArrayParamNullable' + facets: + $ref: '#/components/schemas/FacetsParamNullable' + custom: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: custom + type: object + title: indexSearchParameters + description: >- + Structured search parameters configuration for an Algolia index. + + + Each parameter controls whether it is exposed to the LLM, its default + value, + + optional constraints, and merge behavior. + IndexSearchParametersOutputNullable: + oneOf: + - $ref: '#/components/schemas/IndexSearchParameters-Output' + - type: 'null' + AlgoliaSearchToolIndexConfig-Output: + properties: + index: + type: string + maxLength: 100 + minLength: 1 + title: index + description: + type: string + maxLength: 3000 + minLength: 1 + title: description + enhancedDescription: + type: string + title: enhanceddescription + default: '' + searchParameters: + $ref: '#/components/schemas/SearchParametersOutputNullable' + searchControls: + $ref: '#/components/schemas/IndexSearchParametersOutputNullable' + type: object + required: + - index + - description + title: algoliaSearchToolIndexConfig + AlgoliaSearchToolConfig-Output: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_search_index + title: type + default: algolia_search_index + indices: + items: + $ref: '#/components/schemas/AlgoliaSearchToolIndexConfig-Output' + type: array + title: indices + description: + type: string + title: description + readOnly: true + type: object + required: + - name + - indices + - description + title: algoliaSearchToolConfig + AlgoliaRecommendToolIndexConfig: + properties: + index: + type: string + maxLength: 100 + minLength: 1 + title: index + modelName: + type: string + maxLength: 100 + minLength: 1 + title: modelname + description: + type: string + title: description + default: '' + type: object + required: + - index + - modelName + title: algoliaRecommendToolIndexConfig + AlgoliaRecommendToolConfig-Output: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_recommend + title: type + default: algolia_recommend + allowedConfigs: + items: + $ref: '#/components/schemas/AlgoliaRecommendToolIndexConfig' + type: array + title: allowedconfigs + default: [] + predefinedRecommendParameters: + additionalProperties: true + type: object + title: predefinedrecommendparameters + description: + type: string + title: description + readOnly: true + type: object + required: + - name + - description + title: algoliaRecommendToolConfig + description: |- + Configuration for the Algolia Recommend tool. + Allows specifying recommend models and related parameters. + AlgoliaDisplayResultsToolConfig: + properties: + name: + type: string + const: algolia_display_results + title: name + default: algolia_display_results + type: + type: string + const: algolia_display_results + title: type + default: algolia_display_results + minGroups: + type: integer + minimum: 1 + title: mingroups + default: 1 + maxGroups: + type: integer + minimum: 1 + title: maxgroups + default: 3 + minResultsPerGroup: + type: integer + minimum: 1 + title: minresultspergroup + default: 3 + maxResultsPerGroup: + type: integer + minimum: 1 + title: maxresultspergroup + default: 6 + type: object + title: algoliaDisplayResultsToolConfig + description: Configuration for the algolia_display_results tool. + McpToolConfig: + properties: + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + default: false + alias: + oneOf: + - type: string + maxLength: 32 + minLength: 3 + - type: 'null' + title: alias + type: object + title: mcpToolConfig + ToolConfig: + oneOf: + - $ref: '#/components/schemas/McpToolConfig' + - type: boolean + AllowedToolsUnion: + oneOf: + - additionalProperties: + $ref: '#/components/schemas/ToolConfig' + type: object + - type: 'null' + McpServerToolConfig-Output: + properties: + url: + type: string + maxLength: 512 + minLength: 1 + title: url + transport: + type: string + const: streamable_http + title: transport + default: streamable_http + headers: + additionalProperties: + type: string + type: object + title: headers + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: mcp_tools + title: type + default: mcp_tools + id: + oneOf: + - type: string + - type: 'null' + title: id + description: Stable unique identifier for this MCP tool. + allowedTools: + $ref: '#/components/schemas/AllowedToolsUnion' + type: object + required: + - url + - headers + - name + title: mcpServerToolConfig + UnknownToolConfig: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: unknown + title: type + default: unknown + additionalProperties: true + type: object + required: + - name + title: unknownToolConfig + description: >- + Exists only to ensure that when you change branch from toolX to + feat/toolY, your config stays valid. + ToolConfigOutput: + oneOf: + - $ref: '#/components/schemas/ClientSideToolConfig' + - $ref: '#/components/schemas/AlgoliaSearchToolConfig-Output' + - $ref: '#/components/schemas/AlgoliaRecommendToolConfig-Output' + - $ref: '#/components/schemas/AlgoliaDisplayResultsToolConfig' + - $ref: '#/components/schemas/McpServerToolConfig-Output' + - $ref: '#/components/schemas/UnknownToolConfig' + AgentWithVersionResponse: + properties: + id: + type: string + title: id + name: + type: string + title: name + description: + oneOf: + - type: string + - type: 'null' + title: description + status: + $ref: '#/components/schemas/AgentStatus' + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + type: string + title: instructions + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + config: + additionalProperties: true + type: object + title: config + tools: + items: + $ref: '#/components/schemas/ToolConfigOutput' + type: array + title: tools + templateType: + oneOf: + - type: string + - type: 'null' + title: templatetype + createdAt: + type: string + title: createdat + updatedAt: + oneOf: + - type: string + - type: 'null' + title: updatedat + lastUsedAt: + oneOf: + - type: string + - type: 'null' + title: lastusedat + type: object + required: + - id + - name + - description + - status + - providerId + - instructions + - config + - createdAt + - updatedAt + - lastUsedAt + title: agentWithVersionResponse + PaginationMetadata: + properties: + page: + type: integer + title: page + limit: + type: integer + title: limit + totalCount: + type: integer + title: totalcount + totalPages: + type: integer + title: totalpages + type: object + required: + - page + - limit + - totalCount + - totalPages + title: paginationMetadata + PaginatedAgentsResponse: + properties: + data: + items: + $ref: '#/components/schemas/AgentWithVersionResponse' + type: array + title: data + pagination: + $ref: '#/components/schemas/PaginationMetadata' + type: object + required: + - data + - pagination + title: paginatedAgentsResponse + LocationItemUnion: + oneOf: + - type: string + - type: integer + ValidationError: + properties: + loc: + items: + $ref: '#/components/schemas/LocationItemUnion' + type: array + title: location + msg: + type: string + title: message + type: + type: string + title: errorType + type: object + required: + - loc + - msg + - type + title: validationError + HTTPValidationError: + properties: + detail: + items: + $ref: '#/components/schemas/ValidationError' + type: array + title: detail + type: object + title: hTTPValidationError + TypoToleranceEnum: + type: string + enum: + - min + - strict + - 'true' + - 'false' + title: typoToleranceEnum + description: >- + - `min`. Return matches with the lowest number of typos. For example, + if you have matches without typos, only include those. But if there + are no matches without typos (with 1 typo), include matches with 1 typo + (2 typos). - `strict`. Return matches with the two lowest numbers of + typos. With `strict`, the Typo ranking criterion is applied first in + the `ranking` setting. + TypoToleranceUnion: + oneOf: + - type: boolean + - $ref: '#/components/schemas/TypoToleranceEnum' + - type: 'null' + FacetFilters-Input: + properties: + oneof_schema_1_validator: + $ref: '#/components/schemas/Oneof_schema_1_validatorUnion' + oneof_schema_2_validator: + oneOf: + - type: string + - type: 'null' + title: oneofSchema2Validator + actual_instance: + $ref: '#/components/schemas/Actual_instanceUnion' + one_of_schemas: + items: + type: string + type: array + title: oneOfSchemas + default: + - str + - List[FacetFilters] + type: object + title: facetFilters + description: >- + Filter the search by facet values, so that only records with the same + facet values are retrieved. **Prefer using the `filters` parameter, + which supports all filter types and combinations with boolean + operators.** - `[filter1, filter2]` is interpreted as `filter1 AND + filter2`. - `[[filter1, filter2], filter3]` is interpreted as `filter1 + OR filter2 AND filter3`. - `facet:-value` is interpreted as `NOT + facet:value`. While it's best to avoid attributes that start with a + `-`, you can still filter them by escaping with a backslash: + `facet:\-value`. + Oneof_schema_1_validatorUnion: + oneOf: + - items: + $ref: '#/components/schemas/FacetFilters-Input' + type: array + - type: 'null' + Actual_instanceUnion: + oneOf: + - items: + $ref: '#/components/schemas/FacetFilters-Input' + type: array + - type: string + - type: 'null' + FacetFiltersInputNullable: + oneOf: + - $ref: '#/components/schemas/FacetFilters-Input' + - type: 'null' + RemoveStopWordsUnion: + oneOf: + - type: boolean + - items: + $ref: '#/components/schemas/SupportedLanguage' + type: array + - type: 'null' + IgnorePluralsUnion: + oneOf: + - type: boolean + - items: + $ref: '#/components/schemas/SupportedLanguage' + type: array + - type: 'null' + SearchParameters-Input: + properties: + queryType: + $ref: '#/components/schemas/QueryTypeNullable' + similarQuery: + oneOf: + - type: string + - type: 'null' + title: similarquery + queryLanguages: + $ref: '#/components/schemas/QueryLanguagesUnion' + advancedSyntax: + oneOf: + - type: boolean + - type: 'null' + title: advancedsyntax + advancedSyntaxFeatures: + $ref: '#/components/schemas/AdvancedSyntaxFeaturesUnion' + alternativesAsExact: + $ref: '#/components/schemas/AlternativesAsExactUnion' + decompoundQuery: + oneOf: + - type: boolean + - type: 'null' + title: decompoundquery + typoTolerance: + $ref: '#/components/schemas/TypoToleranceUnion' + allowTyposOnNumericTokens: + oneOf: + - type: boolean + - type: 'null' + title: allowtyposonnumerictokens + minWordSizeFor1Typo: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor1Typo + minWordSizeFor2Typos: + oneOf: + - type: integer + - type: 'null' + title: minwordsizefor2Typos + disableTypoToleranceOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disabletypotoleranceonattributes + filters: + oneOf: + - type: string + - type: 'null' + title: filters + facetFilters: + $ref: '#/components/schemas/FacetFiltersInputNullable' + facets: + $ref: '#/components/schemas/FacetsUnion' + maxValuesPerFacet: + oneOf: + - type: integer + - type: 'null' + title: maxvaluesperfacet + maxFacetHits: + oneOf: + - type: integer + - type: 'null' + title: maxfacethits + facetingAfterDistinct: + oneOf: + - type: boolean + - type: 'null' + title: facetingafterdistinct + sortFacetValuesBy: + oneOf: + - type: string + - type: 'null' + title: sortfacetvaluesby + numericFilters: + $ref: '#/components/schemas/NumericFiltersUnion' + tagFilters: + $ref: '#/components/schemas/TagFiltersUnion' + sumOrFiltersScores: + oneOf: + - type: boolean + - type: 'null' + title: sumorfiltersscores + aroundLatLng: + oneOf: + - type: string + - type: 'null' + title: aroundlatlng + aroundLatLngViaIp: + oneOf: + - type: boolean + - type: 'null' + title: aroundlatlngviaip + aroundRadius: + $ref: '#/components/schemas/AroundRadiusUnion' + aroundPrecision: + $ref: '#/components/schemas/AroundPrecisionUnion' + minimumAroundRadius: + oneOf: + - type: integer + - type: 'null' + title: minimumaroundradius + insideBoundingBox: + $ref: '#/components/schemas/InsideBoundingBoxUnion' + insidePolygon: + $ref: '#/components/schemas/InsidePolygonUnion' + attributesToRetrieve: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestoretrieve + attributesToSnippet: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestosnippet + snippetEllipsisText: + oneOf: + - type: string + - type: 'null' + title: snippetellipsistext + restrictHighlightAndSnippetArrays: + oneOf: + - type: boolean + - type: 'null' + title: restricthighlightandsnippetarrays + page: + oneOf: + - type: integer + - type: 'null' + title: page + offset: + oneOf: + - type: integer + - type: 'null' + title: offset + hitsPerPage: + oneOf: + - type: integer + - type: 'null' + title: hitsperpage + length: + oneOf: + - type: integer + - type: 'null' + title: length + getRankingInfo: + oneOf: + - type: boolean + - type: 'null' + title: getrankinginfo + relevancyStrictness: + oneOf: + - type: integer + - type: 'null' + title: relevancystrictness + minProximity: + oneOf: + - type: integer + - type: 'null' + title: minproximity + attributeCriteriaComputedByMinProximity: + oneOf: + - type: boolean + - type: 'null' + title: attributecriteriacomputedbyminproximity + distinct: + $ref: '#/components/schemas/DistinctUnion' + enableRules: + oneOf: + - type: boolean + - type: 'null' + title: enablerules + enablePersonalization: + oneOf: + - type: boolean + - type: 'null' + title: enablepersonalization + personalizationImpact: + oneOf: + - type: integer + - type: 'null' + title: personalizationimpact + enableAbTest: + oneOf: + - type: boolean + - type: 'null' + title: enableabtest + enableReRanking: + oneOf: + - type: boolean + - type: 'null' + title: enablereranking + reRankingApplyFilter: + $ref: '#/components/schemas/ReRankingApplyFilterUnion' + ruleContexts: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: rulecontexts + removeStopWords: + $ref: '#/components/schemas/RemoveStopWordsUnion' + ignorePlurals: + $ref: '#/components/schemas/IgnorePluralsUnion' + removeWordsIfNoResults: + $ref: '#/components/schemas/RemoveWordsIfNoResultsNullable' + optionalWords: + $ref: '#/components/schemas/OptionalWordsUnion' + optionalFilters: + $ref: '#/components/schemas/OptionalFiltersUnion' + synonyms: + oneOf: + - type: boolean + - type: 'null' + title: synonyms + replaceSynonymsInHighlight: + oneOf: + - type: boolean + - type: 'null' + title: replacesynonymsinhighlight + analytics: + oneOf: + - type: boolean + - type: 'null' + title: analytics + analyticsTags: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: analyticstags + clickAnalytics: + oneOf: + - type: boolean + - type: 'null' + title: clickanalytics + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + restrictSearchableAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: restrictsearchableattributes + disableExactOnAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: disableexactonattributes + exactOnSingleWordQuery: + $ref: '#/components/schemas/ExactOnSingleWordQueryNullable' + naturalLanguages: + $ref: '#/components/schemas/NaturalLanguagesUnion' + percentileComputation: + oneOf: + - type: boolean + - type: 'null' + title: percentilecomputation + explain: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: explain + type: object + title: searchParameters + description: >- + Algolia Search API parameters that can be predefined for the search + tool. + + Reference: + https://www.algolia.com/doc/api-reference/search-api-parameters/ + + + The parameters that seemed irrelevant for the search tool have been + commented out. + + Uses types from algoliasearch.search.models for better type safety. + SearchParametersInputNullable: + oneOf: + - $ref: '#/components/schemas/SearchParameters-Input' + - type: 'null' + IndexSearchParameters-Input: + properties: + query: + $ref: '#/components/schemas/TextParamNullable' + hitsPerPage: + $ref: '#/components/schemas/NumberParamNullable' + page: + $ref: '#/components/schemas/NumberParamNullable' + attributesToRetrieve: + $ref: '#/components/schemas/StringArrayParamNullable' + responseFields: + $ref: '#/components/schemas/StringArrayParamNullable' + facets: + $ref: '#/components/schemas/FacetsParamNullable' + custom: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: custom + type: object + title: indexSearchParameters + description: >- + Structured search parameters configuration for an Algolia index. + + + Each parameter controls whether it is exposed to the LLM, its default + value, + + optional constraints, and merge behavior. + IndexSearchParametersInputNullable: + oneOf: + - $ref: '#/components/schemas/IndexSearchParameters-Input' + - type: 'null' + AlgoliaSearchToolIndexConfig-Input: + properties: + index: + type: string + maxLength: 100 + minLength: 1 + title: index + description: + type: string + maxLength: 3000 + minLength: 1 + title: description + enhancedDescription: + type: string + title: enhanceddescription + default: '' + searchParameters: + $ref: '#/components/schemas/SearchParametersInputNullable' + searchControls: + $ref: '#/components/schemas/IndexSearchParametersInputNullable' + type: object + required: + - index + - description + title: algoliaSearchToolIndexConfig + AlgoliaSearchToolConfig-Input: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_search_index + title: type + default: algolia_search_index + indices: + items: + $ref: '#/components/schemas/AlgoliaSearchToolIndexConfig-Input' + type: array + title: indices + type: object + required: + - name + - indices + title: algoliaSearchToolConfig + AlgoliaRecommendToolConfig-Input: + properties: + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: algolia_recommend + title: type + default: algolia_recommend + allowedConfigs: + items: + $ref: '#/components/schemas/AlgoliaRecommendToolIndexConfig' + type: array + title: allowedconfigs + default: [] + predefinedRecommendParameters: + additionalProperties: true + type: object + title: predefinedrecommendparameters + type: object + required: + - name + title: algoliaRecommendToolConfig + description: |- + Configuration for the Algolia Recommend tool. + Allows specifying recommend models and related parameters. + McpServerToolConfig-Input: + properties: + url: + type: string + maxLength: 512 + minLength: 1 + title: url + transport: + type: string + const: streamable_http + title: transport + default: streamable_http + headers: + additionalProperties: + type: string + type: object + title: headers + name: + type: string + maxLength: 32 + minLength: 3 + title: name + type: + type: string + const: mcp_tools + title: type + default: mcp_tools + id: + oneOf: + - type: string + - type: 'null' + title: id + description: Stable unique identifier for this MCP tool. + allowedTools: + $ref: '#/components/schemas/AllowedToolsUnion' + type: object + required: + - url + - headers + - name + title: mcpServerToolConfig + ToolConfigInput: + oneOf: + - $ref: '#/components/schemas/ClientSideToolConfig' + - $ref: '#/components/schemas/AlgoliaSearchToolConfig-Input' + - $ref: '#/components/schemas/AlgoliaRecommendToolConfig-Input' + - $ref: '#/components/schemas/AlgoliaDisplayResultsToolConfig' + - $ref: '#/components/schemas/McpServerToolConfig-Input' + - $ref: '#/components/schemas/UnknownToolConfig' + AgentConfigCreate: + properties: + name: + type: string + title: name + description: + oneOf: + - type: string + - type: 'null' + title: description + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + type: string + title: instructions + description: >- + The agent prompt: defines the agent's role, tone, and goals. Guides + how it answers using the provided context. Corresponds to the 'Agent + prompt' field in the dashboard. + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + description: >- + The system prompt: defines system-level rules and constraints. + Guides how the agent uses tools, features, and generates context. + Prepended before `instructions` in the final prompt sent to the LLM. + Typically injected by an agent template — modify with caution, as + changes may affect behavior, tool usage, or response accuracy. + Corresponds to the 'System prompt' field in the dashboard. + templateType: + oneOf: + - type: string + - type: 'null' + title: templatetype + config: + additionalProperties: true + type: object + title: config + default: {} + example: + sendUsage: true + sendReasoning: true + temperature: 0.7 + max_tokens: 1500 + reasoning: '{summary: auto}' + features: '[memory]' + tools: + items: + $ref: '#/components/schemas/ToolConfigInput' + type: array + title: tools + type: object + required: + - name + - instructions + title: agentConfigCreate + AgentConfigUpdate: + properties: + name: + oneOf: + - type: string + - type: 'null' + title: name + description: + oneOf: + - type: string + - type: 'null' + title: description + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + oneOf: + - type: string + - type: 'null' + title: instructions + description: >- + The agent prompt: defines the agent's role, tone, and goals. Guides + how it answers using the provided context. Corresponds to the 'Agent + prompt' field in the dashboard. + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + description: >- + The system prompt: defines system-level rules and constraints. + Guides how the agent uses tools, features, and generates context. + Prepended before `instructions` in the final prompt sent to the LLM. + Typically injected by an agent template — modify with caution, as + changes may affect behavior, tool usage, or response accuracy. + Corresponds to the 'System prompt' field in the dashboard. + config: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: config + tools: + oneOf: + - items: + $ref: '#/components/schemas/ToolConfigInput' + type: array + - type: 'null' + title: tools + templateType: + oneOf: + - type: string + - type: 'null' + title: templatetype + type: object + title: agentConfigUpdate + AllowedDomainResponse: + properties: + id: + type: string + title: id + appId: + type: string + title: appid + agentId: + type: string + title: agentid + domain: + type: string + title: domain + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + type: object + required: + - id + - appId + - agentId + - domain + - createdAt + - updatedAt + title: allowedDomainResponse + description: Single allowed domain in API responses. + AllowedDomainListResponse: + properties: + domains: + items: + $ref: '#/components/schemas/AllowedDomainResponse' + type: array + title: domains + type: object + required: + - domains + title: allowedDomainListResponse + description: List of allowed domains for an application. + AllowedDomainCreate: + properties: + domain: + type: string + title: domain + description: Domain or pattern, e.g. https://app.example.com or *.example.com. + type: object + required: + - domain + title: allowedDomainCreate + description: Request body to add a single allowed domain. + AllowedDomainBulkInsert: + properties: + domains: + items: + type: string + type: array + title: domains + description: List of domain patterns to add. + type: object + required: + - domains + title: allowedDomainBulkInsert + description: Request body for bulk insert. + AllowedDomainBulkDelete: + properties: + domainIds: + items: + type: string + type: array + title: domainids + description: IDs of allowed domain records to delete. + type: object + required: + - domainIds + title: allowedDomainBulkDelete + description: Request body for bulk delete by IDs. + AgentIdUnion: + oneOf: + - type: string + - const: test + type: string + CompatibilityMode: + type: string + enum: + - ai-sdk-4 + - ai-sdk-5 + title: compatibilityMode + description: Support Compatibility modes for the completion API. + AgentTestConfiguration: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + providerId: + oneOf: + - type: string + - type: 'null' + title: providerid + model: + oneOf: + - type: string + - type: 'null' + title: model + instructions: + type: string + title: instructions + systemPrompt: + oneOf: + - type: string + - type: 'null' + title: systemprompt + config: + additionalProperties: true + type: object + title: config + tools: + items: + $ref: '#/components/schemas/ToolConfigInput' + type: array + title: tools + type: object + required: + - instructions + - config + - tools + title: agentTestConfiguration + description: Dynamic configuration for testing agents. + AgentTestConfigurationNullable: + oneOf: + - $ref: '#/components/schemas/AgentTestConfiguration' + - type: 'null' + TextPartV4: + properties: + type: + type: string + const: text + title: type + default: text + text: + type: string + title: text + type: object + required: + - text + title: textPartV4 + UserMessageV4: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: user + title: role + default: user + content: + type: string + title: content + parts: + items: + $ref: '#/components/schemas/TextPartV4' + type: array + title: parts + type: object + required: + - content + title: userMessageV4 + StepStartPartV4: + properties: + type: + type: string + const: step-start + title: type + default: step-start + type: object + title: stepStartPartV4 + ReasoningPartV4: + properties: + type: + type: string + const: reasoning + title: type + default: reasoning + reasoning: + type: string + title: reasoning + type: object + required: + - reasoning + title: reasoningPartV4 + ToolInvocationV4: + properties: + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + args: + additionalProperties: true + type: object + title: args + result: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: result + step: + oneOf: + - type: integer + - type: 'null' + title: step + state: + oneOf: + - type: string + - type: 'null' + title: state + providerOptions: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: provideroptions + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + description: + oneOf: + - type: string + - type: 'null' + title: description + argsHash: + oneOf: + - type: string + - type: 'null' + title: argshash + type: object + required: + - toolCallId + - toolName + title: toolInvocationV4 + description: Model for tool invocation in a Message. + ToolInvocationPartV4: + properties: + type: + type: string + const: tool-invocation + title: type + default: tool-invocation + toolInvocation: + $ref: '#/components/schemas/ToolInvocationV4' + type: object + required: + - toolInvocation + title: toolInvocationPartV4 + AssistantPartV4: + oneOf: + - $ref: '#/components/schemas/StepStartPartV4' + - $ref: '#/components/schemas/ReasoningPartV4' + - $ref: '#/components/schemas/TextPartV4' + - $ref: '#/components/schemas/ToolInvocationPartV4' + ToolInvocationsUnion: + oneOf: + - items: + $ref: '#/components/schemas/ToolInvocationV4' + type: array + - type: 'null' + AssistantMessageV4: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: assistant + title: role + default: assistant + content: + type: string + title: content + parts: + items: + $ref: '#/components/schemas/AssistantPartV4' + type: array + title: parts + toolInvocations: + $ref: '#/components/schemas/ToolInvocationsUnion' + type: object + required: + - content + title: assistantMessageV4 + MessageV4: + oneOf: + - $ref: '#/components/schemas/UserMessageV4' + - $ref: '#/components/schemas/AssistantMessageV4' + TextPartV5: + properties: + type: + type: string + const: text + title: type + default: text + text: + type: string + title: text + type: object + required: + - text + title: textPartV5 + UserMessageV5: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: user + title: role + default: user + parts: + items: + $ref: '#/components/schemas/TextPartV5' + type: array + title: parts + type: object + title: userMessageV5 + StepStartPartV5: + properties: + type: + type: string + const: step-start + title: type + default: step-start + type: object + title: stepStartPartV5 + ReasoningPartV5: + properties: + type: + type: string + const: reasoning + title: type + default: reasoning + text: + type: string + title: text + type: object + required: + - text + title: reasoningPartV5 + ToolState: + type: string + enum: + - input-streaming + - input-available + - output-available + - output-error + ToolPartV5: + properties: + type: + type: string + title: type + toolCallId: + type: string + title: toolcallid + state: + $ref: '#/components/schemas/ToolState' + input: + additionalProperties: true + type: object + title: input + output: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: output + errorText: + oneOf: + - type: string + - type: 'null' + title: errortext + providerOptions: + oneOf: + - additionalProperties: true + type: object + - type: 'null' + title: provideroptions + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + description: + oneOf: + - type: string + - type: 'null' + title: description + argsHash: + oneOf: + - type: string + - type: 'null' + title: argshash + type: object + required: + - type + - toolCallId + title: toolPartV5 + description: Model for tool invocation in a Message. + AssistantPartV5: + oneOf: + - $ref: '#/components/schemas/StepStartPartV5' + - $ref: '#/components/schemas/TextPartV5' + - $ref: '#/components/schemas/ReasoningPartV5' + - $ref: '#/components/schemas/ToolPartV5' + AssistantMessageV5: + properties: + id: + oneOf: + - type: string + - type: 'null' + title: id + role: + type: string + const: assistant + title: role + default: assistant + parts: + items: + $ref: '#/components/schemas/AssistantPartV5' + type: array + title: parts + type: object + title: assistantMessageV5 + MessageV5: + oneOf: + - $ref: '#/components/schemas/UserMessageV5' + - $ref: '#/components/schemas/AssistantMessageV5' + MessagesNullableUnionUnion: + oneOf: + - items: + $ref: '#/components/schemas/MessageV4' + type: array + - items: + $ref: '#/components/schemas/MessageV5' + type: array + MessagesNullableUnion: + oneOf: + - $ref: '#/components/schemas/MessagesNullableUnionUnion' + - type: 'null' + SearchParametersOverrides: + properties: + filters: + oneOf: + - type: string + - type: 'null' + title: filters + attributesToRetrieve: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: attributestoretrieve + restrictSearchableAttributes: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: restrictsearchableattributes + distinct: + $ref: '#/components/schemas/DistinctUnion' + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + enablePersonalization: + oneOf: + - type: boolean + - type: 'null' + title: enablepersonalization + personalizationImpact: + oneOf: + - type: integer + - type: 'null' + title: personalizationimpact + additionalProperties: false + type: object + title: searchParametersOverrides + description: >- + Algolia Search API parameters that can be predefined for the search + tool. + + Reference: + https://www.algolia.com/doc/api-reference/search-api-parameters/ + + + A subset of SearchParameters of specific params we allow for runtime + override. + SearchParametersOverridesMapNullable: + oneOf: + - additionalProperties: + $ref: '#/components/schemas/SearchParametersOverrides' + type: object + - type: 'null' + AgentCompletionAlgoliaParams: + properties: + mcpServers: + oneOf: + - additionalProperties: + additionalProperties: + additionalProperties: + type: string + type: object + type: object + type: object + - type: 'null' + title: mcpservers + searchParameters: + $ref: '#/components/schemas/SearchParametersOverridesMapNullable' + type: object + title: agentCompletionAlgoliaParams + AgentCompletionAlgoliaParamsNullable: + oneOf: + - $ref: '#/components/schemas/AgentCompletionAlgoliaParams' + - type: 'null' + AgentCompletionRequest: + properties: + configuration: + $ref: '#/components/schemas/AgentTestConfigurationNullable' + messages: + $ref: '#/components/schemas/MessagesNullableUnion' + id: + oneOf: + - type: string + maxLength: 128 + - type: 'null' + title: id + description: Optional conversation id. + algolia: + $ref: '#/components/schemas/AgentCompletionAlgoliaParamsNullable' + toolApprovals: + oneOf: + - type: object + - type: 'null' + title: toolapprovals + type: object + title: agentCompletionRequest + description: Request model for creating a completion for an assistant. + ConversationMetadata: + properties: + cachedAt: + oneOf: + - type: string + - type: 'null' + title: cachedat + type: object + title: conversationMetadata + description: Public metadata exposed on conversation responses. + ConversationMetadataNullable: + oneOf: + - $ref: '#/components/schemas/ConversationMetadata' + - type: 'null' + FeedbackResponse: + properties: + id: + type: string + title: id + agentId: + type: string + title: agentid + messageId: + type: string + title: messageid + vote: + type: integer + title: vote + tags: + items: + type: string + type: array + title: tags + notes: + oneOf: + - type: string + - type: 'null' + title: notes + model: + oneOf: + - type: string + - type: 'null' + title: model + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + type: object + required: + - id + - agentId + - messageId + - vote + - tags + - createdAt + - updatedAt + title: feedbackResponse + FeedbackUnion: + oneOf: + - items: + $ref: '#/components/schemas/FeedbackResponse' + type: array + - type: 'null' + ConversationBaseResponse: + properties: + id: + type: string + title: id + agentId: + type: string + title: agentid + title: + oneOf: + - type: string + - type: 'null' + title: title + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastActivityAt: + oneOf: + - type: string + - type: 'null' + title: lastactivityat + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + isFromDashboard: + type: boolean + title: isfromdashboard + default: false + messageCount: + type: integer + title: messagecount + default: 0 + totalInputTokens: + type: integer + title: totalinputtokens + default: 0 + totalOutputTokens: + type: integer + title: totaloutputtokens + default: 0 + totalTokens: + type: integer + title: totaltokens + default: 0 + conversationMetadata: + $ref: '#/components/schemas/ConversationMetadataNullable' + feedback: + $ref: '#/components/schemas/FeedbackUnion' + type: object + required: + - id + - agentId + - createdAt + - updatedAt + title: conversationBaseResponse + description: Lightweight response model without its messages. + PaginatedConversationsResponse: + properties: + data: + items: + $ref: '#/components/schemas/ConversationBaseResponse' + type: array + title: data + pagination: + $ref: '#/components/schemas/PaginationMetadata' + type: object + required: + - data + - pagination + title: paginatedConversationsResponse + MessageRole: + type: string + enum: + - user + - assistant + title: messageRole + description: Role of a message in the conversation. + TextPart: + properties: + type: + type: string + const: text + title: type + default: text + text: + type: string + title: text + type: object + required: + - text + title: textPart + ToolCallPart: + properties: + type: + type: string + const: tool-call + title: type + default: tool-call + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + args: + title: args + requiresApproval: + oneOf: + - type: boolean + - type: 'null' + title: requiresapproval + providerOptions: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: provideroptions + type: object + required: + - toolCallId + - toolName + - args + title: toolCallPart + ToolResultOutputType: + type: string + enum: + - text + - json + - error-text + - error-json + - content + title: toolResultOutputType + description: The valid 'type' of tool results. + ToolResultOutput: + properties: + type: + $ref: '#/components/schemas/ToolResultOutputType' + value: + title: value + type: object + required: + - type + - value + title: toolResultOutput + ToolResultPart-Output: + properties: + type: + type: string + const: tool-result + title: type + default: tool-result + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + output: + $ref: '#/components/schemas/ToolResultOutput' + providerOptions: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: provideroptions + type: object + required: + - toolCallId + - toolName + - output + title: toolResultPart + StartPart: + properties: + type: + type: string + const: start + title: type + default: start + type: object + title: startPart + StartStepPart: + properties: + type: + type: string + const: start-step + title: type + default: start-step + type: object + title: startStepPart + ReasoningPart: + properties: + type: + type: string + const: reasoning + title: type + default: reasoning + text: + type: string + title: text + type: object + required: + - text + title: reasoningPart + ToolApprovalRequestPart-Output: + properties: + type: + type: string + const: tool-approval-request + title: type + default: tool-approval-request + toolCallId: + type: string + title: toolcallid + toolName: + type: string + title: toolname + args: + title: args + description: + oneOf: + - type: string + - type: 'null' + title: description + providerOptions: + oneOf: + - type: object + additionalProperties: true + - type: 'null' + title: provideroptions + argsHash: + oneOf: + - type: string + - type: 'null' + title: argshash + type: object + required: + - toolCallId + - toolName + - args + title: toolApprovalRequestPart + MessagePartOutput: + oneOf: + - $ref: '#/components/schemas/TextPart' + - $ref: '#/components/schemas/ToolCallPart' + - $ref: '#/components/schemas/ToolResultPart-Output' + - $ref: '#/components/schemas/StartPart' + - $ref: '#/components/schemas/StartStepPart' + - $ref: '#/components/schemas/ReasoningPart' + - $ref: '#/components/schemas/ToolApprovalRequestPart-Output' + MessageResponse-Output: + properties: + id: + type: string + title: id + conversationId: + type: string + title: conversationid + role: + $ref: '#/components/schemas/MessageRole' + parts: + items: + $ref: '#/components/schemas/MessagePartOutput' + type: array + title: parts + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + model: + oneOf: + - type: string + - type: 'null' + title: model + inputTokens: + oneOf: + - type: integer + - type: 'null' + title: inputtokens + outputTokens: + oneOf: + - type: integer + - type: 'null' + title: outputtokens + type: object + required: + - id + - conversationId + - role + - parts + - createdAt + - updatedAt + title: messageResponse + description: Response model for a message. + ConversationFullResponse: + properties: + id: + type: string + title: id + agentId: + type: string + title: agentid + title: + oneOf: + - type: string + - type: 'null' + title: title + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastActivityAt: + oneOf: + - type: string + - type: 'null' + title: lastactivityat + userToken: + oneOf: + - type: string + - type: 'null' + title: usertoken + isFromDashboard: + type: boolean + title: isfromdashboard + default: false + messageCount: + type: integer + title: messagecount + default: 0 + totalInputTokens: + type: integer + title: totalinputtokens + default: 0 + totalOutputTokens: + type: integer + title: totaloutputtokens + default: 0 + totalTokens: + type: integer + title: totaltokens + default: 0 + conversationMetadata: + $ref: '#/components/schemas/ConversationMetadataNullable' + feedback: + $ref: '#/components/schemas/FeedbackUnion' + messages: + items: + $ref: '#/components/schemas/MessageResponse-Output' + type: array + title: messages + type: object + required: + - id + - agentId + - createdAt + - updatedAt + - messages + title: conversationFullResponse + description: Response model for a conversation with all its messages. + ApplicationConfigResponse: + properties: + maxRetentionDays: + type: integer + title: maxretentiondays + type: object + required: + - maxRetentionDays + title: applicationConfigResponse + ApplicationConfigPatch: + properties: + maxRetentionDays: + oneOf: + - type: integer + - type: 'null' + title: maxretentiondays + description: >- + Maximum number of days to retain data. Valid values: [0, 30, 60, + 90]. + default: 90 + type: object + title: applicationConfigPatch + VoteEnum: + type: integer + enum: + - 0 + - 1 + x-enum-varnames: + - downvote + - upvote + FeedbackCreationRequest: + properties: + messageId: + type: string + title: messageid + agentId: + type: string + title: agentid + vote: + $ref: '#/components/schemas/VoteEnum' + tags: + oneOf: + - items: + type: string + maxLength: 50 + type: array + maxItems: 10 + - type: 'null' + title: tags + notes: + oneOf: + - type: string + maxLength: 1000 + - type: 'null' + title: notes + type: object + required: + - messageId + - agentId + - vote + title: feedbackCreationRequest + description: Request model for creating a feedback entry. + OpenAIProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: openAIProviderInput + description: OpenAI-specific provider input. + AzureOpenAIProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + azureEndpoint: + type: string + title: azureendpoint + azureDeployment: + type: string + minLength: 1 + title: azuredeployment + description: Azure model deployment name is required. + apiVersion: + oneOf: + - type: string + - type: 'null' + title: apiversion + default: 2024-12-01-preview + type: object + required: + - apiKey + - azureEndpoint + - azureDeployment + title: azureOpenAIProviderInput + description: Azure OpenAI-specific provider input. + OpenAICompatibleProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + baseUrl: + type: string + title: baseurl + defaultModel: + type: string + minLength: 1 + title: defaultmodel + description: >- + Default model for this provider. Used for validation and as fallback + when no model is specified at agent level. + type: object + required: + - apiKey + - baseUrl + - defaultModel + title: openAICompatibleProviderInput + description: >- + OpenAI-compatible provider input. + + Contrary to the OpenAIProviderInput, the base_url is required. + + A model is required to verify connectivity and get saved as the default + model. + + This can later be changed at the Agent level. + BaseProviderInput: + properties: + apiKey: + type: string + title: apikey + type: object + required: + - apiKey + title: baseProviderInput + description: Base input that all providers must have. + AnthropicProviderInput-Output: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: anthropicProviderInput + description: Anthropic-specific provider input. + ProviderInputProviderAuthenticationResponse: + oneOf: + - $ref: '#/components/schemas/OpenAIProviderInput-Output' + - $ref: '#/components/schemas/AzureOpenAIProviderInput-Output' + - $ref: '#/components/schemas/OpenAICompatibleProviderInput-Output' + - $ref: '#/components/schemas/BaseProviderInput' + - $ref: '#/components/schemas/AnthropicProviderInput-Output' + ProviderAuthenticationResponse: + properties: + id: + type: string + title: id + name: + type: string + title: name + providerName: + type: string + title: providername + input: + $ref: '#/components/schemas/ProviderInputProviderAuthenticationResponse' + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastUsedAt: + oneOf: + - type: string + - type: 'null' + title: lastusedat + type: object + required: + - id + - name + - providerName + - input + - createdAt + - updatedAt + title: provider + PaginatedProviderAuthenticationsResponse: + properties: + data: + items: + $ref: '#/components/schemas/ProviderAuthenticationResponse' + type: array + title: data + pagination: + $ref: '#/components/schemas/PaginationMetadata' + type: object + required: + - data + - pagination + title: paginatedProviders + ProviderName: + type: string + enum: + - openai + - azure_openai + - google_genai + - deepseek + - openai_compatible + - anthropic + title: providerName + OpenAIProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + maxLength: 2083 + minLength: 1 + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: openAIProviderInput + description: OpenAI-specific provider input. + AzureOpenAIProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + azureEndpoint: + type: string + maxLength: 2083 + minLength: 1 + title: azureendpoint + azureDeployment: + type: string + minLength: 1 + title: azuredeployment + description: Azure model deployment name is required. + apiVersion: + oneOf: + - type: string + - type: 'null' + title: apiversion + default: 2024-12-01-preview + type: object + required: + - apiKey + - azureEndpoint + - azureDeployment + title: azureOpenAIProviderInput + description: Azure OpenAI-specific provider input. + OpenAICompatibleProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + baseUrl: + type: string + maxLength: 2083 + minLength: 1 + title: baseurl + defaultModel: + type: string + minLength: 1 + title: defaultmodel + description: >- + Default model for this provider. Used for validation and as fallback + when no model is specified at agent level. + type: object + required: + - apiKey + - baseUrl + - defaultModel + title: openAICompatibleProviderInput + description: >- + OpenAI-compatible provider input. + + Contrary to the OpenAIProviderInput, the base_url is required. + + A model is required to verify connectivity and get saved as the default + model. + + This can later be changed at the Agent level. + AnthropicProviderInput-Input: + properties: + apiKey: + type: string + title: apikey + baseUrl: + oneOf: + - type: string + maxLength: 2083 + minLength: 1 + - type: 'null' + title: baseurl + type: object + required: + - apiKey + title: anthropicProviderInput + description: Anthropic-specific provider input. + ProviderInput: + oneOf: + - $ref: '#/components/schemas/OpenAIProviderInput-Input' + - $ref: '#/components/schemas/AzureOpenAIProviderInput-Input' + - $ref: '#/components/schemas/OpenAICompatibleProviderInput-Input' + - $ref: '#/components/schemas/BaseProviderInput' + - $ref: '#/components/schemas/AnthropicProviderInput-Input' + ProviderAuthenticationCreate: + properties: + name: + type: string + maxLength: 128 + minLength: 1 + title: name + providerName: + $ref: '#/components/schemas/ProviderName' + input: + $ref: '#/components/schemas/ProviderInput' + additionalProperties: false + type: object + required: + - name + - providerName + - input + title: providerAuthenticationCreate + ProviderInputNullable: + oneOf: + - $ref: '#/components/schemas/OpenAIProviderInput-Input' + - $ref: '#/components/schemas/AzureOpenAIProviderInput-Input' + - $ref: '#/components/schemas/OpenAICompatibleProviderInput-Input' + - $ref: '#/components/schemas/BaseProviderInput' + - $ref: '#/components/schemas/AnthropicProviderInput-Input' + - type: 'null' + ProviderAuthenticationPatch: + properties: + name: + oneOf: + - type: string + maxLength: 128 + minLength: 1 + - type: 'null' + title: name + input: + $ref: '#/components/schemas/ProviderInputNullable' + additionalProperties: false + type: object + title: providerPatch + SecretKeyResponse: + properties: + id: + type: string + title: id + name: + type: string + title: name + value: + type: string + title: value + createdAt: + type: string + title: createdat + updatedAt: + type: string + title: updatedat + lastUsedAt: + oneOf: + - type: string + - type: 'null' + title: lastusedat + isDefault: + type: boolean + title: isdefault + default: false + agentIds: + items: + type: string + type: array + title: agentids + type: object + required: + - id + - name + - value + - createdAt + - updatedAt + - lastUsedAt + - agentIds + title: secretKeyResponse + PaginatedSecretKeysResponse: + properties: + data: + items: + $ref: '#/components/schemas/SecretKeyResponse' + type: array + title: data + pagination: + $ref: '#/components/schemas/PaginationMetadata' + type: object + required: + - data + - pagination + title: paginatedSecretKeysResponse + SecretKeyCreate: + properties: + name: + type: string + maxLength: 128 + minLength: 1 + title: name + description: The name of the secret key. + agentIds: + items: + type: string + type: array + title: agentids + description: List of agent IDs this secret key is associated with. + type: object + required: + - name + title: secretKeyCreate + description: Secret key creation payload. + SecretKeyPatch: + properties: + name: + oneOf: + - type: string + maxLength: 128 + minLength: 1 + - type: 'null' + title: name + description: The new name of the secret key. + agentIds: + oneOf: + - items: + type: string + type: array + - type: 'null' + title: agentids + description: Updated list of agent IDs this secret key is associated with. + type: object + title: secretKeyPatch + description: Secret key patch payload. + MemoryType: + type: string + enum: + - semantic + - episodic + title: memoryType + description: >- + Memory types implemented so far. + + Follows LangMem's ontology: + https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#memory-types. + Episode: + properties: + observation: + type: string + maxLength: 5000 + minLength: 1 + title: observation + description: >- + What user wanted + key context (1-2 sentences). Include prior failed + attempts if they informed the approach. + thoughts: + type: string + maxLength: 5000 + minLength: 1 + title: thoughts + description: >- + WHY this approach was chosen, which constraints/preferences drove + decisions (1-3 sentences). Capture reasoning that applies to similar + future scenarios. + action: + type: string + maxLength: 5000 + minLength: 1 + title: action + description: >- + What was done with PRECISE details (1-3 sentences). WITH tool calls: + use arrow notation `tool(param:value) → feedback → + tool(refined_param:new_value)`. WITHOUT tool calls: capture + communication/workflow pattern. + result: + type: string + maxLength: 5000 + minLength: 1 + title: result + description: >- + Learned pattern + effectiveness (1-3 sentences). What worked and WHY + it's replicable. Note efficiency: multi-turn refinements, which + results were relevant, what made final attempt succeed. Use strict + `param:value` syntax for learnings. Format: 'For [context], use + [param:value] because [reason]'. + type: object + required: + - observation + - thoughts + - action + - result + title: episode + description: >- + Episodic memory schema following LangMem's OTAR pattern: + + Observation → Thoughts → Action → Result + + + Captures complete interaction experiences for agent learning. + + See + https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#episodic-memory-past-experiences. + EpisodeNullable: + oneOf: + - $ref: '#/components/schemas/Episode' + - type: 'null' + MemoryRecord: + properties: + memoryType: + $ref: '#/components/schemas/MemoryType' + episode: + $ref: '#/components/schemas/EpisodeNullable' + text: + type: string + maxLength: 2000 + minLength: 1 + title: text + description: Self-contained, first-person memory for long-term recall. + rawExtract: + type: string + maxLength: 5000 + minLength: 1 + title: rawextract + description: Verbatim conversation extract, not paraphrased. + keywords: + items: + type: string + type: array + title: keywords + description: >- + 5-20 free-form keywords: entities, context, search terms (any + words). + topics: + items: + type: string + type: array + title: topics + description: >- + 2-4 topics ONLY from this list: [complaints, entertainment, family, + feedback, finance, food, goals, health, history, hobbies, learning, + praise, preferences, schedule, shopping, technical, travel, work]. + _tags: + items: + type: string + type: array + title: tags + description: >- + Arbitrary labels/themes for flexible categorization (e.g., + 'Q1-goals', 'paris-trip', 'vip-customer'). + recallTriggers: + items: + type: string + type: array + title: recalltriggers + description: 3-5 natural phrases that should trigger this memory. + objectID: + oneOf: + - type: string + - type: 'null' + title: objectid + description: ObjectID of existing memory to update. Leave empty for new memory. + appId: + type: string + title: appid + description: Application ID. + default: '' + agentIDs: + items: + type: string + type: array + title: agentids + description: >- + Agent IDs with access: ['agent1'], ['*'] for all, ['*', '-agent1'] + to exclude. + userID: + type: string + title: userid + description: User ID. + default: '' + createdAt: + type: integer + title: createdat + description: Epoch seconds. + default: 0 + updatedAt: + type: integer + title: updatedat + description: Epoch seconds. + default: 0 + type: object + required: + - text + - rawExtract + title: memoryRecord + description: >- + Universal storage model for all memory types (semantic, episodic). + + + This is the ONLY model that touches storage (Algolia). Domain models + (SemanticMemory, EpisodicMemory) + + are used for LLM extraction and converted to MemoryRecord before saving. + + + See + https://langchain-ai.github.io/langmem/concepts/conceptual_guide/#memory-types + for memory type definitions. + UserDataResponse: + properties: + conversations: + items: + $ref: '#/components/schemas/ConversationFullResponse' + type: array + title: conversations + memories: + items: + $ref: '#/components/schemas/MemoryRecord' + type: array + title: memories + type: object + required: + - conversations + - memories + title: userDataResponse + ErrorBase: + description: Error. + type: object + x-keep-model: true + additionalProperties: true + properties: + message: + type: string + example: Invalid Application-Id or API-Key + parameters: + PathInPath: + name: path + in: path + description: Path of the endpoint, for example `1/newFeature`. + required: true + schema: + type: string + example: /keys + Parameters: + name: parameters + in: query + description: Query parameters to apply to the current query. + schema: + type: object + additionalProperties: true + responses: + BadRequest: + description: Bad request or request arguments. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBase' + FeatureNotEnabled: + description: This feature is not enabled on your Algolia account. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBase' + MethodNotAllowed: + description: Method not allowed with this API key. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBase' + IndexNotFound: + description: Index not found. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorBase' +x-timeouts: + browser: + connect: 25000 + read: 25000 + write: 25000 + server: + connect: 25000 + read: 25000 + write: 25000 diff --git a/specs/major-breaking-changes-rename.json b/specs/major-breaking-changes-rename.json index 015b71b6cab..4d5137a4da7 100644 --- a/specs/major-breaking-changes-rename.json +++ b/specs/major-breaking-changes-rename.json @@ -1,5 +1,6 @@ { "abtesting": {}, + "agent-studio": {}, "analytics": {}, "ingestion": {}, "insights": {}, diff --git a/templates/Bug_report.yml b/templates/Bug_report.yml index d01e1b39053..a97c314ede3 100644 --- a/templates/Bug_report.yml +++ b/templates/Bug_report.yml @@ -31,6 +31,7 @@ body: options: - All - AB testing + - Agent Studio - Analytics - Ingestion - Insights diff --git a/templates/csharp/api.mustache b/templates/csharp/api.mustache index 09898385916..2bd1a3cca48 100644 --- a/templates/csharp/api.mustache +++ b/templates/csharp/api.mustache @@ -331,7 +331,7 @@ namespace Algolia.Search.Clients; requestOptions.ConnectTimeout ??= TimeSpan.FromMilliseconds({{connect}}); {{/vendorExtensions.x-timeouts}} {{/vendorExtensions}} - {{#returnType}}return {{/returnType}}await _transport.ExecuteRequestAsync{{#returnType}}<{{> return_type}}>{{/returnType}}(new HttpMethod("{{httpMethod}}"),"{{{path}}}", requestOptions, cancellationToken).ConfigureAwait(false); + {{#returnType}}return {{/returnType}}await _transport.ExecuteRequestAsync{{#returnType}}<{{> return_type}}>{{/returnType}}(new HttpMethod("{{httpMethod}}"),"{{{serverPathSuffix}}}{{{path}}}", requestOptions, cancellationToken).ConfigureAwait(false); } @@ -424,7 +424,7 @@ namespace Algolia.Search.Clients; requestOptions.ConnectTimeout ??= TimeSpan.FromMilliseconds({{connect}}); {{/vendorExtensions.x-timeouts}} {{/vendorExtensions}} - return await _transport.ExecuteRequestAsync(new HttpMethod("{{httpMethod}}"),"{{{path}}}", requestOptions, cancellationToken).ConfigureAwait(false); + return await _transport.ExecuteRequestAsync(new HttpMethod("{{httpMethod}}"),"{{{serverPathSuffix}}}{{{path}}}", requestOptions, cancellationToken).ConfigureAwait(false); } /// diff --git a/templates/csharp/modelEnum.mustache b/templates/csharp/modelEnum.mustache index 71cb02ae199..49962a8a318 100644 --- a/templates/csharp/modelEnum.mustache +++ b/templates/csharp/modelEnum.mustache @@ -4,8 +4,8 @@ {{#description}} /// {{{.}}} {{/description}} - [JsonConverter(typeof(Serializer.JsonStringEnumConverter<{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}>))] - {{> visibility}} enum {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} + {{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}[JsonConverter(typeof(Serializer.JsonStringEnumConverter<{{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}>))] + {{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}}{{> visibility}} enum {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}} { {{#allowableValues}} {{#enumVars}} diff --git a/templates/csharp/modelOneOf.mustache b/templates/csharp/modelOneOf.mustache index 5476baf07d9..360abf176c3 100644 --- a/templates/csharp/modelOneOf.mustache +++ b/templates/csharp/modelOneOf.mustache @@ -214,6 +214,18 @@ {{#vendorExtensions}} var jsonDocument = JsonDocument.ParseValue(ref reader); var root = jsonDocument.RootElement; + {{#discriminator}} + if (root.TryGetProperty("{{{propertyBaseName}}}", out JsonElement discriminatorElement)) + { + string discriminatorValue = discriminatorElement.GetString(); + {{#mappedModels}} + if (discriminatorValue == "{{{mappingName}}}") + { + return new {{classname}}{{#x-has-child-generic}}{{/x-has-child-generic}}(jsonDocument.Deserialize<{{{modelName}}}>(JsonConfig.Options)); + } + {{/mappedModels}} + } + {{/discriminator}} {{#composedSchemas.oneOf}} if (root.ValueKind == JsonValueKind.{{#isModel}}Object{{#vendorExtensions.x-discriminator-fields}} && root.TryGetProperty("{{{.}}}", out _){{/vendorExtensions.x-discriminator-fields}}{{/isModel}}{{#isEnumRef}}String{{/isEnumRef}}{{#isArray}}Array{{/isArray}}{{#isInteger}}Number{{/isInteger}}{{#isLong}}Number{{/isLong}}{{#isDouble}}Number{{/isDouble}}{{#isBoolean}}True || root.ValueKind == JsonValueKind.False{{/isBoolean}}{{#isString}}String{{/isString}}{{^isEnumRef}}{{^isModel}}{{^isArray}}{{^isInteger}}{{^isLong}}{{^isDouble}}{{^isBoolean}}{{^isString}}Object{{/isString}}{{/isBoolean}}{{/isDouble}}{{/isLong}}{{/isInteger}}{{/isArray}}{{/isModel}}{{/isEnumRef}}) { diff --git a/templates/csharp/tests/e2e/e2e.mustache b/templates/csharp/tests/e2e/e2e.mustache index 1b443e5a7ca..f1638ddd362 100644 --- a/templates/csharp/tests/e2e/e2e.mustache +++ b/templates/csharp/tests/e2e/e2e.mustache @@ -6,7 +6,6 @@ using Algolia.Search.Serializer; using Algolia.Search.Tests.Utils; using Xunit; using System.Text.Json; -using Quibble.Xunit; using dotenv.net; {{#isSearchClient}} using Action = Algolia.Search.Models.Search.Action; @@ -50,6 +49,7 @@ public class {{client}}RequestTestsE2E } + {{#blocksE2E}} {{#tests}} [Fact(DisplayName = "{{{testName}}}")] @@ -64,7 +64,7 @@ public class {{client}}RequestTestsE2E {{/statusCode}} {{#body}} - JsonAssert.EqualOverrideDefault("{{#lambda.escapeQuotes}}{{{.}}}{{/lambda.escapeQuotes}}", JsonSerializer.Serialize(resp, JsonConfig.Options), new JsonDiffConfig(true)); + TestHelpers.LenientJsonAssert("{{#lambda.escapeQuotes}}{{{.}}}{{/lambda.escapeQuotes}}", JsonSerializer.Serialize(resp, JsonConfig.Options)); {{/body}} } catch (Exception e) { diff --git a/templates/csharp/tests/requests/requests.mustache b/templates/csharp/tests/requests/requests.mustache index 91e63412a7a..22ff2ca68d9 100644 --- a/templates/csharp/tests/requests/requests.mustache +++ b/templates/csharp/tests/requests/requests.mustache @@ -47,7 +47,7 @@ private readonly {{client}} client; var req = _echo.LastResponse; {{#request}} - Assert.Equal("{{{path}}}",req.Path); + Assert.Equal("{{{serverPathSuffix}}}{{{path}}}",req.Path); Assert.Equal("{{{method}}}",req.Method.ToString()); {{#body}} JsonAssert.EqualOverrideDefault("{{#lambda.escapeJSON}}{{{body}}}{{/lambda.escapeJSON}}", req.Body, new JsonDiffConfig(false)); diff --git a/templates/dart/api.mustache b/templates/dart/api.mustache index ca7d956bae6..59436a553d9 100644 --- a/templates/dart/api.mustache +++ b/templates/dart/api.mustache @@ -123,10 +123,10 @@ final class {{classname}} implements ApiClient { final request = ApiRequest( method: RequestMethod.{{#lambda.snakecase}}{{httpMethod}}{{/lambda.snakecase}}, {{^vendorExtensions.x-is-custom-request}} - path: r'{{{path}}}'{{#pathParams}}.replaceAll('{' r'{{{baseName}}}' '}', Uri.encodeComponent({{{paramName}}}.toString())){{/pathParams}}, + path: r'{{{serverPathSuffix}}}{{{path}}}'{{#pathParams}}.replaceAll('{' r'{{{baseName}}}' '}', Uri.encodeComponent({{{paramName}}}.toString())){{/pathParams}}, {{/vendorExtensions.x-is-custom-request}} {{#vendorExtensions.x-is-custom-request}} - path: r'{{{path}}}'{{#pathParams}}.replaceAll('{' r'{{{baseName}}}' '}', {{{paramName}}}){{/pathParams}}, + path: r'{{{serverPathSuffix}}}{{{path}}}'{{#pathParams}}.replaceAll('{' r'{{{baseName}}}' '}', {{{paramName}}}){{/pathParams}}, {{/vendorExtensions.x-is-custom-request}} {{#vendorExtensions.x-use-read-transporter}} isRead : {{{vendorExtensions.x-use-read-transporter}}}, @@ -175,6 +175,9 @@ final class {{classname}} implements ApiClient { ) + {{/vendorExtensions.x-timeouts}}requestOptions, ); {{#returnType}} + {{#vendorExtensions.x-is-custom-request}} + if (response == null) return AlgoliaNoResponse(); + {{/vendorExtensions.x-is-custom-request}} return deserialize<{{{.}}}, {{{returnBaseType}}}>(response, '{{{.}}}', growable: true,); {{/returnType}} } diff --git a/templates/dart/tests/client/method.mustache b/templates/dart/tests/client/method.mustache index 23e87357c92..06ef872cc7b 100644 --- a/templates/dart/tests/client/method.mustache +++ b/templates/dart/tests/client/method.mustache @@ -9,7 +9,12 @@ try { expectBody(res, """{{{match.value}}}"""); {{/match.isPrimitive}} {{#match.isPrimitive}} + {{#match.isNull}} + expect(res, isA()); + {{/match.isNull}} + {{^match.isNull}} expect(res, {{#match}}{{> tests/param_value}}{{/match}}); + {{/match.isNull}} {{/match.isPrimitive}} {{/testResponse}} } on InterceptionException catch (_) { diff --git a/templates/dart/tests/requests/requests.mustache b/templates/dart/tests/requests/requests.mustache index c413b47865d..ebdfe83c5be 100644 --- a/templates/dart/tests/requests/requests.mustache +++ b/templates/dart/tests/requests/requests.mustache @@ -22,7 +22,7 @@ void main() { call: (client) => {{> tests/method}}, intercept: (request) { {{#request}} - expectPath(request.path, '{{{path}}}'); + expectPath(request.path, '{{{serverPathSuffix}}}{{{path}}}'); expect(request.method, '{{#lambda.lowercase}}{{&method}}{{/lambda.lowercase}}'); {{#headers}} expectHeaders(request.headers, """{{{.}}}"""); diff --git a/templates/go/api.mustache b/templates/go/api.mustache index 6c6371a4cb2..cbdbe48b5ec 100644 --- a/templates/go/api.mustache +++ b/templates/go/api.mustache @@ -386,11 +386,11 @@ func (c *APIClient) NewApi{{{nickname}}}Request({{#requiredParams}} {{paramName} {{#allParams}} {{^required}} -// With{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}} adds the {{paramName}} to the {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request and returns the request for chaining. +// With{{#nameInPascalCase}}{{nameInPascalCase}}{{/nameInPascalCase}}{{^nameInPascalCase}}{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}}{{/nameInPascalCase}} adds the {{paramName}} to the {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request and returns the request for chaining. {{#isDeprecated}} // Deprecated {{/isDeprecated}} -func (r {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request) With{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}}({{paramName}} {{^isFreeFormObject}}{{^isArray}}{{^isMap}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isMap}}{{/isArray}}{{/isFreeFormObject}}{{{dataType}}}) {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request { +func (r {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request) With{{#nameInPascalCase}}{{nameInPascalCase}}{{/nameInPascalCase}}{{^nameInPascalCase}}{{#lambda.titlecase}}{{baseName}}{{/lambda.titlecase}}{{/nameInPascalCase}}({{paramName}} {{^isFreeFormObject}}{{^isArray}}{{^isMap}}{{^isPrimitiveType}}{{^isEnumRef}}*{{/isEnumRef}}{{/isPrimitiveType}}{{/isMap}}{{/isArray}}{{/isFreeFormObject}}{{{dataType}}}) {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request { r.{{paramName}} = {{#isPrimitiveType}}{{^isMap}}&{{/isMap}}{{/isPrimitiveType}}{{paramName}} return r } @@ -428,7 +428,7 @@ func (r {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/s */ func (c *APIClient) {{nickname}}WithHTTPInfo({{#hasParams}}r {{#structPrefix}}{{&classname}}{{/structPrefix}}{{^structPrefix}}Api{{/structPrefix}}{{operationId}}Request,{{/hasParams}} opts ...RequestOption) (*http.Response, []byte, error) { {{#vendorExtensions}} - requestPath := "{{{path}}}"{{#pathParams}} + requestPath := "{{{serverPathSuffix}}}{{{path}}}"{{#pathParams}} requestPath = strings.ReplaceAll(requestPath, {{=<% %>=}}"{<%baseName%>}"<%={{ }}=%>, {{#x-is-custom-request}}utils.ParameterToString(r.{{paramName}}){{/x-is-custom-request}}{{^x-is-custom-request}}url.PathEscape(utils.ParameterToString(r.{{paramName}})){{/x-is-custom-request}}){{/pathParams}} {{/vendorExtensions}} diff --git a/templates/go/model_oneof.mustache b/templates/go/model_oneof.mustache index 6d95ebae052..25755a010b4 100644 --- a/templates/go/model_oneof.mustache +++ b/templates/go/model_oneof.mustache @@ -25,11 +25,26 @@ func (dst *{{classname}}) UnmarshalJSON(data []byte) error { } {{/isNullable}} + {{#discriminator}} + var jsonDict map[string]any + _ = json.Unmarshal(data, &jsonDict) + if typeValue, ok := jsonDict["{{{propertyBaseName}}}"]; ok { + switch typeValue { + {{#mappedModels}} + case "{{{mappingName}}}": + err = json.Unmarshal(data, &dst.{{#lambda.type-to-name}}{{{modelName}}}{{/lambda.type-to-name}}) + if err == nil { return nil } + dst.{{#lambda.type-to-name}}{{{modelName}}}{{/lambda.type-to-name}} = nil + {{/mappedModels}} + } + } + {{/discriminator}} + {{^discriminator}} {{#vendorExtensions.x-has-discriminator}} - // use discriminator value to speed up the lookup if possible, if not we will try every possibility var jsonDict map[string]any _ = json.Unmarshal(data, &jsonDict) {{/vendorExtensions.x-has-discriminator}} + {{/discriminator}} {{#composedSchemas.oneOf}} {{#vendorExtensions.x-discriminator-fields.size}} if {{#vendorExtensions.x-discriminator-fields}}utils.HasKey(jsonDict, "{{.}}"){{^-last}} && {{/-last}}{{/vendorExtensions.x-discriminator-fields}} { diff --git a/templates/go/search_helpers.mustache b/templates/go/search_helpers.mustache index a297bec029f..7baf14198e4 100644 --- a/templates/go/search_helpers.mustache +++ b/templates/go/search_helpers.mustache @@ -802,7 +802,12 @@ func (c *APIClient) SaveObjectsWithTransformation(indexName string, objects []ma return nil, reportError("`region` must be provided at client instantiation before calling this method.") } - return c.ingestionTransporter.ChunkedPush(indexName, objects, ingestion.Action(ACTION_ADD_OBJECT), nil, toIngestionChunkedBatchOptions(opts)...) //nolint:wrapcheck + return c.ingestionTransporter.ChunkedPush( + indexName, + objects, + ingestion.Action(ACTION_ADD_OBJECT), + nil, + toIngestionChunkedBatchOptions(opts)...) } /* @@ -836,5 +841,10 @@ func (c *APIClient) PartialUpdateObjectsWithTransformation(indexName string, obj action = ACTION_PARTIAL_UPDATE_OBJECT_NO_CREATE } - return c.ingestionTransporter.ChunkedPush(indexName, objects, ingestion.Action(action), nil, toIngestionChunkedBatchOptions(partialUpdateObjectsToChunkedBatchOptions(opts))...) //nolint:wrapcheck -} \ No newline at end of file + return c.ingestionTransporter.ChunkedPush( + indexName, + objects, + ingestion.Action(action), + nil, + toIngestionChunkedBatchOptions(partialUpdateObjectsToChunkedBatchOptions(opts))...) +} diff --git a/templates/go/tests/arrayType.mustache b/templates/go/tests/arrayType.mustache index 9a6da90dad3..68eecc6e356 100644 --- a/templates/go/tests/arrayType.mustache +++ b/templates/go/tests/arrayType.mustache @@ -1 +1 @@ -{{#value.0}}[]{{#isEnum}}{{clientPrefix}}.{{/isEnum}}{{#oneOfModel}}{{^isEnum}}{{clientPrefix}}.{{/isEnum}}{{parentClassName}}{{/oneOfModel}}{{^oneOfModel}}{{#isObject}}{{clientPrefix}}.{{/isObject}}{{#isArray}}{{> tests/arrayType}}{{/isArray}}{{^isArray}}{{objectName}}{{/isArray}}{{/oneOfModel}}{{/value.0}}{{^value.0}}nil{{/value.0}} \ No newline at end of file +{{#value.0}}[]{{#isEnum}}{{clientPrefix}}.{{/isEnum}}{{#oneOfModel}}{{^isEnum}}{{clientPrefix}}.{{/isEnum}}{{parentClassName}}{{/oneOfModel}}{{^oneOfModel}}{{#isObject}}{{clientPrefix}}.{{/isObject}}{{#isArray}}{{> tests/arrayType}}{{/isArray}}{{^isArray}}{{#isFreeFormObject}}map[string]any{{/isFreeFormObject}}{{^isFreeFormObject}}{{objectName}}{{/isFreeFormObject}}{{/isArray}}{{/oneOfModel}}{{/value.0}}{{^value.0}}nil{{/value.0}} \ No newline at end of file diff --git a/templates/go/tests/generateInnerParams.mustache b/templates/go/tests/generateInnerParams.mustache index 7a4ff09f866..c68b3336e00 100644 --- a/templates/go/tests/generateInnerParams.mustache +++ b/templates/go/tests/generateInnerParams.mustache @@ -1,3 +1,3 @@ -{{#isVerbatim}}{{{value}}}{{/isVerbatim}}{{#isHelper}}{{#goFunctionalParam}}{{clientPrefix}}.With{{#lambda.pascalcase}}{{key}}{{/lambda.pascalcase}}({{/goFunctionalParam}}{{/isHelper}}{{#isNull}}{{#inClientTest}}tests.ZeroValue[{{#isNullObject}}*{{clientPrefix}}.{{/isNullObject}}{{objectName}}](){{/inClientTest}}{{^inClientTest}}nil{{/inClientTest}}{{/isNull}}{{#isString}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}"{{/isString}}{{#isInteger}}{{{value}}}{{/isInteger}}{{#isLong}}{{{value}}}{{/isLong}}{{#isDouble}}{{{value}}}{{/isDouble}}{{#isBoolean}}{{{value}}}{{/isBoolean}}{{#isEnum}}{{clientPrefix}}.{{objectName}}("{{{value}}}"){{/isEnum}}{{#isArray}} +{{#isVerbatim}}{{{value}}}{{/isVerbatim}}{{#isHelper}}{{#goFunctionalParam}}{{clientPrefix}}.With{{#lambda.pascalcase}}{{key}}{{/lambda.pascalcase}}({{/goFunctionalParam}}{{/isHelper}}{{#isNull}}{{#inClientTest}}tests.ZeroValue[{{#isNullObject}}*{{clientPrefix}}.{{/isNullObject}}{{objectName}}](){{/inClientTest}}{{^inClientTest}}nil{{/inClientTest}}{{/isNull}}{{#isString}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}"{{/isString}}{{#isInteger}}{{{value}}}{{/isInteger}}{{#isLong}}{{{value}}}{{/isLong}}{{#isDouble}}{{{value}}}{{/isDouble}}{{#isBoolean}}{{{value}}}{{/isBoolean}}{{#isEnum}}{{clientPrefix}}.{{objectName}}({{^isIntegerEnum}}"{{{value}}}"{{/isIntegerEnum}}{{#isIntegerEnum}}{{{value}}}{{/isIntegerEnum}}){{/isEnum}}{{#isArray}} {{> tests/arrayType}}{{^value.isEmpty}}{ {{#value}}{{#isObject}}*{{/isObject}}{{#oneOfModel}}{{^isObject}}*{{/isObject}}{{/oneOfModel}}{{> tests/generateParams}},{{/value}} }{{/value.isEmpty}}{{/isArray}}{{#isObject}} {{clientPrefix}}.NewEmpty{{objectName}}(){{#value}}{{#isAdditionalProperty}}.SetAdditionalProperty("{{{key}}}", {{> tests/generateParams}}){{/isAdditionalProperty}}{{^isAdditionalProperty}}.Set{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{/isAdditionalProperty}}{{/value}}{{/isObject}}{{#isFreeFormObject}}{{#isAnyType}}map[string]any{ {{#value}}{{#entrySet}}"{{{key}}}": "{{{value}}}",{{/entrySet}}{{/value}} }{{/isAnyType}}{{^isAnyType}}{{> tests/mapType}}{ {{#value}}"{{{key}}}": {{#oneOfModel}}{{^isObject}}*{{/isObject}}{{/oneOfModel}}{{#isObject}}*{{/isObject}}{{> tests/generateParams}},{{/value}} }{{/isAnyType}}{{/isFreeFormObject}}{{#isHelper}}{{#goFunctionalParam}}){{/goFunctionalParam}}{{/isHelper}} \ No newline at end of file diff --git a/templates/go/tests/method.mustache b/templates/go/tests/method.mustache index d2819805aef..2231a58f529 100644 --- a/templates/go/tests/method.mustache +++ b/templates/go/tests/method.mustache @@ -1,2 +1,2 @@ client.{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}({{#hasParams}}{{^isHelper}}client.NewApi{{#lambda.titlecase}}{{method}}{{/lambda.titlecase}}Request({{/isHelper}} - {{#parametersWithDataType}}{{#required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}} {{^isHelper}}){{#parametersWithDataType}}{{^required}}.With{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{#-last}},{{/-last}}{{/required}}{{/parametersWithDataType}}{{/isHelper}}{{#isHelper}}{{#parametersWithDataType}}{{^required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}}{{/isHelper}}{{/hasParams}}{{#requestOptions}}{{#queryParameters.parametersWithDataType}}{{clientPrefix}}.WithQueryParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/queryParameters.parametersWithDataType}}{{#headers.parametersWithDataType}}{{clientPrefix}}.WithHeaderParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/headers.parametersWithDataType}} {{#timeouts.read}} ,{{clientPrefix}}.WithReadTimeout({{.}} * time.Millisecond), {{/timeouts.read}} {{#timeouts.write}} ,{{clientPrefix}}.WithWriteTimeout({{.}} * time.Millisecond), {{/timeouts.write}} {{#timeouts.connect}} ,{{clientPrefix}}.WithConnectTimeout({{.}} * time.Millisecond), {{/timeouts.connect}} {{/requestOptions}}) \ No newline at end of file + {{#parametersWithDataType}}{{#required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}} {{^isHelper}}){{#parametersWithDataType}}{{^required}}{{^isNull}}.With{{#lambda.pascalcase}}{{{key}}}{{/lambda.pascalcase}}({{> tests/generateParams}}){{/isNull}}{{#-last}},{{/-last}}{{/required}}{{/parametersWithDataType}}{{/isHelper}}{{#isHelper}}{{#parametersWithDataType}}{{^required}}{{> tests/generateParams}},{{/required}}{{/parametersWithDataType}}{{/isHelper}}{{/hasParams}}{{#requestOptions}}{{#queryParameters.parametersWithDataType}}{{clientPrefix}}.WithQueryParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/queryParameters.parametersWithDataType}}{{#headers.parametersWithDataType}}{{clientPrefix}}.WithHeaderParam("{{{key}}}", {{> tests/generateInnerParams}}),{{/headers.parametersWithDataType}} {{#timeouts.read}} ,{{clientPrefix}}.WithReadTimeout({{.}} * time.Millisecond), {{/timeouts.read}} {{#timeouts.write}} ,{{clientPrefix}}.WithWriteTimeout({{.}} * time.Millisecond), {{/timeouts.write}} {{#timeouts.connect}} ,{{clientPrefix}}.WithConnectTimeout({{.}} * time.Millisecond), {{/timeouts.connect}} {{/requestOptions}}) \ No newline at end of file diff --git a/templates/go/tests/requests/requests.mustache b/templates/go/tests/requests/requests.mustache index 85d59bd274f..165a5fe22fc 100644 --- a/templates/go/tests/requests/requests.mustache +++ b/templates/go/tests/requests/requests.mustache @@ -47,7 +47,7 @@ func Test{{#lambda.titlecase}}{{clientPrefix}}{{/lambda.titlecase}}_{{#lambda.ti require.NoError(t, err) {{#request}} - require.Equal(t, "{{path}}", echo.Path) + require.Equal(t, "{{{serverPathSuffix}}}{{path}}", echo.Path) require.Equal(t, "{{{method}}}", echo.Method) {{#body}} diff --git a/templates/java/api.mustache b/templates/java/api.mustache index 9607df885bb..259eaf10cd9 100644 --- a/templates/java/api.mustache +++ b/templates/java/api.mustache @@ -325,7 +325,7 @@ public class {{classname}} extends ApiClient { {{/required}}{{/allParams}} HttpRequest request = HttpRequest.builder() - {{#vendorExtensions}}{{#x-is-custom-request}}.setPathEncoded("{{{path}}}", path){{/x-is-custom-request}}{{^x-is-custom-request}}.setPath("{{{path}}}"{{#pathParams}}, {{{paramName}}}{{/pathParams}}){{/x-is-custom-request}}{{/vendorExtensions}} + {{#vendorExtensions}}{{#x-is-custom-request}}.setPathEncoded("{{{serverPathSuffix}}}{{{path}}}", path){{/x-is-custom-request}}{{^x-is-custom-request}}.setPath("{{{serverPathSuffix}}}{{{path}}}"{{#pathParams}}, {{{paramName}}}{{/pathParams}}){{/x-is-custom-request}}{{/vendorExtensions}} .setMethod("{{httpMethod}}") {{#bodyParam}}.setBody({{paramName}}){{/bodyParam}} {{#vendorExtensions.x-use-read-transporter}}.setRead(true){{/vendorExtensions.x-use-read-transporter}} @@ -351,7 +351,7 @@ public class {{classname}} extends ApiClient { {{/required}}{{/allParams}} HttpRequest request = HttpRequest.builder() - {{#vendorExtensions}}{{#x-is-custom-request}}.setPathEncoded("{{{path}}}", path){{/x-is-custom-request}}{{^x-is-custom-request}}.setPath("{{{path}}}"{{#pathParams}}, {{{paramName}}}{{/pathParams}}){{/x-is-custom-request}}{{/vendorExtensions}} + {{#vendorExtensions}}{{#x-is-custom-request}}.setPathEncoded("{{{serverPathSuffix}}}{{{path}}}", path){{/x-is-custom-request}}{{^x-is-custom-request}}.setPath("{{{serverPathSuffix}}}{{{path}}}"{{#pathParams}}, {{{paramName}}}{{/pathParams}}){{/x-is-custom-request}}{{/vendorExtensions}} .setMethod("{{httpMethod}}") {{#bodyParam}}.setBody({{paramName}}){{/bodyParam}} {{#vendorExtensions.x-use-read-transporter}}.setRead(true){{/vendorExtensions.x-use-read-transporter}} diff --git a/templates/java/oneof_interface.mustache b/templates/java/oneof_interface.mustache index 751765c1a22..1f153023494 100644 --- a/templates/java/oneof_interface.mustache +++ b/templates/java/oneof_interface.mustache @@ -112,6 +112,19 @@ public interface {{classname}}{{#vendorExtensions.x-has-child-generic}}{{/ven @Override public {{classname}}{{#vendorExtensions.x-has-child-generic}}{{/vendorExtensions.x-has-child-generic}} deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { JsonNode tree = jp.readValueAsTree(); + {{#discriminator}} + JsonNode discriminatorNode = tree.get("{{{propertyBaseName}}}"); + if (discriminatorNode != null && discriminatorNode.isTextual()) { + String discriminatorValue = discriminatorNode.asText(); + {{#mappedModels}} + if ("{{{mappingName}}}".equals(discriminatorValue)) { + try(JsonParser parser = tree.traverse(jp.getCodec())) { + return parser.readValueAs({{{modelName}}}.class); + } + } + {{/mappedModels}} + } + {{/discriminator}} {{#composedSchemas.oneOf}} // deserialize {{{datatypeWithEnum}}} if (tree.{{#isModel}}isObject(){{#vendorExtensions.x-discriminator-fields}} && tree.has("{{{.}}}"){{/vendorExtensions.x-discriminator-fields}}{{/isModel}}{{#isEnumRef}}isTextual(){{/isEnumRef}}{{#isArray}}isArray(){{/isArray}}{{#isInteger}}isInt(){{/isInteger}}{{#isLong}}isLong(){{/isLong}}{{#isDouble}}isDouble(){{/isDouble}}{{#isBoolean}}isBoolean(){{/isBoolean}}{{#isString}}isTextual(){{/isString}}{{^isEnumRef}}{{^isModel}}{{^isArray}}{{^isInteger}}{{^isLong}}{{^isDouble}}{{^isBoolean}}{{^isString}}isObject(){{/isString}}{{/isBoolean}}{{/isDouble}}{{/isLong}}{{/isInteger}}{{/isArray}}{{/isModel}}{{/isEnumRef}}){ diff --git a/templates/java/tests/e2e/e2e.mustache b/templates/java/tests/e2e/e2e.mustache index 3ef00c05013..196647c9e12 100644 --- a/templates/java/tests/e2e/e2e.mustache +++ b/templates/java/tests/e2e/e2e.mustache @@ -12,8 +12,7 @@ import io.github.cdimascio.dotenv.Dotenv; import java.util.*; import java.time.Duration; import org.junit.jupiter.api.*; -import org.skyscreamer.jsonassert.JSONAssert; -import org.skyscreamer.jsonassert.JSONCompareMode; +import com.algolia.utils.TestHelpers; @TestInstance(TestInstance.Lifecycle.PER_CLASS) class {{client}}RequestsTestsE2E { @@ -45,7 +44,7 @@ class {{client}}RequestsTestsE2E { {{returnType}} res = {{> tests/method}}; {{#response}} {{#body}} - assertDoesNotThrow(() -> JSONAssert.assertEquals("{{#lambda.escapeQuotes}}{{{body}}}{{/lambda.escapeQuotes}}", json.writeValueAsString(res), JSONCompareMode.LENIENT)); + assertDoesNotThrow(() -> TestHelpers.lenientJsonAssert("{{#lambda.escapeQuotes}}{{{body}}}{{/lambda.escapeQuotes}}", json.writeValueAsString(res))); {{/body}} {{/response}} } diff --git a/templates/java/tests/generateInnerParams.mustache b/templates/java/tests/generateInnerParams.mustache index 81796c232b2..c6017dc7b5a 100644 --- a/templates/java/tests/generateInnerParams.mustache +++ b/templates/java/tests/generateInnerParams.mustache @@ -20,7 +20,7 @@ {{{value}}} {{/isBoolean}} {{#isEnum}} - {{{objectName}}}.{{#lambda.javaEnum}}{{{value}}}{{/lambda.javaEnum}} + {{^isIntegerEnum}}{{{objectName}}}.{{#lambda.javaEnum}}{{{value}}}{{/lambda.javaEnum}}{{/isIntegerEnum}}{{#isIntegerEnum}}{{{objectName}}}.fromValue({{{value}}}){{/isIntegerEnum}} {{/isEnum}} {{#isArray}} Arrays.asList({{#value}}{{> tests/generateParams}}{{^-last}},{{/-last}}{{/value}}) diff --git a/templates/java/tests/requests/requests.mustache b/templates/java/tests/requests/requests.mustache index f04b4de1bee..1c6af9b70ac 100644 --- a/templates/java/tests/requests/requests.mustache +++ b/templates/java/tests/requests/requests.mustache @@ -52,7 +52,7 @@ class {{client}}RequestsTests { }); {{#request}} EchoResponse req = echo.getLastResponse(); - assertEquals("{{{path}}}", req.path); + assertEquals("{{{serverPathSuffix}}}{{{path}}}", req.path); assertEquals("{{{method}}}", req.method); {{#body}} assertDoesNotThrow(() -> JSONAssert.assertEquals("{{#lambda.escapeJSON}}{{{body}}}{{/lambda.escapeJSON}}", req.body, JSONCompareMode.STRICT)); diff --git a/templates/javascript/clients/api-single.mustache b/templates/javascript/clients/api-single.mustache index 360fd0fff7d..398b804e63f 100644 --- a/templates/javascript/clients/api-single.mustache +++ b/templates/javascript/clients/api-single.mustache @@ -114,15 +114,11 @@ export function create{{#lambda.titlecase}}{{clientName}}{{/lambda.titlecase}}({ {{#allParams}} {{#required}} - if ({{#isBoolean}}{{paramName}} === null || {{paramName}} === undefined{{/isBoolean}}{{^isBoolean}}!{{paramName}}{{/isBoolean}}) { - throw new Error('Parameter `{{paramName}}` is required when calling `{{nickname}}`.'); - } + validateRequired('{{paramName}}', '{{nickname}}', {{paramName}}); {{#vars}} {{#required}} - if ({{#isBoolean}}{{paramName}}.{{baseName}} === null || {{paramName}}.{{baseName}} === undefined{{/isBoolean}}{{^isBoolean}}!{{paramName}}.{{baseName}}{{/isBoolean}}) { - throw new Error('Parameter `{{paramName}}.{{baseName}}` is required when calling `{{nickname}}`.'); - } + validateRequired('{{paramName}}.{{baseName}}', '{{nickname}}', {{paramName}}.{{baseName}}); {{/required}} {{/vars}} @@ -130,7 +126,7 @@ export function create{{#lambda.titlecase}}{{clientName}}{{/lambda.titlecase}}({ {{/allParams}} {{#vendorExtensions}} - const requestPath = '{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>,{{#x-is-custom-request}}{{paramName}}{{/x-is-custom-request}}{{^x-is-custom-request}}encodeURIComponent({{paramName}}){{/x-is-custom-request}}){{/pathParams}}; + const requestPath = '{{{serverPathSuffix}}}{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>,{{#x-is-custom-request}}{{paramName}}{{/x-is-custom-request}}{{^x-is-custom-request}}encodeURIComponent({{paramName}}){{/x-is-custom-request}}){{/pathParams}}; {{/vendorExtensions}} const headers: Headers = {}; const queryParameters: QueryParameters = {{#vendorExtensions.x-is-custom-request}}parameters ? parameters : {{/vendorExtensions.x-is-custom-request}}{}; @@ -179,6 +175,79 @@ export function create{{#lambda.titlecase}}{{clientName}}{{/lambda.titlecase}}({ return transporter.request(request, requestOptions); }, + {{#vendorExtensions.x-streaming}} + /** + * {{#notes}}{{{.}}} {{/notes}}(raw streaming version). + * + * Yields raw {@link ServerSentEvent} objects. Each event's `data` field contains a JSON-encoded `{{{returnType}}}` string. + * + * @see {{nickname}}Stream for the parsed variant. + * @see {{nickname}} for the non-streaming version. + */ + {{nickname}}StreamRaw( {{> client/api/operation/parameters}} ) : AsyncGenerator { + {{#allParams}} + {{#required}} + validateRequired('{{paramName}}', '{{nickname}}StreamRaw', {{paramName}}); + + {{#vars}} + {{#required}} + validateRequired('{{paramName}}.{{baseName}}', '{{nickname}}StreamRaw', {{paramName}}.{{baseName}}); + {{/required}} + {{/vars}} + + {{/required}} + {{/allParams}} + + {{#vendorExtensions}} + const requestPath = '{{{serverPathSuffix}}}{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>,{{#x-is-custom-request}}{{paramName}}{{/x-is-custom-request}}{{^x-is-custom-request}}encodeURIComponent({{paramName}}){{/x-is-custom-request}}){{/pathParams}}; + {{/vendorExtensions}} + const headers: Headers = {}; + const queryParameters: QueryParameters = {{#vendorExtensions.x-is-custom-request}}parameters ? parameters : {{/vendorExtensions.x-is-custom-request}}{}; + + {{^vendorExtensions.x-is-custom-request}}{{#queryParams}} + if ({{paramName}} !== undefined) { + queryParameters['{{baseName}}'] = {{paramName}}.toString(); + } + + {{/queryParams}}{{/vendorExtensions.x-is-custom-request}} + + {{#headerParams}} + if ({{paramName}} !== undefined) { + headers['{{baseName}}'] = {{paramName}}.toString(); + } + {{/headerParams}} + + const request: Request = { + method: '{{httpMethod}}', + path: requestPath, + queryParameters, + headers, + {{#bodyParam}} + data: {{paramName}}{{^required}}? {{paramName}} : {}{{/required}}, + {{/bodyParam}} + }; + + return transporter.requestStream(request, requestOptions); + }, + /** + * {{#notes}}{{{.}}} {{/notes}}(streaming version). + * + * Yields {@link StreamEvent} objects wrapping parsed `{{{returnType}}}` payloads. + * + * @see {{nickname}}StreamRaw for the raw variant. + * @see {{nickname}} for the non-streaming version. + */ + async *{{nickname}}Stream( {{> client/api/operation/parameters}} ) : AsyncGenerator{{/returnType}}>> { + for await (const event of this.{{nickname}}StreamRaw({{#vendorExtensions}}{{#x-create-wrapping-object}}{ {{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}} }{{/x-create-wrapping-object}}{{^x-create-wrapping-object}}{{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{/x-create-wrapping-object}}{{/vendorExtensions}}, requestOptions)) { + try { + const data = JSON.parse(event.data) as {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Record{{/returnType}}; + yield { data, raw: event }; + } catch (e) { + yield { data: null, raw: event, error: e as Error }; + } + } + }, + {{/vendorExtensions.x-streaming}} {{/operation}} }; diff --git a/templates/javascript/clients/client/api/imports.mustache b/templates/javascript/clients/client/api/imports.mustache index 1a30501e1c5..6fd5588f670 100644 --- a/templates/javascript/clients/client/api/imports.mustache +++ b/templates/javascript/clients/client/api/imports.mustache @@ -4,6 +4,7 @@ import { createNullLogger, getAlgoliaAgent, shuffle, + validateRequired, {{#isSearchClient}} ApiError, serializeQueryParameters, @@ -24,6 +25,8 @@ import type { QueryParameters, Request, RequestOptions, + ServerSentEvent, + StreamEvent, {{#isSearchClient}} IterableOptions, {{/isSearchClient}} diff --git a/templates/javascript/snippets/method.mustache b/templates/javascript/snippets/method.mustache index 2bd9aa0fde1..688840e1eb7 100644 --- a/templates/javascript/snippets/method.mustache +++ b/templates/javascript/snippets/method.mustache @@ -19,6 +19,7 @@ export {{#isAsyncMethod}}async{{/isAsyncMethod}} function snippetFor{{#lambda.pa {{> snippets/init}} // Call the API + {{^isStreaming}} {{#hasResponse}}const response = {{/hasResponse}}{{> tests/method}}; // >LOG @@ -26,6 +27,22 @@ export {{#isAsyncMethod}}async{{/isAsyncMethod}} function snippetFor{{#lambda.pa // print the response console.log(response); {{/hasResponse}} + {{/isStreaming}} + {{#isStreaming}} + // Use the streaming variant to iterate over events + for await (const event of client.{{method}}Stream({{#hasParams}}{{#parametersWithDataType.size}}{{{parameters}}}{{/parametersWithDataType.size}}{{/hasParams}}{{#hasRequestOptions}}, { + {{#requestOptions.queryParameters.parameters}} + queryParameters: {{{.}}}, + {{/requestOptions.queryParameters.parameters}} + {{#requestOptions.headers.parameters}} + headers: {{{.}}}, + {{/requestOptions.headers.parameters}} + } + {{/hasRequestOptions}} + )) { + console.log(event.data); + } + {{/isStreaming}} // SEPARATOR< } diff --git a/templates/javascript/tests/e2e/e2e.mustache b/templates/javascript/tests/e2e/e2e.mustache index f38e417eed2..623e97bda24 100644 --- a/templates/javascript/tests/e2e/e2e.mustache +++ b/templates/javascript/tests/e2e/e2e.mustache @@ -14,11 +14,12 @@ if (!process.env.{{e2eApiKey}}) { throw new Error("please provide an `{{e2eApiKey}}` env var for e2e tests"); } -const client = {{{clientName}}}(process.env.{{e2eAppID}}, process.env.{{e2eApiKey}}){{^isStandaloneClient}}.{{{initMethod}}}({{#hasRegionalHost}} {region:'{{{defaultRegion}}}'} {{/hasRegionalHost}}){{/isStandaloneClient}}; +const client = {{{clientName}}}(process.env.{{e2eAppID}}, process.env.{{e2eApiKey}}{{#isStandaloneClient}}{{#hasRegionalHost}}, '{{{defaultRegion}}}'{{/hasRegionalHost}}{{/isStandaloneClient}}){{^isStandaloneClient}}.{{{initMethod}}}({{#hasRegionalHost}} {region:'{{{defaultRegion}}}'} {{/hasRegionalHost}}){{/isStandaloneClient}}; {{#blocksE2E}} describe('{{operationId}}', () => { {{#tests}} + {{^isStreamingTest}} test('{{{testName}}}', async () => { {{#response}} {{#body}}const resp = {{/body}}{{> tests/method}}; @@ -30,6 +31,36 @@ describe('{{operationId}}', () => { {{/body}} {{/response}} }); + {{/isStreamingTest}} + + {{#isStreamingTest}} + test('{{{testName}}}', async () => { + {{#response}} + const events = []; + for await (const event of client.{{method}}Stream({{#hasParams}}{{#parametersWithDataType.size}}{{{parameters}}}{{/parametersWithDataType.size}}{{/hasParams}}{{#hasRequestOptions}}, { + {{#requestOptions.queryParameters.parameters}} + queryParameters: {{{.}}}, + {{/requestOptions.queryParameters.parameters}} + {{#requestOptions.headers.parameters}} + headers: {{{.}}}, + {{/requestOptions.headers.parameters}} + } + {{/hasRequestOptions}} + )) { + events.push(event); + } + expect(events.length).toBeGreaterThan(0); + + {{#body}} + const expectedBody = {{{.}}}; + // assert on collected events + const allData = events.filter(e => e.data !== null).map(e => e.data); + expect(allData.length).toBeGreaterThan(0); + expect(expectedBody).toEqual(union(expectedBody, allData[0])); + {{/body}} + {{/response}} + }); + {{/isStreamingTest}} {{/tests}} }) diff --git a/templates/javascript/tests/requests/requests.mustache b/templates/javascript/tests/requests/requests.mustache index 679c8448b1b..dac40ff6f6c 100644 --- a/templates/javascript/tests/requests/requests.mustache +++ b/templates/javascript/tests/requests/requests.mustache @@ -4,7 +4,7 @@ import { describe, test, expect } from 'vitest'; import { {{{clientName}}} } from '{{{importPackage}}}'; import { nodeEchoRequester } from '@algolia/requester-testing'; import type { EchoResponse } from '@algolia/requester-testing'; -import type { ClientOptions } from '@algolia/client-common'; +import type { ClientOptions, ServerSentEvent } from '@algolia/client-common'; const appId = process.env.ALGOLIA_APPLICATION_ID || 'test_app_id'; const apiKey = process.env.ALGOLIA_SEARCH_KEY || 'test_api_key'; @@ -15,10 +15,11 @@ const client = {{{clientName}}}(appId, apiKey{{#isStandaloneClient}}, {{#hasRegi {{#blocksRequests}} describe('{{operationId}}', () => { {{#tests}} + {{^isStreamingTest}} test('{{{testName}}}', async () => { const req = ({{> tests/method}}) as unknown as EchoResponse; - expect(req.path).toEqual('{{{request.path}}}'); + expect(req.path).toEqual('{{{serverPathSuffix}}}{{{request.path}}}'); expect(req.method).toEqual('{{{request.method}}}'); expect(req.data).toEqual({{#request.body}}{{{.}}}{{/request.body}}{{^request.body}}undefined{{/request.body}}); expect(req.searchParams).toStrictEqual({{#request.queryParameters}}{{{.}}}{{/request.queryParameters}}{{^request.queryParameters}}undefined{{/request.queryParameters}}); @@ -28,6 +29,37 @@ describe('{{operationId}}', () => { ); {{/request.headers}} }); + {{/isStreamingTest}} + + {{#isStreamingTest}} + test('{{{testName}}}', async () => { + const events: ServerSentEvent[] = []; + for await (const event of client.{{method}}{{streamMethodSuffix}}({{#hasParams}}{{#parametersWithDataType.size}}{{{parameters}}}{{/parametersWithDataType.size}}{{/hasParams}}{{#hasRequestOptions}}, { + {{#requestOptions.queryParameters.parameters}} + queryParameters: {{{.}}}, + {{/requestOptions.queryParameters.parameters}} + {{#requestOptions.headers.parameters}} + headers: {{{.}}}, + {{/requestOptions.headers.parameters}} + } + {{/hasRequestOptions}} + )) { + events.push(event); + } + expect(events.length).toBeGreaterThan(0); + const req = JSON.parse(events[0].data) as unknown as EchoResponse; + + expect(req.path).toEqual('{{{serverPathSuffix}}}{{{request.path}}}'); + expect(req.method).toEqual('{{{request.method}}}'); + expect(req.data).toEqual({{#request.body}}{{{.}}}{{/request.body}}{{^request.body}}undefined{{/request.body}}); + expect(req.searchParams).toStrictEqual({{#request.queryParameters}}{{{.}}}{{/request.queryParameters}}{{^request.queryParameters}}undefined{{/request.queryParameters}}); + {{#request.headers}} + expect(req.headers).toEqual( + expect.objectContaining({{{request.headers}}}) + ); + {{/request.headers}} + }); + {{/isStreamingTest}} {{/tests}} }) diff --git a/templates/kotlin/api.mustache b/templates/kotlin/api.mustache index 568552bb0dd..2b9b6df2973 100644 --- a/templates/kotlin/api.mustache +++ b/templates/kotlin/api.mustache @@ -89,7 +89,7 @@ public class {{classname}}( {{/requiredParams}} val requestConfig = RequestConfig( method = RequestMethod.{{httpMethod}}, - path = {{#vendorExtensions}}{{#x-is-custom-request}}"{{{path}}}".replace("{path}", path){{/x-is-custom-request}}{{^x-is-custom-request}}listOf({{#pathSegments}}{{{.}}}, {{/pathSegments}}){{/x-is-custom-request}}{{/vendorExtensions}}, + path = {{#vendorExtensions}}{{#x-is-custom-request}}"{{{serverPathSuffix}}}{{{path}}}".replace("{path}", path){{/x-is-custom-request}}{{^x-is-custom-request}}"{{{serverPathSuffix}}}".split("/").filter { it.isNotBlank() } + listOf({{#pathSegments}}{{{.}}}, {{/pathSegments}}){{/x-is-custom-request}}{{/vendorExtensions}}, {{#vendorExtensions.x-use-read-transporter}} isRead = {{{vendorExtensions.x-use-read-transporter}}}, {{/vendorExtensions.x-use-read-transporter}} diff --git a/templates/kotlin/builder_function.mustache b/templates/kotlin/builder_function.mustache index 67056984cd9..c5edebc751f 100644 --- a/templates/kotlin/builder_function.mustache +++ b/templates/kotlin/builder_function.mustache @@ -8,7 +8,7 @@ */ public fun {{vendorExtensions.x-one-of-explicit-name}}( {{#vars}} - {{{name}}}: {{> data_class_field_type}}{{^required}}? = null{{/required}}, + {{{name}}}: {{> data_class_field_type}}{{#required}}{{#isNullable}}? = null{{/isNullable}}{{/required}}{{^required}}? = null{{/required}}, {{/vars}} ): {{classname}} = {{vendorExtensions.x-fully-qualified-classname}} ( {{#vars}} diff --git a/templates/kotlin/data_class_field.mustache b/templates/kotlin/data_class_field.mustache index 5a4a65f2f6a..70336d084fc 100644 --- a/templates/kotlin/data_class_field.mustache +++ b/templates/kotlin/data_class_field.mustache @@ -4,4 +4,4 @@ {{#deprecated}} @Deprecated(message = "This property is deprecated.") {{/deprecated}} - @SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{#isInherited}}override {{/isInherited}}val {{{name}}}: {{> data_class_field_type}}{{^required}}? = null{{/required}} + @SerialName(value = "{{{vendorExtensions.x-base-name-literal}}}") {{#isInherited}}override {{/isInherited}}val {{{name}}}: {{> data_class_field_type}}{{#required}}{{#isNullable}}? = null{{/isNullable}}{{/required}}{{^required}}? = null{{/required}} diff --git a/templates/kotlin/enum_class.mustache b/templates/kotlin/enum_class.mustache index 608f4f43436..1c80b270475 100644 --- a/templates/kotlin/enum_class.mustache +++ b/templates/kotlin/enum_class.mustache @@ -1,20 +1,41 @@ import kotlinx.serialization.* +import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* {{#description}} /** * {{{description}}} */ {{/description}} +{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}} @Serializable public enum class {{classname}}(public val value: kotlin.String){{> oneof_parent}} { +{{/isString}}{{^isString}} +@Serializable(with = {{classname}}.Serializer::class) +public enum class {{classname}}(public val value: kotlin.String){{> oneof_parent}} { +{{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}} {{#allowableValues}}{{#enumVars}} {{#enumDescription}} /** * {{{.}}} */ {{/enumDescription}} - @SerialName(value = {{{value}}}) - {{#lambda.pascalcase}}{{&name}}{{/lambda.pascalcase}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} + {{#isString}}@SerialName(value = {{{value}}}) + {{/isString}}{{#lambda.pascalcase}}{{&name}}{{/lambda.pascalcase}}({{{value}}}){{^-last}},{{/-last}}{{#-last}};{{/-last}} {{/enumVars}}{{/allowableValues}} +{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}} + override fun toString(): kotlin.String = value +{{/isString}}{{^isString}} override fun toString(): kotlin.String = value + + internal object Serializer : KSerializer<{{classname}}> { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("{{classname}}", PrimitiveKind.INT) + override fun serialize(encoder: Encoder, value: {{classname}}): Unit = encoder.encodeInt(value.value.toInt()) + override fun deserialize(decoder: Decoder): {{classname}} { + val v = decoder.decodeInt() + return entries.first { it.value == v.toString() } + } + } +{{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}} } diff --git a/templates/kotlin/json_object_field.mustache b/templates/kotlin/json_object_field.mustache index fcced575c14..99d08309c14 100644 --- a/templates/kotlin/json_object_field.mustache +++ b/templates/kotlin/json_object_field.mustache @@ -4,4 +4,4 @@ {{#deprecated}} @Deprecated(message = "This property is deprecated.") {{/deprecated}} - {{#isInherited}}override {{/isInherited}}val {{{name}}}: {{> data_class_field_type}}{{^required}}? = null{{/required}} + {{#isInherited}}override {{/isInherited}}val {{{name}}}: {{> data_class_field_type}}{{#required}}{{#isNullable}}? = null{{/isNullable}}{{/required}}{{^required}}? = null{{/required}} diff --git a/templates/kotlin/oneof_interface.mustache b/templates/kotlin/oneof_interface.mustache index 9546c1abbac..454fb423df2 100644 --- a/templates/kotlin/oneof_interface.mustache +++ b/templates/kotlin/oneof_interface.mustache @@ -67,6 +67,13 @@ public sealed interface {{classname}} { internal class {{classname}}Serializer : JsonContentPolymorphicSerializer<{{classname}}>({{classname}}::class) { override fun selectDeserializer(element: JsonElement): DeserializationStrategy<{{classname}}> { + {{#discriminator}} + when (element.jsonObject["{{{propertyBaseName}}}"]?.jsonPrimitive?.contentOrNull) { + {{#mappedModels}} + "{{{mappingName}}}" -> return {{{modelName}}}.serializer() + {{/mappedModels}} + } + {{/discriminator}} return when { {{#composedSchemas.oneOf}} {{^isModel}} diff --git a/templates/kotlin/tests/e2e/e2e.mustache b/templates/kotlin/tests/e2e/e2e.mustache index 1d02f27f596..1d95fbf0ac2 100644 --- a/templates/kotlin/tests/e2e/e2e.mustache +++ b/templates/kotlin/tests/e2e/e2e.mustache @@ -6,8 +6,6 @@ import com.algolia.client.model.{{import}}.* import com.algolia.client.configuration.* import com.algolia.client.transport.* import com.algolia.utils.* -import org.skyscreamer.jsonassert.JSONAssert; -import org.skyscreamer.jsonassert.JSONCompareMode; import io.ktor.http.* import io.github.cdimascio.dotenv.Dotenv import kotlinx.coroutines.test.* @@ -44,7 +42,7 @@ class {{clientPrefix}}Test { {{#response}} {{#body}} response = { - JSONAssert.assertEquals("{{#lambda.escapeQuotes}}{{{body}}}{{/lambda.escapeQuotes}}", Json.encodeToString(it), JSONCompareMode.LENIENT) + lenientJsonAssert("{{#lambda.escapeQuotes}}{{{body}}}{{/lambda.escapeQuotes}}", Json.encodeToString(it)) }, {{/body}} {{/response}} diff --git a/templates/kotlin/tests/intercept.mustache b/templates/kotlin/tests/intercept.mustache index 82d98dd9168..f93ff6d2ce7 100644 --- a/templates/kotlin/tests/intercept.mustache +++ b/templates/kotlin/tests/intercept.mustache @@ -1,5 +1,5 @@ {{#request}} - assertEquals("{{{path}}}".toPathSegments(), it.url.pathSegments) + assertEquals("{{{serverPathSuffix}}}{{{path}}}".toPathSegments(), it.url.pathSegments) assertEquals(HttpMethod.parse("{{method}}"), it.method) {{#headers}} assertContainsAll("""{{{.}}}""", it.headers) diff --git a/templates/kotlin/tests/request_param.mustache b/templates/kotlin/tests/request_param.mustache index 0146bf2f787..de0f8367fa4 100644 --- a/templates/kotlin/tests/request_param.mustache +++ b/templates/kotlin/tests/request_param.mustache @@ -1 +1 @@ -{{^hasAdditionalProperties}}{{#lambda.camelcase}}{{{key}}}{{/lambda.camelcase}} = {{> tests/param_value}},{{/hasAdditionalProperties}}{{#hasAdditionalProperties}}{{> tests/param_value}},{{/hasAdditionalProperties}} \ No newline at end of file +{{^isNull}}{{^hasAdditionalProperties}}{{#lambda.camelcase}}{{{key}}}{{/lambda.camelcase}} = {{> tests/param_value}},{{/hasAdditionalProperties}}{{#hasAdditionalProperties}}{{> tests/param_value}},{{/hasAdditionalProperties}}{{/isNull}}{{#isNull}}{{#required}}{{^hasAdditionalProperties}}{{#lambda.camelcase}}{{{key}}}{{/lambda.camelcase}} = {{> tests/param_value}},{{/hasAdditionalProperties}}{{#hasAdditionalProperties}}{{> tests/param_value}},{{/hasAdditionalProperties}}{{/required}}{{/isNull}} \ No newline at end of file diff --git a/templates/php/api.mustache b/templates/php/api.mustache index 0cab9ec7c52..df92e515bab 100644 --- a/templates/php/api.mustache +++ b/templates/php/api.mustache @@ -286,7 +286,7 @@ use Algolia\AlgoliaSearch\Exceptions\NotFoundException; {{/required}} {{/allParams}} - $resourcePath = '{{{path}}}'; + $resourcePath = '{{{serverPathSuffix}}}{{{path}}}'; $queryParameters = []; $headers = []; $httpBody = {{#bodyParam}}{{#required}}${{paramName}}{{/required}}{{^required}} isset(${{paramName}}) ? ${{paramName}} : []{{/required}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; diff --git a/templates/php/model_enum.mustache b/templates/php/model_enum.mustache index 77001f2e605..ccf21b5dfac 100644 --- a/templates/php/model_enum.mustache +++ b/templates/php/model_enum.mustache @@ -5,7 +5,7 @@ class {{classname}} */ {{#allowableValues}} {{#enumVars}} - public const {{^isString}}NUMBER_{{/isString}}{{{name}}} = {{{value}}}; + public const {{{name}}} = {{{value}}}; {{/enumVars}} {{/allowableValues}} @@ -18,7 +18,7 @@ class {{classname}} return [ {{#allowableValues}} {{#enumVars}} - self::{{^isString}}NUMBER_{{/isString}}{{{name}}}{{^-last}}, + self::{{{name}}}{{^-last}}, {{/-last}} {{/enumVars}} {{/allowableValues}} diff --git a/templates/php/tests/generateParams.mustache b/templates/php/tests/generateParams.mustache index abfd1fdf9ed..3191df6c71c 100644 --- a/templates/php/tests/generateParams.mustache +++ b/templates/php/tests/generateParams.mustache @@ -9,7 +9,7 @@ "{{#lambda.escapeJSON}}{{{value}}}{{/lambda.escapeJSON}}", {{/isString}} {{#isEnum}} - "{{{value}}}", + {{^isIntegerEnum}}"{{{value}}}"{{/isIntegerEnum}}{{#isIntegerEnum}}{{{value}}}{{/isIntegerEnum}}, {{/isEnum}} {{#isInteger}} {{{value}}}, diff --git a/templates/php/tests/requests/requests.mustache b/templates/php/tests/requests/requests.mustache index 5d051e5ad32..3d90edc6613 100644 --- a/templates/php/tests/requests/requests.mustache +++ b/templates/php/tests/requests/requests.mustache @@ -87,7 +87,7 @@ class {{clientPrefix}}Test extends TestCase implements HttpClientInterface $this->assertRequests([ [ - "path" => "{{{request.path}}}", + "path" => "{{{serverPathSuffix}}}{{{request.path}}}", "method" => "{{{request.method}}}", {{#request.body}} "body" => json_decode("{{#lambda.escapeQuotes}}{{{request.body}}}{{/lambda.escapeQuotes}}"), diff --git a/templates/python/api.mustache b/templates/python/api.mustache index 15c24c19f55..aa1368d3717 100644 --- a/templates/python/api.mustache +++ b/templates/python/api.mustache @@ -232,7 +232,7 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}: return {{^isSyncClient}}await {{/isSyncClient}}self._transporter.request( verb=Verb.{{httpMethod}}, - path={{#vendorExtensions}}'{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>, {{#x-is-custom-request}}{{paramName}}{{/x-is-custom-request}}{{^x-is-custom-request}}quote(str({{paramName}}), safe=''){{/x-is-custom-request}}){{/pathParams}},{{/vendorExtensions}} + path={{#vendorExtensions}}'{{{serverPathSuffix}}}{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>, {{#x-is-custom-request}}{{paramName}}{{/x-is-custom-request}}{{^x-is-custom-request}}quote(str({{paramName}}), safe=''){{/x-is-custom-request}}){{/pathParams}},{{/vendorExtensions}} request_options=self._request_options.merge( {{#queryParams.0}}query_parameters=_query_parameters,{{/queryParams.0}} {{#headerParams.0}}headers=_headers,{{/headerParams.0}} @@ -275,6 +275,100 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}: resp = {{^isSyncClient}}await {{/isSyncClient}}self.{{operationId}}_with_http_info({{#allParams}}{{paramName}},{{/allParams}}request_options) return resp.deserialize({{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}None{{/returnType}}, resp.raw_data) + {{#vendorExtensions.x-streaming}} + + {{^isSyncClient}}async {{/isSyncClient}}def {{operationId}}_stream_raw{{> partial_api_args}} -> {{^isSyncClient}}AsyncIterator[ServerSentEvent]{{/isSyncClient}}{{#isSyncClient}}Iterator[ServerSentEvent]{{/isSyncClient}}: + """ + {{#notes}} + {{{.}}} (raw streaming version). + {{/notes}} + + Yields raw :class:`ServerSentEvent` objects.{{#returnType}} Each event's ``data`` field contains a JSON-encoded ``{{{returnType}}}`` string.{{/returnType}} + + {{#allParams}} + :param {{paramName}}:{{#description}} {{{.}}}{{/description}}{{#required}} (required){{/required}}{{#optional}}(optional){{/optional}} + :type {{paramName}}: {{dataType}}{{#optional}}, optional{{/optional}} + {{/allParams}} + :param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional) + :return: Returns an iterator of ServerSentEvent objects.{{#returnType}} Each event's ``data`` field contains JSON-encoded ``{{{returnType}}}``.{{/returnType}} + """ + + {{#allParams}} + {{#required}} + if {{paramName}} is None: + raise ValueError("Parameter `{{paramName}}` is required when calling `{{nickname}}_stream_raw`.") + + {{/required}} + {{/allParams}} + + {{#queryParams.0}} + _query_parameters: Dict[str, Any] = {} + {{/queryParams.0}} + {{#headerParams.0}} + _headers: Dict[str, str] = {} + {{/headerParams.0}} + + {{#vendorExtensions}} + {{#queryParams}} + if {{paramName}} is not None: + {{^x-is-custom-request}} + _query_parameters["{{baseName}}"] = {{paramName}} + {{/x-is-custom-request}} + {{#x-is-custom-request}} + for _qpkey, _qpvalue in {{paramName}}.items(): + _query_parameters[_qpkey] = _qpvalue + {{/x-is-custom-request}} + {{/queryParams}} + {{/vendorExtensions}} + + {{#headerParams}} + if {{paramName}} is not None: + _headers['{{#lambda.lowercase}}{{baseName}}{{/lambda.lowercase}}'] = {{paramName}} + {{/headerParams}} + + {{#bodyParam}} + _data = {} + if {{paramName}} is not None: + _data = {{paramName}} + {{/bodyParam}} + + {{^isSyncClient}}async {{/isSyncClient}}for event in self._transporter.request_stream( + verb=Verb.{{httpMethod}}, + path={{#vendorExtensions}}'{{{serverPathSuffix}}}{{{path}}}'{{#pathParams}}.replace({{=<% %>=}}'{<%baseName%>}'<%={{ }}=%>, {{#x-is-custom-request}}{{paramName}}{{/x-is-custom-request}}{{^x-is-custom-request}}quote(str({{paramName}}), safe=''){{/x-is-custom-request}}){{/pathParams}},{{/vendorExtensions}} + request_options=self._request_options.merge( + {{#queryParams.0}}query_parameters=_query_parameters,{{/queryParams.0}} + {{#headerParams.0}}headers=_headers,{{/headerParams.0}} + {{#bodyParam}}data=dumps(body_serializer(_data)),{{/bodyParam}} + user_request_options=request_options, + ), + use_read_transporter={{#vendorExtensions}}{{#x-use-read-transporter}}True{{/x-use-read-transporter}}{{^x-use-read-transporter}}False{{/x-use-read-transporter}}{{/vendorExtensions}}, + ): + yield event + + {{^isSyncClient}}async {{/isSyncClient}}def {{operationId}}_stream{{> partial_api_args}} -> {{^isSyncClient}}AsyncIterator[StreamEvent[{{{returnType}}}{{^returnType}}object{{/returnType}}]]{{/isSyncClient}}{{#isSyncClient}}Iterator[StreamEvent[{{{returnType}}}{{^returnType}}object{{/returnType}}]]{{/isSyncClient}}: + """ + {{#notes}} + {{{.}}} (streaming version). + {{/notes}} + + Yields :class:`StreamEvent` objects wrapping parsed ``{{{returnType}}}{{^returnType}}object{{/returnType}}`` payloads. + + {{#allParams}} + :param {{paramName}}:{{#description}} {{{.}}}{{/description}}{{#required}} (required){{/required}}{{#optional}}(optional){{/optional}} + :type {{paramName}}: {{dataType}}{{#optional}}, optional{{/optional}} + {{/allParams}} + :param request_options: The request options to send along with the query, they will be merged with the transporter base parameters (headers, query params, timeouts, etc.). (optional) + :return: Returns an iterator of :class:`StreamEvent[{{{returnType}}}{{^returnType}}object{{/returnType}}]` objects. + """ + + {{^isSyncClient}}async {{/isSyncClient}}for event in self.{{operationId}}_stream_raw({{#allParams}}{{paramName}},{{/allParams}}request_options): + try: + _parsed = ApiResponse.deserialize({{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}None{{/returnType}}, event.data) + yield StreamEvent(data=_parsed, raw=event) + except Exception as e: + yield StreamEvent(data=None, raw=event, error=e) + + {{/vendorExtensions.x-streaming}} {{/operation}} {{/operations}} {{/modes}} diff --git a/templates/python/imports.mustache b/templates/python/imports.mustache index 29a9b531b55..1be2c64c044 100644 --- a/templates/python/imports.mustache +++ b/templates/python/imports.mustache @@ -29,6 +29,7 @@ from pydantic import ( ) from typing import ( Any, + AsyncIterator, Callable, ClassVar, Dict, @@ -56,5 +57,6 @@ from algoliasearch.http.serializer import body_serializer, QueryParametersSerial from algoliasearch.http.transporter import Transporter from algoliasearch.http.transporter_sync import TransporterSync from algoliasearch.http.verb import Verb +from algoliasearch.http.sse import ServerSentEvent, StreamEvent from algoliasearch.{{packageName}}.config import {{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}} \ No newline at end of file diff --git a/templates/python/model_oneof.mustache b/templates/python/model_oneof.mustache index cb39264b306..2fa49b2f0ba 100644 --- a/templates/python/model_oneof.mustache +++ b/templates/python/model_oneof.mustache @@ -46,6 +46,16 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}} {{/isNullable}} error_messages = [] + {{#discriminator}} + _json_dict = loads(json_str) + _discriminator_value = _json_dict.get("{{{propertyBaseName}}}") + {{#mappedModels}} + if _discriminator_value == "{{{mappingName}}}": + instance.actual_instance = {{{modelName}}}.from_json(json_str) + return instance + {{/mappedModels}} + {{/discriminator}} + {{#composedSchemas.oneOf}} {{#isContainer}} try: diff --git a/templates/python/snippets/method.mustache b/templates/python/snippets/method.mustache index 723e58f5c2a..9b6d0de087c 100644 --- a/templates/python/snippets/method.mustache +++ b/templates/python/snippets/method.mustache @@ -17,6 +17,7 @@ def snippet_for_{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}{{testIndex} {{> snippets/init}} # Call the API + {{^isStreaming}} {{#hasResponse}}response = {{/hasResponse}}client.{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}({{#parametersWithDataType}}{{> tests/generateParams}}{{/parametersWithDataType}}{{#hasRequestOptions}} request_options={ {{#requestOptions.headers.parameters}}"headers":loads("""{{{.}}}"""),{{/requestOptions.headers.parameters}}{{#requestOptions.queryParameters.parameters}}"query_parameters":loads("""{{{.}}}"""),{{/requestOptions.queryParameters.parameters}} }{{/hasRequestOptions}}) # >LOG @@ -24,6 +25,13 @@ def snippet_for_{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}{{testIndex} # print the response print(response) {{/hasResponse}} + {{/isStreaming}} + {{#isStreaming}} + # Use the streaming variant to iterate over events + for event in client.{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}_stream({{#parametersWithDataType}}{{> tests/generateParams}}{{/parametersWithDataType}}{{#hasRequestOptions}} request_options={ {{#requestOptions.headers.parameters}}"headers":loads("""{{{.}}}"""),{{/requestOptions.headers.parameters}}{{#requestOptions.queryParameters.parameters}}"query_parameters":loads("""{{{.}}}"""),{{/requestOptions.queryParameters.parameters}} }{{/hasRequestOptions}}): + # >LOG + print(event.data) + {{/isStreaming}} # SEPARATOR< {{/snippets}} diff --git a/templates/python/tests/e2e/e2e.mustache b/templates/python/tests/e2e/e2e.mustache index d5430524b25..41c113517a0 100644 --- a/templates/python/tests/e2e/e2e.mustache +++ b/templates/python/tests/e2e/e2e.mustache @@ -28,6 +28,7 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}{{#isSyncClien {{#blocksE2E}} {{#tests}} + {{^isStreamingTest}} {{^isSyncClient}}async {{/isSyncClient}}def test_{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}_{{testIndex}}(self): """ {{{testName}}} @@ -45,6 +46,27 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}{{#isSyncClien assert self._helpers.union(_expected_body, self._helpers.unwrap(resp)) == _expected_body {{/body}} {{/response}} + {{/isStreamingTest}} + + {{#isStreamingTest}} + {{^isSyncClient}}async {{/isSyncClient}}def test_{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}_{{testIndex}}(self): + """ + {{{testName}}} + """ + _events = [] + {{^isSyncClient}}async {{/isSyncClient}}for event in {{#lambda.pascalcase}}{{{client}}}{{#isSyncClient}}Sync{{/isSyncClient}}{{/lambda.pascalcase}}(self._e2e_app_id, self._e2e_api_key{{#hasRegionalHost}}, "{{{defaultRegion}}}"{{/hasRegionalHost}}).{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}_stream({{#parametersWithDataType}}{{> tests/generateParams}}{{/parametersWithDataType}}{{#hasRequestOptions}} request_options={ {{#requestOptions.headers.parameters}}"headers":loads("""{{{.}}}"""),{{/requestOptions.headers.parameters}}{{#requestOptions.queryParameters.parameters}}"query_parameters":loads("""{{{.}}}"""),{{/requestOptions.queryParameters.parameters}} }{{/hasRequestOptions}}): + _events.append(event) + assert len(_events) > 0 + {{#response}} + + {{#body}} + _all_data = [e.data for e in _events if e.data is not None] + assert len(_all_data) > 0 + _expected_body = loads("""{{{.}}}""") + assert self._helpers.union(_expected_body, self._helpers.unwrap(_all_data[0])) == _expected_body + {{/body}} + {{/response}} + {{/isStreamingTest}} {{/tests}} {{/blocksE2E}} diff --git a/templates/python/tests/generateInnerParams.mustache b/templates/python/tests/generateInnerParams.mustache index ce24dc49320..7d88bc10697 100644 --- a/templates/python/tests/generateInnerParams.mustache +++ b/templates/python/tests/generateInnerParams.mustache @@ -1 +1 @@ -{{#isNull}} None {{/isNull}}{{#isVerbatim}}{{#lambda.snakecase}}{{{value}}}{{/lambda.snakecase}}{{/isVerbatim}}{{#isString}} "{{#lambda.escapeJSON}}{{{value}}}{{/lambda.escapeJSON}}" {{/isString}} {{#isInteger}} {{{value}}} {{/isInteger}} {{#isLong}} {{{value}}} {{/isLong}} {{#isDouble}} {{{value}}} {{/isDouble}} {{#isBoolean}} {{#lambda.titlecase}}{{{value}}}{{/lambda.titlecase}} {{/isBoolean}} {{#isEnum}} "{{{value}}}" {{/isEnum}} {{#isArray}} [ {{#value}}{{> tests/generateParams}}{{/value}} ] {{/isArray}} {{#isObject}} { {{#value}}{{> tests/generateParams}}{{/value}} } {{/isObject}} {{#isFreeFormObject}} {{#isAnyType}} { {{#value}}{{#entrySet}}"{{{key}}}":"{{{value}}}"{{^-last}},{{/-last}}{{/entrySet}}{{/value}} } {{/isAnyType}} {{^isAnyType}} { {{#value}}{{> tests/generateParams}}{{/value}} } {{/isAnyType}} {{/isFreeFormObject}} \ No newline at end of file +{{#isNull}} None {{/isNull}}{{#isVerbatim}}{{#lambda.snakecase}}{{{value}}}{{/lambda.snakecase}}{{/isVerbatim}}{{#isString}} "{{#lambda.escapeJSON}}{{{value}}}{{/lambda.escapeJSON}}" {{/isString}} {{#isInteger}} {{{value}}} {{/isInteger}} {{#isLong}} {{{value}}} {{/isLong}} {{#isDouble}} {{{value}}} {{/isDouble}} {{#isBoolean}} {{#lambda.titlecase}}{{{value}}}{{/lambda.titlecase}} {{/isBoolean}} {{#isEnum}} {{^isIntegerEnum}}"{{{value}}}"{{/isIntegerEnum}}{{#isIntegerEnum}}{{{value}}}{{/isIntegerEnum}} {{/isEnum}} {{#isArray}} [ {{#value}}{{> tests/generateParams}}{{/value}} ] {{/isArray}} {{#isObject}} { {{#value}}{{> tests/generateParams}}{{/value}} } {{/isObject}} {{#isFreeFormObject}} {{#isAnyType}} { {{#value}}{{#entrySet}}"{{{key}}}":"{{{value}}}"{{^-last}},{{/-last}}{{/entrySet}}{{/value}} } {{/isAnyType}} {{^isAnyType}} { {{#value}}{{> tests/generateParams}}{{/value}} } {{/isAnyType}} {{/isFreeFormObject}} \ No newline at end of file diff --git a/templates/python/tests/requests/requests.mustache b/templates/python/tests/requests/requests.mustache index 66dee2ff581..c24b918a008 100644 --- a/templates/python/tests/requests/requests.mustache +++ b/templates/python/tests/requests/requests.mustache @@ -20,6 +20,7 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}{{#isSyncClien {{#blocksRequests}} {{#tests}} + {{^isStreamingTest}} {{^isSyncClient}}async {{/isSyncClient}}def test_{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}_{{testIndex}}(self): """ {{{testName}}} @@ -27,7 +28,7 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}{{#isSyncClien _req = {{^isSyncClient}}await {{/isSyncClient}}self._client.{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}_with_http_info({{#parametersWithDataType}}{{> tests/generateParams}}{{/parametersWithDataType}}{{#hasRequestOptions}} request_options={ {{#requestOptions.headers.parameters}}"headers":loads("""{{{.}}}"""),{{/requestOptions.headers.parameters}}{{#requestOptions.queryParameters.parameters}}"query_parameters":loads("""{{{.}}}"""),{{/requestOptions.queryParameters.parameters}} }{{/hasRequestOptions}}) {{#request}} - assert _req.path == "{{{path}}}" + assert _req.path == "{{{serverPathSuffix}}}{{{path}}}" assert _req.verb == "{{{method}}}" assert _req.query_parameters.items() == {{#queryParameters}}{{{.}}}{{/queryParameters}}{{^queryParameters}}{}{{/queryParameters}}.items() assert _req.headers.items() >= {{#headers}}{{{.}}}{{/headers}}{{^headers}}{}{{/headers}}.items() @@ -40,6 +41,34 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}}{{#isSyncClien {{/assertNullBody}} {{/body}} {{/request}} + {{/isStreamingTest}} + + {{#isStreamingTest}} + {{^isSyncClient}}async {{/isSyncClient}}def test_{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}_{{testIndex}}(self): + """ + {{{testName}}} + """ + _events = [] + {{^isSyncClient}}async {{/isSyncClient}}for event in self._client.{{#lambda.snakecase}}{{method}}{{/lambda.snakecase}}{{streamMethodSuffixSnake}}({{#parametersWithDataType}}{{> tests/generateParams}}{{/parametersWithDataType}}{{#hasRequestOptions}} request_options={ {{#requestOptions.headers.parameters}}"headers":loads("""{{{.}}}"""),{{/requestOptions.headers.parameters}}{{#requestOptions.queryParameters.parameters}}"query_parameters":loads("""{{{.}}}"""),{{/requestOptions.queryParameters.parameters}} }{{/hasRequestOptions}}): + _events.append(event) + assert len(_events) > 0 + _req = loads(_events[0].data) + + {{#request}} + assert _req["path"] == "{{{serverPathSuffix}}}{{{path}}}" + assert _req["verb"] == "{{{method}}}" + assert dict(_req["query_parameters"]).items() == {{#queryParameters}}{{{.}}}{{/queryParameters}}{{^queryParameters}}{}{{/queryParameters}}.items() + assert dict(_req["headers"]).items() >= {{#headers}}{{{.}}}{{/headers}}{{^headers}}{}{{/headers}}.items() + {{#body}} + assert loads(_req["data"]) == loads("""{{#lambda.escapeSlash}}{{{.}}}{{/lambda.escapeSlash}}""") + {{/body}} + {{^body}} + {{#assertNullBody}} + assert _req["data"] is None + {{/assertNullBody}} + {{/body}} + {{/request}} + {{/isStreamingTest}} {{/tests}} {{/blocksRequests}} diff --git a/templates/ruby/api.mustache b/templates/ruby/api.mustache index f8781db17c7..218718d5b37 100644 --- a/templates/ruby/api.mustache +++ b/templates/ruby/api.mustache @@ -132,7 +132,7 @@ module {{moduleName}} {{/isNullable}} {{/allParams}} {{#vendorExtensions}} - path = '{{{path}}}'{{#pathParams}}.sub('{' + '{{baseName}}' + '}', {{#x-is-custom-request}}{{paramName}}.to_s{{/x-is-custom-request}}{{^x-is-custom-request}}Transport.encode_uri({{paramName}}.to_s){{/x-is-custom-request}}){{/pathParams}} + path = '{{{serverPathSuffix}}}{{{path}}}'{{#pathParams}}.sub('{' + '{{baseName}}' + '}', {{#x-is-custom-request}}{{paramName}}.to_s{{/x-is-custom-request}}{{^x-is-custom-request}}Transport.encode_uri({{paramName}}.to_s){{/x-is-custom-request}}){{/pathParams}} query_params = {} {{#queryParams}} {{#x-is-custom-request}} diff --git a/templates/ruby/partial_oneof_module.mustache b/templates/ruby/partial_oneof_module.mustache index 8f88b0eb1ca..2b0dfaf3a55 100644 --- a/templates/ruby/partial_oneof_module.mustache +++ b/templates/ruby/partial_oneof_module.mustache @@ -27,6 +27,16 @@ # due to the way the deserialization is made in the base_object template (it just casts without verifying). # - TODO: scalar values are de facto behaving as if they were nullable. # - TODO: logging when debugging is set. + {{#discriminator}} + if data.is_a?(Hash) && data.key?("{{{propertyBaseName}}}") + case data["{{{propertyBaseName}}}"] + {{#mappedModels}} + when "{{{mappingName}}}" + return find_and_cast_into_type(:'{{{modelName}}}', data) + {{/mappedModels}} + end + end + {{/discriminator}} openapi_one_of.each do |klass| begin next if klass == :AnyType # "nullable: true" diff --git a/templates/ruby/tests/generateInnerParams.mustache b/templates/ruby/tests/generateInnerParams.mustache index bae0e51b7d8..05a24c96985 100644 --- a/templates/ruby/tests/generateInnerParams.mustache +++ b/templates/ruby/tests/generateInnerParams.mustache @@ -1 +1 @@ -{{#isNull}}nil {{/isNull}}{{#isVerbatim}}{{#lambda.identifier}}{{{value}}}{{/lambda.identifier}}{{/isVerbatim}}{{#isString}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}" {{/isString}}{{#isInteger}}{{{value}}} {{/isInteger}}{{#isLong}}{{{value}}} {{/isLong}}{{#isDouble}}{{{value}}} {{/isDouble}}{{#isBoolean}}{{{value}}} {{/isBoolean}} {{#isEnum}}'{{{value}}}' {{/isEnum}}{{#isArray}} [ {{#value}}{{> tests/generateParams}}{{/value}} ] {{/isArray}}{{#isObject}} Algolia::{{{modelModule}}}::{{{objectName}}}.new({{#value}}{{> tests/generateParams}}{{/value}}) {{/isObject}}{{#isFreeFormObject}} {{#isAnyType}} { {{#value}}{{#entrySet}}'{{{key}}}':"{{{value}}}"{{^-last}},{{/-last}}{{/entrySet}}{{/value}} } {{/isAnyType}} {{^isAnyType}} { {{#value}}{{> tests/generateParams}}{{/value}} } {{/isAnyType}} {{/isFreeFormObject}} \ No newline at end of file +{{#isNull}}nil {{/isNull}}{{#isVerbatim}}{{#lambda.identifier}}{{{value}}}{{/lambda.identifier}}{{/isVerbatim}}{{#isString}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}" {{/isString}}{{#isInteger}}{{{value}}} {{/isInteger}}{{#isLong}}{{{value}}} {{/isLong}}{{#isDouble}}{{{value}}} {{/isDouble}}{{#isBoolean}}{{{value}}} {{/isBoolean}} {{#isEnum}}{{^isIntegerEnum}}'{{{value}}}'{{/isIntegerEnum}}{{#isIntegerEnum}}{{{value}}}{{/isIntegerEnum}} {{/isEnum}}{{#isArray}} [ {{#value}}{{> tests/generateParams}}{{/value}} ] {{/isArray}}{{#isObject}} Algolia::{{{modelModule}}}::{{{objectName}}}.new({{#value}}{{> tests/generateParams}}{{/value}}) {{/isObject}}{{#isFreeFormObject}} {{#isAnyType}} { {{#value}}{{#entrySet}}'{{{key}}}':"{{{value}}}"{{^-last}},{{/-last}}{{/entrySet}}{{/value}} } {{/isAnyType}} {{^isAnyType}} { {{#value}}{{> tests/generateParams}}{{/value}} } {{/isAnyType}} {{/isFreeFormObject}} \ No newline at end of file diff --git a/templates/ruby/tests/requests/requests.mustache b/templates/ruby/tests/requests/requests.mustache index 9ef1d7808ae..49919547a73 100644 --- a/templates/ruby/tests/requests/requests.mustache +++ b/templates/ruby/tests/requests/requests.mustache @@ -20,7 +20,7 @@ class Test{{#lambda.pascalcase}}{{{client}}}{{/lambda.pascalcase}} < Test::Unit: {{#request}} assert_equal(:{{#lambda.lowercase}}{{method}}{{/lambda.lowercase}}, req.method) - assert_equal('{{path}}', req.path) + assert_equal('{{{serverPathSuffix}}}{{path}}', req.path) assert_equal({{#request.queryParameters}}{{{.}}}{{/request.queryParameters}}{{^request.queryParameters}}{}{{/request.queryParameters}}.to_a, req.query_params.to_a) assert(({{#request.headers}}{{{.}}}.transform_keys(&:to_s){{/request.headers}}{{^request.headers}}{}{{/request.headers}}.to_a - req.headers.to_a).empty?, req.headers.to_s) {{#body}} diff --git a/templates/scala/api.mustache b/templates/scala/api.mustache index c26df2e6f4b..eb1d9a8aa54 100644 --- a/templates/scala/api.mustache +++ b/templates/scala/api.mustache @@ -152,7 +152,7 @@ class {{classname}}( val request = HttpRequest.builder() .withMethod("{{httpMethod}}") - .withPath({{#vendorExtensions}}{{#x-is-custom-request}}s"{{{path}}}"{{/x-is-custom-request}}{{^x-is-custom-request}}s"{{#lambda.escape-path}}{{{path}}}{{/lambda.escape-path}}"{{/x-is-custom-request}}{{/vendorExtensions}}) + .withPath({{#vendorExtensions}}{{#x-is-custom-request}}s"{{{serverPathSuffix}}}{{{path}}}"{{/x-is-custom-request}}{{^x-is-custom-request}}s"{{{serverPathSuffix}}}{{#lambda.escape-path}}{{{path}}}{{/lambda.escape-path}}"{{/x-is-custom-request}}{{/vendorExtensions}}) {{#bodyParam}}.withBody({{paramName}}){{/bodyParam}} {{#headerParams}}.withHeader("{{baseName}}", {{paramName}}){{/headerParams}} {{#vendorExtensions.x-use-read-transporter}}.withRead(true){{/vendorExtensions.x-use-read-transporter}} diff --git a/templates/scala/enum.mustache b/templates/scala/enum.mustache index 8f16c46e780..f0777e06325 100644 --- a/templates/scala/enum.mustache +++ b/templates/scala/enum.mustache @@ -24,11 +24,11 @@ class {{classname}}Serializer extends CustomSerializer[{{classname}}](_ => ( { - case JString(value) => {{classname}}.withName(value) + {{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}case JString(value) => {{classname}}.withName(value){{/isString}}{{^isString}}case JInt(value) => {{classname}}.withName(value.toString){{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}} case JNull => null }, { case value: {{classname}} => - JString(value.toString) + {{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}JString(value.toString){{/isString}}{{^isString}}JInt(BigInt(value.toString)){{/isString}}{{/-first}}{{/enumVars}}{{/allowableValues}} } ) ) \ No newline at end of file diff --git a/templates/scala/oneof_trait.mustache b/templates/scala/oneof_trait.mustache index d3d118342cb..ef46c564b05 100644 --- a/templates/scala/oneof_trait.mustache +++ b/templates/scala/oneof_trait.mustache @@ -104,7 +104,19 @@ object {{classname}} { object {{classname}}Serializer extends Serializer[{{classname}}] { override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), {{classname}}] = { - case (TypeInfo(clazz, _), json) if clazz == classOf[{{classname}}] => + case (TypeInfo(clazz, _), json) if clazz == classOf[{{classname}}] => + {{#discriminator}} + (json match { + case jobject: JObject => + (jobject \ "{{{propertyBaseName}}}") match { + {{#mappedModels}} + case JString("{{{mappingName}}}") => Some(Extraction.extract[{{{modelName}}}](jobject)(format - this, manifest[{{{modelName}}}])) + {{/mappedModels}} + case _ => None + } + case _ => None + }).getOrElse { + {{/discriminator}} json match { {{#composedSchemas.oneOf}} {{#isModel}} @@ -115,6 +127,13 @@ object {{classname}}Serializer extends Serializer[{{classname}}] { {{#vendorExtensions.x-discriminator-fields.size}} case value: JObject {{#vendorExtensions.x-discriminator-fields}}{{#-first}}if{{/-first}}{{^-first}}&&{{/-first}} value.obj.exists(_._1 == "{{{.}}}"){{/vendorExtensions.x-discriminator-fields}} => Extraction.extract[{{{datatypeWithEnum}}}](value) {{/vendorExtensions.x-discriminator-fields.size}} + {{^vendorExtensions.x-discriminator-fields.size}} + {{#isMap}} + {{^isContainer}} + case value: JObject {{#vendorExtensions.x-discriminator-fields}}{{#-first}}if{{/-first}}{{^-first}}&&{{/-first}} value.obj.exists(_._1 == "{{{.}}}"){{/vendorExtensions.x-discriminator-fields}} => Extraction.extract[{{{datatypeWithEnum}}}](value) + {{/isContainer}} + {{/isMap}} + {{/vendorExtensions.x-discriminator-fields.size}} {{/isEnumRef}} {{/isModel}} {{#isMap}} @@ -148,6 +167,9 @@ object {{classname}}Serializer extends Serializer[{{classname}}] { {{/composedSchemas.oneOf}} case _ => throw new MappingException("Can't convert " + json + " to {{classname}}") } + {{#discriminator}} + } + {{/discriminator}} } override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = { case value: {{classname}} => diff --git a/templates/scala/tests/request_param.mustache b/templates/scala/tests/request_param.mustache index 0c5bacece1f..31030d474f4 100644 --- a/templates/scala/tests/request_param.mustache +++ b/templates/scala/tests/request_param.mustache @@ -1 +1 @@ -{{^hasAdditionalProperties}}{{#lambda.identifier}}{{key}}{{/lambda.identifier}} = {{> tests/param_optional}}{{/hasAdditionalProperties}}{{#hasAdditionalProperties}}{{> tests/param_value}}{{/hasAdditionalProperties}} \ No newline at end of file +{{^hasAdditionalProperties}}{{#isNull}}{{^required}}{{#lambda.identifier}}{{key}}{{/lambda.identifier}} = None{{/required}}{{#required}}{{#lambda.identifier}}{{key}}{{/lambda.identifier}} = {{> tests/param_optional}}{{/required}}{{/isNull}}{{^isNull}}{{#lambda.identifier}}{{key}}{{/lambda.identifier}} = {{> tests/param_optional}}{{/isNull}}{{/hasAdditionalProperties}}{{#hasAdditionalProperties}}{{> tests/param_value}}{{/hasAdditionalProperties}} \ No newline at end of file diff --git a/templates/scala/tests/requests/requests.mustache b/templates/scala/tests/requests/requests.mustache index c0b2e5a495c..6376e10dca8 100644 --- a/templates/scala/tests/requests/requests.mustache +++ b/templates/scala/tests/requests/requests.mustache @@ -46,7 +46,7 @@ class {{clientPrefix}}Test extends AnyFunSuite { Await.ready(future, Duration.Inf) val res = echo.lastResponse.get - assert(res.path == "{{{path}}}") + assert(res.path == "{{{serverPathSuffix}}}{{{path}}}") assert(res.method == "{{{method}}}") {{#body}} val expectedBody = parse("""{{{body}}}""") diff --git a/templates/swift/api.mustache b/templates/swift/api.mustache index b0d2b886ad2..441d3c2e4b1 100644 --- a/templates/swift/api.mustache +++ b/templates/swift/api.mustache @@ -112,7 +112,7 @@ import Foundation } {{/required}}{{/isString}}{{/queryParams}} - {{^pathParams}}let{{/pathParams}}{{#pathParams}}{{#-first}}var{{/-first}}{{/pathParams}} resourcePath = "{{{path}}}"{{#pathParams}} + {{^pathParams}}let{{/pathParams}}{{#pathParams}}{{#-first}}var{{/-first}}{{/pathParams}} resourcePath = "{{{serverPathSuffix}}}{{{path}}}"{{#pathParams}} let {{paramName}}PreEscape = "\({{#isEnum}}{{paramName}}{{#isContainer}}{{{dataType}}}{{/isContainer}}{{^isContainer}}.rawValue{{/isContainer}}{{/isEnum}}{{^isEnum}}APIHelper.mapValueToPathItem({{paramName}}){{/isEnum}})"{{^x-is-custom-request}} let {{paramName}}PostEscape = {{paramName}}PreEscape.addingPercentEncoding(withAllowedCharacters: .urlPathAlgoliaAllowed) ?? ""{{/x-is-custom-request}} resourcePath = resourcePath.replacingOccurrences(of: "{{=<% %>=}}{<%baseName%>}<%={{ }}=%>", with: {{^x-is-custom-request}}{{paramName}}PostEscape{{/x-is-custom-request}}{{#x-is-custom-request}}{{paramName}}PreEscape{{/x-is-custom-request}}, options: .literal, range: nil){{/pathParams}} diff --git a/templates/swift/modelOneOf.mustache b/templates/swift/modelOneOf.mustache index f8608593d9d..cc3a40d2394 100644 --- a/templates/swift/modelOneOf.mustache +++ b/templates/swift/modelOneOf.mustache @@ -15,6 +15,20 @@ public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() + {{#discriminator}} + if let jsonObject = try? container.decode([String: AnyCodable].self), + let discriminatorValue = jsonObject["{{{propertyBaseName}}}"]?.value as? String { + switch discriminatorValue { + {{#mappedModels}} + case "{{{mappingName}}}": + self = .{{#lambda.camelcase}}{{#lambda.type-to-name}}{{{modelName}}}{{/lambda.type-to-name}}{{/lambda.camelcase}}(try container.decode({{{modelName}}}{{#vendorExtensions.x-has-child-generic}}{{/vendorExtensions.x-has-child-generic}}.self)) + return + {{/mappedModels}} + default: + break + } + } + {{/discriminator}} {{#composedSchemas.oneOf}} {{#-first}} if let value = try? container.decode({{{datatypeWithEnum}}}{{#vendorExtensions.x-has-child-generic}}{{/vendorExtensions.x-has-child-generic}}.self) { diff --git a/templates/swift/tests/client/tests.mustache b/templates/swift/tests/client/tests.mustache index 1df06c93b3e..87d00c0ef29 100644 --- a/templates/swift/tests/client/tests.mustache +++ b/templates/swift/tests/client/tests.mustache @@ -53,7 +53,12 @@ {{/match.isPrimitive}} {{/isHelper}} {{^isHelper}} - XTCJSONEquals(received: response, expected: "{{#lambda.escapeQuotes}}{{{match.value}}}{{/lambda.escapeQuotes}}") + {{#match.isNull}} + XCTAssertTrue(response.value is Void) + {{/match.isNull}} + {{^match.isNull}} + XTCJSONEquals(received: response, expected: "{{#lambda.escapeQuotes}}{{{match.value}}}{{/lambda.escapeQuotes}}") + {{/match.isNull}} {{/isHelper}} {{/testResponse}} {{#shouldScope}} diff --git a/templates/swift/tests/paramValue.mustache b/templates/swift/tests/paramValue.mustache index 90462b8d176..b49da96127c 100644 --- a/templates/swift/tests/paramValue.mustache +++ b/templates/swift/tests/paramValue.mustache @@ -1 +1 @@ -{{#isVerbatim}}{{{value}}}{{/isVerbatim}}{{#isObject}}{{objectName}}({{^hasAdditionalProperties}}{{#value}}{{> tests/generateParams}}{{^-last}}, {{/-last}}{{/value}}{{/hasAdditionalProperties}}{{#hasAdditionalProperties}}from: [{{#value}}"{{key}}": AnyCodable({{> tests/paramValue }}){{^-last}}, {{/-last}}{{/value}}]{{/hasAdditionalProperties}}){{/isObject}}{{#isString}}{{#isAnyType}}AnyCodable({{/isAnyType}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}"{{#isAnyType}}){{/isAnyType}}{{/isString}}{{#isNumber}}{{#isLong}}Int64({{/isLong}}{{{value}}}{{#isLong}}){{/isLong}}{{/isNumber}}{{#isBoolean}}{{{value}}}{{/isBoolean}}{{#isEnum}}{{objectName}}.{{#lambda.identifier}}{{#lambda.camelcase}}{{value}}{{/lambda.camelcase}}{{/lambda.identifier}}{{/isEnum}}{{#isArray}}[{{#value}}{{> tests/generateParams}}{{^-last}}, {{/-last}}{{/value}}]{{/isArray}}{{#isMap}}Map[{{{.}}}]{{/isMap}}{{#isFreeFormObject}}{{#isAnyType}}[{{#value}}{{#entrySet}}"{{key}}": "{{{value}}}"{{/entrySet}}{{/value}}]{{/isAnyType}}{{^isAnyType}}{{^value}}[String: AnyCodable](){{/value}}{{#value}}{{#-first}}[{{/-first}}{{> tests/generateParams}}{{^-last}}, {{/-last}}{{#-last}}]{{/-last}}{{/value}}{{/isAnyType}}{{/isFreeFormObject}}{{#isNull}}{{#inClientTest}}TestNull{{{objectName}}}(){{/inClientTest}}{{/isNull}} \ No newline at end of file +{{#isVerbatim}}{{{value}}}{{/isVerbatim}}{{#isObject}}{{objectName}}({{^hasAdditionalProperties}}{{#value}}{{> tests/generateParams}}{{^-last}}, {{/-last}}{{/value}}{{/hasAdditionalProperties}}{{#hasAdditionalProperties}}from: [{{#value}}"{{key}}": AnyCodable({{> tests/paramValue }}){{^-last}}, {{/-last}}{{/value}}]{{/hasAdditionalProperties}}){{/isObject}}{{#isString}}{{#isAnyType}}AnyCodable({{/isAnyType}}"{{#lambda.escapeQuotes}}{{{value}}}{{/lambda.escapeQuotes}}"{{#isAnyType}}){{/isAnyType}}{{/isString}}{{#isNumber}}{{#isLong}}Int64({{/isLong}}{{{value}}}{{#isLong}}){{/isLong}}{{/isNumber}}{{#isBoolean}}{{{value}}}{{/isBoolean}}{{#isEnum}}{{^isIntegerEnum}}{{objectName}}.{{#lambda.identifier}}{{#lambda.camelcase}}{{value}}{{/lambda.camelcase}}{{/lambda.identifier}}{{/isIntegerEnum}}{{#isIntegerEnum}}{{objectName}}(rawValue: {{value}})!{{/isIntegerEnum}}{{/isEnum}}{{#isArray}}[{{#value}}{{> tests/generateParams}}{{^-last}}, {{/-last}}{{/value}}]{{/isArray}}{{#isMap}}Map[{{{.}}}]{{/isMap}}{{#isFreeFormObject}}{{#isAnyType}}[{{#value}}{{#entrySet}}"{{key}}": "{{{value}}}"{{/entrySet}}{{/value}}]{{/isAnyType}}{{^isAnyType}}{{^value}}[String: AnyCodable](){{/value}}{{#value}}{{#-first}}[{{/-first}}{{> tests/generateParams}}{{^-last}}, {{/-last}}{{#-last}}]{{/-last}}{{/value}}{{/isAnyType}}{{/isFreeFormObject}}{{#isNull}}{{#inClientTest}}TestNull{{{objectName}}}(){{/inClientTest}}{{^inClientTest}}nil{{/inClientTest}}{{/isNull}} \ No newline at end of file diff --git a/templates/swift/tests/requests/requests.mustache b/templates/swift/tests/requests/requests.mustache index 8465afd9e01..262bf9249a6 100644 --- a/templates/swift/tests/requests/requests.mustache +++ b/templates/swift/tests/requests/requests.mustache @@ -48,7 +48,7 @@ final class {{client}}RequestsTests: XCTestCase { {{/assertNullBody}} {{/body}} - XCTAssertEqual(echoResponse.path, "{{path}}") + XCTAssertEqual(echoResponse.path, "{{{serverPathSuffix}}}{{path}}") XCTAssertEqual(echoResponse.method, HTTPMethod.{{#lambda.lowercase}}{{&method}}{{/lambda.lowercase}}) {{#queryParameters}} diff --git a/tests/CTS/client/agent-studio/api.json b/tests/CTS/client/agent-studio/api.json new file mode 100644 index 00000000000..3bdca94a2a5 --- /dev/null +++ b/tests/CTS/client/agent-studio/api.json @@ -0,0 +1,39 @@ +[ + { + "testName": "calls api with default read timeouts", + "steps": [ + { + "type": "method", + "method": "listAgents", + "parameters": {}, + "expected": { + "type": "timeouts", + "match": { + "connectTimeout": 25000, + "responseTimeout": 25000 + } + } + } + ] + }, + { + "testName": "calls api with default write timeouts", + "steps": [ + { + "type": "method", + "method": "createAgent", + "parameters": { + "name": "test-agent", + "instructions": "test instructions" + }, + "expected": { + "type": "timeouts", + "match": { + "connectTimeout": 25000, + "responseTimeout": 25000 + } + } + } + ] + } +] diff --git a/tests/CTS/client/agent-studio/parameters.json b/tests/CTS/client/agent-studio/parameters.json new file mode 100644 index 00000000000..cf5ff9f9a70 --- /dev/null +++ b/tests/CTS/client/agent-studio/parameters.json @@ -0,0 +1,26 @@ +[ + { + "testName": "uses the correct host", + "autoCreateClient": false, + "steps": [ + { + "type": "createClient", + "parameters": { + "appId": "my-app-id", + "apiKey": "my-api-key" + } + }, + { + "type": "method", + "method": "customGet", + "parameters": { + "path": "test" + }, + "expected": { + "type": "host", + "match": "my-app-id-dsn.algolia.net" + } + } + ] + } +] diff --git a/tests/CTS/client/common/noContent.json b/tests/CTS/client/common/noContent.json new file mode 100644 index 00000000000..327a73933a2 --- /dev/null +++ b/tests/CTS/client/common/noContent.json @@ -0,0 +1,32 @@ +[ + { + "testName": "handles 204 No Content responses correctly", + "autoCreateClient": false, + "steps": [ + { + "type": "createClient", + "parameters": { + "appId": "test-app-id", + "apiKey": "test-api-key", + "region": "us", + "customHosts": [ + { + "port": 6692 + } + ] + } + }, + { + "type": "method", + "method": "customDelete", + "parameters": { + "path": "1/test/no-content" + }, + "expected": { + "type": "response", + "match": null + } + } + ] + } +] diff --git a/tests/CTS/requests/agent-studio/bulkCreateAllowedDomains.json b/tests/CTS/requests/agent-studio/bulkCreateAllowedDomains.json new file mode 100644 index 00000000000..49c324d6cce --- /dev/null +++ b/tests/CTS/requests/agent-studio/bulkCreateAllowedDomains.json @@ -0,0 +1,18 @@ +[ + { + "testName": "bulkCreateAllowedDomains", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "allowedDomainBulkInsert": { + "domains": ["https://app.example.com", "*.example.org"] + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains/bulk", + "method": "POST", + "body": { + "domains": ["https://app.example.com", "*.example.org"] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/bulkDeleteAllowedDomains.json b/tests/CTS/requests/agent-studio/bulkDeleteAllowedDomains.json new file mode 100644 index 00000000000..d3637d1c789 --- /dev/null +++ b/tests/CTS/requests/agent-studio/bulkDeleteAllowedDomains.json @@ -0,0 +1,18 @@ +[ + { + "testName": "bulkDeleteAllowedDomains", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "allowedDomainBulkDelete": { + "domainIds": ["a1b2c3d4-5678-90ab-cdef-123456789abc", "b2c3d4e5-6789-01ab-cdef-234567890abc"] + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains/bulk", + "method": "DELETE", + "body": { + "domainIds": ["a1b2c3d4-5678-90ab-cdef-123456789abc", "b2c3d4e5-6789-01ab-cdef-234567890abc"] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/consolidateMemories.json b/tests/CTS/requests/agent-studio/consolidateMemories.json new file mode 100644 index 00000000000..81a4affa003 --- /dev/null +++ b/tests/CTS/requests/agent-studio/consolidateMemories.json @@ -0,0 +1,25 @@ +[ + { + "testName": "consolidateMemories", + "parameters": { + "providerID": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4o-mini", + "existingMemories": [ + {"id": "mem-1", "text": "User likes dark mode"}, + {"id": "mem-2", "text": "User prefers dark themes"} + ] + }, + "request": { + "path": "/1/memory/consolidate", + "method": "POST", + "body": { + "providerID": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4o-mini", + "existingMemories": [ + {"id": "mem-1", "text": "User likes dark mode"}, + {"id": "mem-2", "text": "User prefers dark themes"} + ] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/createAgent.json b/tests/CTS/requests/agent-studio/createAgent.json new file mode 100644 index 00000000000..20a5e6a7d2c --- /dev/null +++ b/tests/CTS/requests/agent-studio/createAgent.json @@ -0,0 +1,54 @@ +[ + { + "testName": "createAgent with minimal parameters", + "parameters": { + "name": "test-agent", + "instructions": "You are a helpful assistant." + }, + "request": { + "path": "/1/agents", + "method": "POST", + "body": { + "name": "test-agent", + "instructions": "You are a helpful assistant." + } + } + }, + { + "testName": "createAgent with all parameters", + "parameters": { + "name": "test-agent", + "description": "A test agent for CTS", + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4", + "instructions": "You are a helpful assistant.", + "config": { + "sendUsage": true, + "sendReasoning": true, + "temperature": 0.7, + "max_tokens": 1500 + }, + "tools": [{ + "type": "start" + }] + }, + "request": { + "path": "/1/agents", + "method": "POST", + "body": { + "name": "test-agent", + "description": "A test agent for CTS", + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4", + "instructions": "You are a helpful assistant.", + "config": { + "sendUsage": true, + "sendReasoning": true, + "temperature": 0.7, + "max_tokens": 1500 + }, + "tools": [{ "type": "start" }] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/createAgentAllowedDomain.json b/tests/CTS/requests/agent-studio/createAgentAllowedDomain.json new file mode 100644 index 00000000000..ff174d1f9bb --- /dev/null +++ b/tests/CTS/requests/agent-studio/createAgentAllowedDomain.json @@ -0,0 +1,18 @@ +[ + { + "testName": "createAgentAllowedDomain", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "allowedDomainCreate": { + "domain": "https://app.example.com" + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains", + "method": "POST", + "body": { + "domain": "https://app.example.com" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/createAgentCompletion.json b/tests/CTS/requests/agent-studio/createAgentCompletion.json new file mode 100644 index 00000000000..383129cf5b2 --- /dev/null +++ b/tests/CTS/requests/agent-studio/createAgentCompletion.json @@ -0,0 +1,160 @@ +[ + { + "testName": "createAgentCompletion with v4 messages", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "compatibilityMode": "ai-sdk-4", + "agentCompletionRequest": { + "messages": [ + { + "role": "user", + "content": "Hello, how are you?" + } + ] + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/completions", + "method": "POST", + "queryParameters": { + "compatibilityMode": "ai-sdk-4" + }, + "body": { + "messages": [ + { + "role": "user", + "content": "Hello, how are you?" + } + ] + } + } + }, + { + "testName": "createAgentCompletion streaming with simple messages", + "isStreaming": true, + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "compatibilityMode": "ai-sdk-5", + "agentCompletionRequest": { + "messages": [ + { + "role": "user", + "content": "Hello, how are you?" + } + ] + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/completions", + "method": "POST", + "queryParameters": { + "compatibilityMode": "ai-sdk-5" + }, + "body": { + "messages": [ + { + "role": "user", + "content": "Hello, how are you?" + } + ] + } + }, + "skipLanguages": ["csharp", "dart", "go", "java", "kotlin", "php", "ruby", "scala", "swift"] + }, + { + "testName": "createAgentCompletion streaming with v5 messages and all query params", + "isStreaming": true, + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "compatibilityMode": "ai-sdk-5", + "agentCompletionRequest": { + "messages": [ + { + "role": "user", + "parts": [ + { + "type": "text", + "text": "What is Algolia?" + } + ] + } + ], + "id": "test-conversation-id" + }, + "stream": false, + "cache": false, + "memory": false + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/completions", + "method": "POST", + "queryParameters": { + "compatibilityMode": "ai-sdk-5", + "stream": "false", + "cache": "false", + "memory": "false" + }, + "body": { + "messages": [ + { + "role": "user", + "parts": [ + { + "type": "text", + "text": "What is Algolia?" + } + ] + } + ], + "id": "test-conversation-id" + } + }, + "skipLanguages": ["csharp", "dart", "go", "java", "kotlin", "php", "ruby", "scala", "swift"] + }, + { + "testName": "createAgentCompletion streaming with test configuration", + "isStreaming": true, + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "compatibilityMode": "ai-sdk-5", + "agentCompletionRequest": { + "messages": [ + { + "role": "user", + "content": "Hello" + } + ], + "configuration": { + "instructions": "Test instructions override", + "config": { + "temperature": 0.2 + }, + "tools": [{ "type": "start" }] + } + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/completions", + "method": "POST", + "queryParameters": { + "compatibilityMode": "ai-sdk-5" + }, + "body": { + "messages": [ + { + "role": "user", + "content": "Hello" + } + ], + "configuration": { + "instructions": "Test instructions override", + "config": { + "temperature": 0.2 + }, + "tools": [{ "type": "start" }] + } + } + }, + "skipLanguages": ["csharp", "dart", "go", "java", "kotlin", "php", "ruby", "scala", "swift"] + } +] diff --git a/tests/CTS/requests/agent-studio/createFeedback.json b/tests/CTS/requests/agent-studio/createFeedback.json new file mode 100644 index 00000000000..642e566cfc9 --- /dev/null +++ b/tests/CTS/requests/agent-studio/createFeedback.json @@ -0,0 +1,40 @@ +[ + { + "testName": "createFeedback with required parameters", + "parameters": { + "messageId": "msg-abc123", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "vote": 1 + }, + "request": { + "path": "/1/feedback", + "method": "POST", + "body": { + "messageId": "msg-abc123", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "vote": 1 + } + } + }, + { + "testName": "createFeedback with all parameters", + "parameters": { + "messageId": "msg-abc123", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "vote": 0, + "tags": ["unhelpful", "off-topic"], + "notes": "The response did not address my question." + }, + "request": { + "path": "/1/feedback", + "method": "POST", + "body": { + "messageId": "msg-abc123", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "vote": 0, + "tags": ["unhelpful", "off-topic"], + "notes": "The response did not address my question." + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/createProvider.json b/tests/CTS/requests/agent-studio/createProvider.json new file mode 100644 index 00000000000..aa4a01b0c38 --- /dev/null +++ b/tests/CTS/requests/agent-studio/createProvider.json @@ -0,0 +1,48 @@ +[ + { + "testName": "createProvider with OpenAI", + "parameters": { + "name": "My OpenAI Provider", + "providerName": "openai", + "input": { + "apiKey": "sk-test-key-1234" + } + }, + "request": { + "path": "/1/providers", + "method": "POST", + "body": { + "name": "My OpenAI Provider", + "providerName": "openai", + "input": { + "apiKey": "sk-test-key-1234" + } + } + } + }, + { + "testName": "createProvider with Azure OpenAI", + "parameters": { + "name": "My Azure Provider", + "providerName": "azure_openai", + "input": { + "apiKey": "az-test-key-5678", + "azureEndpoint": "https://my-resource.openai.azure.com", + "azureDeployment": "gpt-4o" + } + }, + "request": { + "path": "/1/providers", + "method": "POST", + "body": { + "name": "My Azure Provider", + "providerName": "azure_openai", + "input": { + "apiKey": "az-test-key-5678", + "azureEndpoint": "https://my-resource.openai.azure.com", + "azureDeployment": "gpt-4o" + } + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/createSecretKey.json b/tests/CTS/requests/agent-studio/createSecretKey.json new file mode 100644 index 00000000000..92ff509100f --- /dev/null +++ b/tests/CTS/requests/agent-studio/createSecretKey.json @@ -0,0 +1,30 @@ +[ + { + "testName": "createSecretKey with minimal parameters", + "parameters": { + "name": "my-secret-key" + }, + "request": { + "path": "/1/secret-keys", + "method": "POST", + "body": { + "name": "my-secret-key" + } + } + }, + { + "testName": "createSecretKey with agent IDs", + "parameters": { + "name": "scoped-key", + "agentIds": ["785b14e4-61a8-4c03-8028-bf2ba3ae85ff"] + }, + "request": { + "path": "/1/secret-keys", + "method": "POST", + "body": { + "name": "scoped-key", + "agentIds": ["785b14e4-61a8-4c03-8028-bf2ba3ae85ff"] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteAgent.json b/tests/CTS/requests/agent-studio/deleteAgent.json new file mode 100644 index 00000000000..1da574a4c91 --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteAgent.json @@ -0,0 +1,12 @@ +[ + { + "testName": "deleteAgent", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteAgentConversations.json b/tests/CTS/requests/agent-studio/deleteAgentConversations.json new file mode 100644 index 00000000000..0729d1d04d9 --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteAgentConversations.json @@ -0,0 +1,28 @@ +[ + { + "testName": "deleteAgentConversations without filters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations", + "method": "DELETE" + } + }, + { + "testName": "deleteAgentConversations with date filters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "startDate": "2024-01-01", + "endDate": "2024-06-30" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations", + "method": "DELETE", + "queryParameters": { + "startDate": "2024-01-01", + "endDate": "2024-06-30" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteAllowedDomain.json b/tests/CTS/requests/agent-studio/deleteAllowedDomain.json new file mode 100644 index 00000000000..a81b9d7f87e --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteAllowedDomain.json @@ -0,0 +1,13 @@ +[ + { + "testName": "deleteAllowedDomain", + "parameters": { + "domainId": "a1b2c3d4-5678-90ab-cdef-123456789abc", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteConversation.json b/tests/CTS/requests/agent-studio/deleteConversation.json new file mode 100644 index 00000000000..aba88308653 --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteConversation.json @@ -0,0 +1,13 @@ +[ + { + "testName": "deleteConversation", + "parameters": { + "conversationId": "test-conversation-id", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations/test-conversation-id", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteMemory.json b/tests/CTS/requests/agent-studio/deleteMemory.json new file mode 100644 index 00000000000..f98a71d8ce3 --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteMemory.json @@ -0,0 +1,12 @@ +[ + { + "testName": "deleteMemory", + "parameters": { + "memoryId": "a1b2c3d4-5678-90ab-cdef-123456789abc" + }, + "request": { + "path": "/1/memory/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteProvider.json b/tests/CTS/requests/agent-studio/deleteProvider.json new file mode 100644 index 00000000000..31c79d0f799 --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteProvider.json @@ -0,0 +1,12 @@ +[ + { + "testName": "deleteProvider", + "parameters": { + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a" + }, + "request": { + "path": "/1/providers/5e33d805-7892-4329-bda7-b524dc3fd04a", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteSecretKey.json b/tests/CTS/requests/agent-studio/deleteSecretKey.json new file mode 100644 index 00000000000..4faa149584e --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteSecretKey.json @@ -0,0 +1,12 @@ +[ + { + "testName": "deleteSecretKey", + "parameters": { + "secretKeyId": "a1b2c3d4-5678-90ab-cdef-123456789abc" + }, + "request": { + "path": "/1/secret-keys/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/requests/agent-studio/deleteUserData.json b/tests/CTS/requests/agent-studio/deleteUserData.json new file mode 100644 index 00000000000..108ded1d9a8 --- /dev/null +++ b/tests/CTS/requests/agent-studio/deleteUserData.json @@ -0,0 +1,12 @@ +[ + { + "testName": "deleteUserData", + "parameters": { + "userToken": "test-user-token" + }, + "request": { + "path": "/1/user-data/test-user-token", + "method": "DELETE" + } + } +] diff --git a/tests/CTS/requests/agent-studio/exportConversations.json b/tests/CTS/requests/agent-studio/exportConversations.json new file mode 100644 index 00000000000..0ab754bce8a --- /dev/null +++ b/tests/CTS/requests/agent-studio/exportConversations.json @@ -0,0 +1,41 @@ +[ + { + "testName": "exportConversations without filters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations/export", + "method": "GET" + } + }, + { + "testName": "exportConversations with date filters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "startDate": "2024-01-01", + "endDate": "2024-12-31" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations/export", + "method": "GET", + "queryParameters": { + "startDate": "2024-01-01", + "endDate": "2024-12-31" + } + } + }, + { + "testName": "e2e export conversations", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations/export", + "method": "GET" + }, + "response": { + "statusCode": 200 + } + } +] diff --git a/tests/CTS/requests/agent-studio/extractEpisodicMemories.json b/tests/CTS/requests/agent-studio/extractEpisodicMemories.json new file mode 100644 index 00000000000..37804548aa6 --- /dev/null +++ b/tests/CTS/requests/agent-studio/extractEpisodicMemories.json @@ -0,0 +1,23 @@ +[ + { + "testName": "extractEpisodicMemories", + "parameters": { + "providerID": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4o-mini", + "messages": [ + {"role": "user", "content": "I had a great experience with the product"} + ] + }, + "request": { + "path": "/1/memory/extract/episodic", + "method": "POST", + "body": { + "providerID": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4o-mini", + "messages": [ + {"role": "user", "content": "I had a great experience with the product"} + ] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/extractQueries.json b/tests/CTS/requests/agent-studio/extractQueries.json new file mode 100644 index 00000000000..a33a6bce932 --- /dev/null +++ b/tests/CTS/requests/agent-studio/extractQueries.json @@ -0,0 +1,23 @@ +[ + { + "testName": "extractQueries", + "parameters": { + "messages": [ + {"role": "user", "content": "What running shoes do you recommend for marathons?"}, + {"role": "assistant", "content": "I'd suggest looking at stability shoes with good cushioning."}, + {"role": "user", "content": "I overpronate, so I need extra support."} + ] + }, + "request": { + "path": "/1/memory/extract/queries", + "method": "POST", + "body": { + "messages": [ + {"role": "user", "content": "What running shoes do you recommend for marathons?"}, + {"role": "assistant", "content": "I'd suggest looking at stability shoes with good cushioning."}, + {"role": "user", "content": "I overpronate, so I need extra support."} + ] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/extractSemanticMemories.json b/tests/CTS/requests/agent-studio/extractSemanticMemories.json new file mode 100644 index 00000000000..b8cc8daa121 --- /dev/null +++ b/tests/CTS/requests/agent-studio/extractSemanticMemories.json @@ -0,0 +1,23 @@ +[ + { + "testName": "extractSemanticMemories", + "parameters": { + "providerID": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4o-mini", + "messages": [ + {"role": "user", "content": "I prefer dark mode and minimal UI"} + ] + }, + "request": { + "path": "/1/memory/extract/semantic", + "method": "POST", + "body": { + "providerID": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "model": "gpt-4o-mini", + "messages": [ + {"role": "user", "content": "I prefer dark mode and minimal UI"} + ] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/getAgent.json b/tests/CTS/requests/agent-studio/getAgent.json new file mode 100644 index 00000000000..e86fe665a41 --- /dev/null +++ b/tests/CTS/requests/agent-studio/getAgent.json @@ -0,0 +1,31 @@ +[ + { + "testName": "getAgent", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "method": "GET" + } + }, + { + "testName": "e2e get agent", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "id": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "name": "cts_e2e_agent", + "status": "published", + "instructions": "You are a helpful assistant for CTS e2e testing." + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/getAllowedDomain.json b/tests/CTS/requests/agent-studio/getAllowedDomain.json new file mode 100644 index 00000000000..ec07b5421af --- /dev/null +++ b/tests/CTS/requests/agent-studio/getAllowedDomain.json @@ -0,0 +1,32 @@ +[ + { + "testName": "getAllowedDomain", + "parameters": { + "domainId": "a1b2c3d4-5678-90ab-cdef-123456789abc", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "GET" + } + }, + { + "testName": "e2e get allowed domain", + "parameters": { + "domainId": "6486b782-6b4c-4c93-bbba-04849ae4d101", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains/6486b782-6b4c-4c93-bbba-04849ae4d101", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "id": "6486b782-6b4c-4c93-bbba-04849ae4d101", + "domain": "cts-e2e.algolia.com", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/getConfiguration.json b/tests/CTS/requests/agent-studio/getConfiguration.json new file mode 100644 index 00000000000..257f3b17d91 --- /dev/null +++ b/tests/CTS/requests/agent-studio/getConfiguration.json @@ -0,0 +1,23 @@ +[ + { + "parameters": {}, + "request": { + "path": "/1/configuration", + "method": "GET" + } + }, + { + "testName": "e2e getConfiguration", + "parameters": {}, + "request": { + "path": "/1/configuration", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "maxRetentionDays": 90 + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/getConversation.json b/tests/CTS/requests/agent-studio/getConversation.json new file mode 100644 index 00000000000..6c5eab03700 --- /dev/null +++ b/tests/CTS/requests/agent-studio/getConversation.json @@ -0,0 +1,32 @@ +[ + { + "testName": "getConversation", + "parameters": { + "conversationId": "test-conversation-id", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations/test-conversation-id", + "method": "GET" + } + }, + { + "testName": "e2e get conversation", + "parameters": { + "conversationId": "dashboard-eacdd4aa-9a80-4f5b-b657-f04b7fd34082", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations/dashboard-eacdd4aa-9a80-4f5b-b657-f04b7fd34082", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "id": "dashboard-eacdd4aa-9a80-4f5b-b657-f04b7fd34082", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "title": "Greeting Message" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/getMemory.json b/tests/CTS/requests/agent-studio/getMemory.json new file mode 100644 index 00000000000..6897de04b8e --- /dev/null +++ b/tests/CTS/requests/agent-studio/getMemory.json @@ -0,0 +1,12 @@ +[ + { + "testName": "getMemory", + "parameters": { + "memoryId": "a1b2c3d4-5678-90ab-cdef-123456789abc" + }, + "request": { + "path": "/1/memory/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "GET" + } + } +] diff --git a/tests/CTS/requests/agent-studio/getProvider.json b/tests/CTS/requests/agent-studio/getProvider.json new file mode 100644 index 00000000000..4510dc1bc9d --- /dev/null +++ b/tests/CTS/requests/agent-studio/getProvider.json @@ -0,0 +1,30 @@ +[ + { + "testName": "getProvider", + "parameters": { + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a" + }, + "request": { + "path": "/1/providers/5e33d805-7892-4329-bda7-b524dc3fd04a", + "method": "GET" + } + }, + { + "testName": "e2e get provider", + "parameters": { + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a" + }, + "request": { + "path": "/1/providers/5e33d805-7892-4329-bda7-b524dc3fd04a", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "id": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "name": "cts_e2e_provider", + "providerName": "openai" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/getSecretKey.json b/tests/CTS/requests/agent-studio/getSecretKey.json new file mode 100644 index 00000000000..1aa341d9478 --- /dev/null +++ b/tests/CTS/requests/agent-studio/getSecretKey.json @@ -0,0 +1,29 @@ +[ + { + "testName": "getSecretKey", + "parameters": { + "secretKeyId": "a1b2c3d4-5678-90ab-cdef-123456789abc" + }, + "request": { + "path": "/1/secret-keys/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "GET" + } + }, + { + "testName": "e2e get secret key", + "parameters": { + "secretKeyId": "f730dda4-9649-4cfb-9d88-b7e992fb89c9" + }, + "request": { + "path": "/1/secret-keys/f730dda4-9649-4cfb-9d88-b7e992fb89c9", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "id": "f730dda4-9649-4cfb-9d88-b7e992fb89c9", + "name": "cts_e2e_secret_key" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/getUserData.json b/tests/CTS/requests/agent-studio/getUserData.json new file mode 100644 index 00000000000..e6413dbf80f --- /dev/null +++ b/tests/CTS/requests/agent-studio/getUserData.json @@ -0,0 +1,12 @@ +[ + { + "testName": "getUserData", + "parameters": { + "userToken": "test-user-token" + }, + "request": { + "path": "/1/user-data/test-user-token", + "method": "GET" + } + } +] diff --git a/tests/CTS/requests/agent-studio/invalidateAgentCache.json b/tests/CTS/requests/agent-studio/invalidateAgentCache.json new file mode 100644 index 00000000000..872d10807bd --- /dev/null +++ b/tests/CTS/requests/agent-studio/invalidateAgentCache.json @@ -0,0 +1,26 @@ +[ + { + "testName": "invalidateAgentCache without before", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/cache", + "method": "DELETE" + } + }, + { + "testName": "invalidateAgentCache with before date", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "before": "2024-12-01" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/cache", + "method": "DELETE", + "queryParameters": { + "before": "2024-12-01" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/listAgentAllowedDomains.json b/tests/CTS/requests/agent-studio/listAgentAllowedDomains.json new file mode 100644 index 00000000000..a7e42b30649 --- /dev/null +++ b/tests/CTS/requests/agent-studio/listAgentAllowedDomains.json @@ -0,0 +1,33 @@ +[ + { + "testName": "listAgentAllowed-Domains", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains", + "method": "GET" + } + }, + { + "testName": "e2e list agent allowed domains", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/allowed-domains", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "domains": [ + { + "id": "6486b782-6b4c-4c93-bbba-04849ae4d101", + "domain": "cts-e2e.algolia.com" + } + ] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/listAgentConversations.json b/tests/CTS/requests/agent-studio/listAgentConversations.json new file mode 100644 index 00000000000..7681f18db6a --- /dev/null +++ b/tests/CTS/requests/agent-studio/listAgentConversations.json @@ -0,0 +1,71 @@ +[ + { + "testName": "listAgentConversations with minimal parameters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations", + "method": "GET" + } + }, + { + "testName": "listAgentConversations with all parameters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "startDate": "2024-01-01", + "endDate": "2024-12-31", + "includeFeedback": true, + "feedbackVote": 1, + "page": 2, + "limit": 10, + "xAlgoliaSecureUserToken": null + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations", + "method": "GET", + "queryParameters": { + "startDate": "2024-01-01", + "endDate": "2024-12-31", + "includeFeedback": "true", + "feedbackVote": "1", + "page": "2", + "limit": "10" + } + }, + "response": { + "statusCode": 200, + "body": { + "data": [], + "pagination": { + "page": 2, + "limit": 10, + "totalCount": 0, + "totalPages": 0 + } + } + } + }, + { + "testName": "e2e list agent conversations", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/conversations", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "data": [ + { + "id": "dashboard-eacdd4aa-9a80-4f5b-b657-f04b7fd34082", + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "title": "Greeting Message" + } + ] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/listAgents.json b/tests/CTS/requests/agent-studio/listAgents.json new file mode 100644 index 00000000000..d063eefc7bc --- /dev/null +++ b/tests/CTS/requests/agent-studio/listAgents.json @@ -0,0 +1,58 @@ +[ + { + "testName": "list agents with no params", + "parameters": {}, + "request": { + "path": "/1/agents", + "method": "GET" + } + }, + { + "testName": "list agents with all parameters", + "parameters": { + "page": 2, + "limit": 5, + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a" + }, + "request": { + "path": "/1/agents", + "method": "GET", + "queryParameters": { + "page": "2", + "limit": "5", + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a" + } + } + }, + { + "testName": "e2e list agents", + "parameters": { + "page": 1, + "limit": 2, + "providerId": null + }, + "request": { + "path": "/1/agents", + "method": "GET", + "queryParameters": { + "page": "1", + "limit": "2" + } + }, + "response": { + "statusCode": 200, + "body": { + "data": [ + { + "name": "cts_e2e_agent", + "status": "published" + } + ], + "pagination": { + "page": 1, + "limit": 2 + } + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/listMemories.json b/tests/CTS/requests/agent-studio/listMemories.json new file mode 100644 index 00000000000..95a8a7f11b4 --- /dev/null +++ b/tests/CTS/requests/agent-studio/listMemories.json @@ -0,0 +1,33 @@ +[ + { + "testName": "listMemories with no params", + "parameters": {}, + "request": { + "path": "/1/memory", + "method": "GET" + } + }, + { + "testName": "listMemories with all parameters", + "parameters": { + "agentIds": "agent1,agent2", + "page": 2, + "limit": 10, + "memoryType": "semantic", + "startDate": "2024-01-01", + "endDate": "2024-12-31" + }, + "request": { + "path": "/1/memory", + "method": "GET", + "queryParameters": { + "agentIds": "agent1%2Cagent2", + "page": "2", + "limit": "10", + "memoryType": "semantic", + "startDate": "2024-01-01", + "endDate": "2024-12-31" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/listModels.json b/tests/CTS/requests/agent-studio/listModels.json new file mode 100644 index 00000000000..209ce630f01 --- /dev/null +++ b/tests/CTS/requests/agent-studio/listModels.json @@ -0,0 +1,23 @@ +[ + { + "parameters": {}, + "request": { + "path": "/1/providers/models", + "method": "GET" + } + }, + { + "testName": "e2e list models", + "parameters": {}, + "request": { + "path": "/1/providers/models", + "method": "GET" + }, + "response": { + "statusCode": 200, + "body": { + "openai": ["gpt-4"] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/listProviderModels.json b/tests/CTS/requests/agent-studio/listProviderModels.json new file mode 100644 index 00000000000..35ee91704cd --- /dev/null +++ b/tests/CTS/requests/agent-studio/listProviderModels.json @@ -0,0 +1,25 @@ +[ + { + "testName": "listProviderModels", + "parameters": { + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a" + }, + "request": { + "path": "/1/providers/5e33d805-7892-4329-bda7-b524dc3fd04a/models", + "method": "GET" + } + }, + { + "testName": "e2e list provider models", + "parameters": { + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a" + }, + "request": { + "path": "/1/providers/5e33d805-7892-4329-bda7-b524dc3fd04a/models", + "method": "GET" + }, + "response": { + "statusCode": 200 + } + } +] diff --git a/tests/CTS/requests/agent-studio/listProviders.json b/tests/CTS/requests/agent-studio/listProviders.json new file mode 100644 index 00000000000..9e7feb0927f --- /dev/null +++ b/tests/CTS/requests/agent-studio/listProviders.json @@ -0,0 +1,54 @@ +[ + { + "testName": "listProviders with no params", + "parameters": {}, + "request": { + "path": "/1/providers", + "method": "GET" + } + }, + { + "testName": "listProviders with pagination", + "parameters": { + "page": 2, + "limit": 5 + }, + "request": { + "path": "/1/providers", + "method": "GET", + "queryParameters": { + "page": "2", + "limit": "5" + } + } + }, + { + "testName": "e2e list providers", + "parameters": { + "page": 1, + "limit": 2 + }, + "request": { + "path": "/1/providers", + "method": "GET", + "queryParameters": { + "page": "1", + "limit": "2" + } + }, + "response": { + "statusCode": 200, + "body": { + "data": [ + { + "name": "cts_e2e_provider" + } + ], + "pagination": { + "page": 1, + "limit": 2 + } + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/listSecretKeys.json b/tests/CTS/requests/agent-studio/listSecretKeys.json new file mode 100644 index 00000000000..1c1280fbdd5 --- /dev/null +++ b/tests/CTS/requests/agent-studio/listSecretKeys.json @@ -0,0 +1,54 @@ +[ + { + "testName": "listSecretKeys with no params", + "parameters": {}, + "request": { + "path": "/1/secret-keys", + "method": "GET" + } + }, + { + "testName": "listSecretKeys with pagination", + "parameters": { + "page": 2, + "limit": 5 + }, + "request": { + "path": "/1/secret-keys", + "method": "GET", + "queryParameters": { + "page": "2", + "limit": "5" + } + } + }, + { + "testName": "e2e list secret keys", + "parameters": { + "page": 1, + "limit": 10 + }, + "request": { + "path": "/1/secret-keys", + "method": "GET", + "queryParameters": { + "page": "1", + "limit": "10" + } + }, + "response": { + "statusCode": 200, + "body": { + "data": [ + { + "name": "cts_e2e_secret_key" + } + ], + "pagination": { + "page": 1, + "limit": 10 + } + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/publishAgent.json b/tests/CTS/requests/agent-studio/publishAgent.json new file mode 100644 index 00000000000..cc6692793f2 --- /dev/null +++ b/tests/CTS/requests/agent-studio/publishAgent.json @@ -0,0 +1,12 @@ +[ + { + "testName": "publishAgent", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/publish", + "method": "POST" + } + } +] diff --git a/tests/CTS/requests/agent-studio/saveEpisodicMemory.json b/tests/CTS/requests/agent-studio/saveEpisodicMemory.json new file mode 100644 index 00000000000..69459dc41fb --- /dev/null +++ b/tests/CTS/requests/agent-studio/saveEpisodicMemory.json @@ -0,0 +1,25 @@ +[ + { + "testName": "saveEpisodicMemory", + "parameters": { + "observation": "User asked about marathon shoes", + "thoughts": "User seems to be a runner with overpronation issues", + "action": "Recommended stability shoes with cushioning", + "result": "User found the suggestion helpful", + "text": "Recommended stability running shoes for marathon runner with overpronation", + "rawExtract": "User: What running shoes for marathons? Assistant: Stability shoes with cushioning." + }, + "request": { + "path": "/1/memory/episodic", + "method": "POST", + "body": { + "observation": "User asked about marathon shoes", + "thoughts": "User seems to be a runner with overpronation issues", + "action": "Recommended stability shoes with cushioning", + "result": "User found the suggestion helpful", + "text": "Recommended stability running shoes for marathon runner with overpronation", + "rawExtract": "User: What running shoes for marathons? Assistant: Stability shoes with cushioning." + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/saveSemanticMemory.json b/tests/CTS/requests/agent-studio/saveSemanticMemory.json new file mode 100644 index 00000000000..db74427e109 --- /dev/null +++ b/tests/CTS/requests/agent-studio/saveSemanticMemory.json @@ -0,0 +1,17 @@ +[ + { + "testName": "saveSemanticMemory", + "parameters": { + "text": "User prefers dark mode and minimal UI design", + "rawExtract": "User: I prefer dark mode and minimal UI" + }, + "request": { + "path": "/1/memory/semantic", + "method": "POST", + "body": { + "text": "User prefers dark mode and minimal UI design", + "rawExtract": "User: I prefer dark mode and minimal UI" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/searchMemories.json b/tests/CTS/requests/agent-studio/searchMemories.json new file mode 100644 index 00000000000..bd68892b425 --- /dev/null +++ b/tests/CTS/requests/agent-studio/searchMemories.json @@ -0,0 +1,15 @@ +[ + { + "testName": "searchMemories", + "parameters": { + "queries": ["What does the user prefer?"] + }, + "request": { + "path": "/1/memory/search", + "method": "POST", + "body": { + "queries": ["What does the user prefer?"] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/unpublishAgent.json b/tests/CTS/requests/agent-studio/unpublishAgent.json new file mode 100644 index 00000000000..4a045321432 --- /dev/null +++ b/tests/CTS/requests/agent-studio/unpublishAgent.json @@ -0,0 +1,12 @@ +[ + { + "testName": "unpublishAgent", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff" + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff/unpublish", + "method": "POST" + } + } +] diff --git a/tests/CTS/requests/agent-studio/updateAgent.json b/tests/CTS/requests/agent-studio/updateAgent.json new file mode 100644 index 00000000000..0635b445388 --- /dev/null +++ b/tests/CTS/requests/agent-studio/updateAgent.json @@ -0,0 +1,50 @@ +[ + { + "testName": "updateAgent with minimal parameters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "agentConfigUpdate": { + "name": "updated-agent-name" + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "method": "PATCH", + "body": { + "name": "updated-agent-name" + } + } + }, + { + "testName": "updateAgent with all parameters", + "parameters": { + "agentId": "785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "agentConfigUpdate": { + "name": "updated-agent", + "description": "Updated description", + "providerId": "new-provider-id", + "model": "gpt-4o", + "instructions": "Updated instructions.", + "config": { + "temperature": 0.5 + }, + "tools": [{ "type": "start" }] + } + }, + "request": { + "path": "/1/agents/785b14e4-61a8-4c03-8028-bf2ba3ae85ff", + "method": "PATCH", + "body": { + "name": "updated-agent", + "description": "Updated description", + "providerId": "new-provider-id", + "model": "gpt-4o", + "instructions": "Updated instructions.", + "config": { + "temperature": 0.5 + }, + "tools": [{ "type": "start" }] + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/updateConfiguration.json b/tests/CTS/requests/agent-studio/updateConfiguration.json new file mode 100644 index 00000000000..91930a1770e --- /dev/null +++ b/tests/CTS/requests/agent-studio/updateConfiguration.json @@ -0,0 +1,15 @@ +[ + { + "testName": "updateConfiguration", + "parameters": { + "maxRetentionDays": 30 + }, + "request": { + "path": "/1/configuration", + "method": "PATCH", + "body": { + "maxRetentionDays": 30 + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/updateMemory.json b/tests/CTS/requests/agent-studio/updateMemory.json new file mode 100644 index 00000000000..3d732bbfa27 --- /dev/null +++ b/tests/CTS/requests/agent-studio/updateMemory.json @@ -0,0 +1,18 @@ +[ + { + "testName": "updateMemory", + "parameters": { + "memoryId": "a1b2c3d4-5678-90ab-cdef-123456789abc", + "memoryUpdateRequest": { + "text": "Updated memory content" + } + }, + "request": { + "path": "/1/memory/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "PATCH", + "body": { + "text": "Updated memory content" + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/updateProvider.json b/tests/CTS/requests/agent-studio/updateProvider.json new file mode 100644 index 00000000000..92e1546b425 --- /dev/null +++ b/tests/CTS/requests/agent-studio/updateProvider.json @@ -0,0 +1,40 @@ +[ + { + "testName": "updateProvider", + "parameters": { + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "providerAuthenticationPatch": { + "name": "Updated Provider Name" + } + }, + "request": { + "path": "/1/providers/5e33d805-7892-4329-bda7-b524dc3fd04a", + "method": "PATCH", + "body": { + "name": "Updated Provider Name" + } + } + }, + { + "testName": "updateProvider with input", + "parameters": { + "providerId": "5e33d805-7892-4329-bda7-b524dc3fd04a", + "providerAuthenticationPatch": { + "name": "Updated Provider", + "input": { + "apiKey": "sk-new-key-5678" + } + } + }, + "request": { + "path": "/1/providers/5e33d805-7892-4329-bda7-b524dc3fd04a", + "method": "PATCH", + "body": { + "name": "Updated Provider", + "input": { + "apiKey": "sk-new-key-5678" + } + } + } + } +] diff --git a/tests/CTS/requests/agent-studio/updateSecretKey.json b/tests/CTS/requests/agent-studio/updateSecretKey.json new file mode 100644 index 00000000000..73d5a9fda1e --- /dev/null +++ b/tests/CTS/requests/agent-studio/updateSecretKey.json @@ -0,0 +1,20 @@ +[ + { + "testName": "updateSecretKey", + "parameters": { + "secretKeyId": "a1b2c3d4-5678-90ab-cdef-123456789abc", + "secretKeyPatch": { + "name": "renamed-key", + "agentIds": ["785b14e4-61a8-4c03-8028-bf2ba3ae85ff"] + } + }, + "request": { + "path": "/1/secret-keys/a1b2c3d4-5678-90ab-cdef-123456789abc", + "method": "PATCH", + "body": { + "name": "renamed-key", + "agentIds": ["785b14e4-61a8-4c03-8028-bf2ba3ae85ff"] + } + } + } +] diff --git a/tests/output/csharp/src/LoggingTests.cs b/tests/output/csharp/src/LoggingTests.cs index 05a9aa568c9..b72b90d365b 100644 --- a/tests/output/csharp/src/LoggingTests.cs +++ b/tests/output/csharp/src/LoggingTests.cs @@ -64,7 +64,12 @@ private HttpTransport CreateTransportWithHosts(params string[] hosts) var config = new SearchConfig("test-app-id", "test-api-key") { CustomHosts = hosts - .Select(h => new StatefulHost { Url = h, Accept = CallType.Read | CallType.Write, Up = true }) + .Select(h => new StatefulHost + { + Url = h, + Accept = CallType.Read | CallType.Write, + Up = true, + }) .ToList(), ConnectTimeout = TimeSpan.FromSeconds(ConnectTimeoutSeconds), }; @@ -77,7 +82,12 @@ private HttpTransport CreateTransportWithMockHttp(IHttpRequester mockHttp) { CustomHosts = new List { - new() { Url = "localhost", Accept = CallType.Read | CallType.Write, Up = true }, + new() + { + Url = "localhost", + Accept = CallType.Read | CallType.Write, + Up = true, + }, }, }; return new HttpTransport(config, mockHttp, _loggerFactory); @@ -127,11 +137,12 @@ public async Task InfoRequestSummaryFormat() ) .ReturnsAsync(OkResponse()); - await CreateTransportWithMockHttp(mockHttp.Object).ExecuteRequestAsync( - HttpMethod.Get, - "/1/test/instant", - new InternalRequestOptions { UseReadTransporter = true } - ); + await CreateTransportWithMockHttp(mockHttp.Object) + .ExecuteRequestAsync( + HttpMethod.Get, + "/1/test/instant", + new InternalRequestOptions { UseReadTransporter = true } + ); AssertLogMatches( LogLevel.Information, @@ -155,11 +166,12 @@ public async Task DebugRequestResponseDetails() ) .ReturnsAsync(OkResponse()); - await CreateTransportWithMockHttp(mockHttp.Object).ExecuteRequestAsync( - HttpMethod.Get, - "/1/test/instant", - new InternalRequestOptions { UseReadTransporter = true } - ); + await CreateTransportWithMockHttp(mockHttp.Object) + .ExecuteRequestAsync( + HttpMethod.Get, + "/1/test/instant", + new InternalRequestOptions { UseReadTransporter = true } + ); var traceMessages = GetLogsByLevel(LogLevel.Trace).Select(l => l.Message).ToList(); Assert.Contains(traceMessages, m => m.Contains("Header:")); @@ -182,15 +194,16 @@ public async Task ApiKeyFilteredFromHeadersAndUrls() ) .ReturnsAsync(OkResponse()); - await CreateTransportWithMockHttp(mockHttp.Object).ExecuteRequestAsync( - HttpMethod.Get, - "/1/test/instant", - new InternalRequestOptions - { - UseReadTransporter = true, - QueryParameters = new Dictionary { ["apiKey"] = "secret-in-url" }, - } - ); + await CreateTransportWithMockHttp(mockHttp.Object) + .ExecuteRequestAsync( + HttpMethod.Get, + "/1/test/instant", + new InternalRequestOptions + { + UseReadTransporter = true, + QueryParameters = new Dictionary { ["apiKey"] = "secret-in-url" }, + } + ); foreach (var log in _logs) { @@ -198,7 +211,11 @@ await CreateTransportWithMockHttp(mockHttp.Object).ExecuteRequestAsync( Assert.DoesNotContain("secret-in-url", log.Message); } - var headerLog = AssertLogMatches(LogLevel.Trace, @"Header: x-algolia-api-key", "Should have filtered api-key header log"); + var headerLog = AssertLogMatches( + LogLevel.Trace, + @"Header: x-algolia-api-key", + "Should have filtered api-key header log" + ); Assert.Contains("[FILTERED]", headerLog); } @@ -207,11 +224,12 @@ public async Task RetryLogFormats() { try { - await CreateTransportWithHosts(NonRoutableIp, NonRoutableIp2).ExecuteRequestAsync( - HttpMethod.Get, - "/1/test", - new InternalRequestOptions { UseReadTransporter = true } - ); + await CreateTransportWithHosts(NonRoutableIp, NonRoutableIp2) + .ExecuteRequestAsync( + HttpMethod.Get, + "/1/test", + new InternalRequestOptions { UseReadTransporter = true } + ); } catch (AlgoliaUnreachableHostException) { } @@ -233,11 +251,12 @@ public async Task RetryExhaustionLogsError() { try { - await CreateTransportWithHosts(NonRoutableIp).ExecuteRequestAsync( - HttpMethod.Get, - "/1/test", - new InternalRequestOptions { UseReadTransporter = true } - ); + await CreateTransportWithHosts(NonRoutableIp) + .ExecuteRequestAsync( + HttpMethod.Get, + "/1/test", + new InternalRequestOptions { UseReadTransporter = true } + ); } catch (AlgoliaUnreachableHostException) { } @@ -268,8 +287,18 @@ public async Task RetryCompletionLogsInfo() { CustomHosts = new List { - new() { Url = "bad-host", Accept = CallType.Read | CallType.Write, Up = true }, - new() { Url = "good-host", Accept = CallType.Read | CallType.Write, Up = true }, + new() + { + Url = "bad-host", + Accept = CallType.Read | CallType.Write, + Up = true, + }, + new() + { + Url = "good-host", + Accept = CallType.Read | CallType.Write, + Up = true, + }, }, }; @@ -301,21 +330,16 @@ public async Task FailureOutcomeLogsError() It.IsAny() ) ) - .ReturnsAsync( - new AlgoliaHttpResponse - { - HttpStatusCode = 403, - Error = "Invalid API key", - } - ); + .ReturnsAsync(new AlgoliaHttpResponse { HttpStatusCode = 403, Error = "Invalid API key" }); try { - await CreateTransportWithMockHttp(mockHttp.Object).ExecuteRequestAsync( - HttpMethod.Get, - "/1/test", - new InternalRequestOptions { UseReadTransporter = true } - ); + await CreateTransportWithMockHttp(mockHttp.Object) + .ExecuteRequestAsync( + HttpMethod.Get, + "/1/test", + new InternalRequestOptions { UseReadTransporter = true } + ); } catch (AlgoliaApiException) { } @@ -345,7 +369,12 @@ public async Task DefaultLoggerProducesNoOutput() { CustomHosts = new List { - new() { Url = "localhost", Accept = CallType.Read | CallType.Write, Up = true }, + new() + { + Url = "localhost", + Accept = CallType.Read | CallType.Write, + Up = true, + }, }, }; var transport = new HttpTransport(config, mockHttp.Object, NullLoggerFactory.Instance); @@ -386,7 +415,12 @@ public async Task BatchOperationLogging() { CustomHosts = new List { - new() { Url = "localhost", Accept = CallType.Read | CallType.Write, Up = true }, + new() + { + Url = "localhost", + Accept = CallType.Read | CallType.Write, + Up = true, + }, }, }; @@ -450,11 +484,12 @@ public async Task DeserializationErrorLogsError() try { - await CreateTransportWithMockHttp(mockHttp.Object).ExecuteRequestAsync( - HttpMethod.Get, - "/1/test", - new InternalRequestOptions { UseReadTransporter = true } - ); + await CreateTransportWithMockHttp(mockHttp.Object) + .ExecuteRequestAsync( + HttpMethod.Get, + "/1/test", + new InternalRequestOptions { UseReadTransporter = true } + ); } catch (AlgoliaException) { } @@ -483,11 +518,12 @@ public async Task SerializationErrorLogsError() try { // double.NaN cannot be serialized to JSON by System.Text.Json - await CreateTransportWithMockHttp(mockHttp.Object).ExecuteRequestAsync( - HttpMethod.Post, - "/1/test", - new InternalRequestOptions { Data = new { value = double.NaN } } - ); + await CreateTransportWithMockHttp(mockHttp.Object) + .ExecuteRequestAsync( + HttpMethod.Post, + "/1/test", + new InternalRequestOptions { Data = new { value = double.NaN } } + ); } catch (AlgoliaException) { } @@ -539,6 +575,7 @@ Func formatter private sealed class NullScope : IDisposable { public static readonly NullScope Instance = new(); + public void Dispose() { } } } diff --git a/tests/output/csharp/src/Utils/TestHelpers.cs b/tests/output/csharp/src/Utils/TestHelpers.cs new file mode 100644 index 00000000000..7264da6a005 --- /dev/null +++ b/tests/output/csharp/src/Utils/TestHelpers.cs @@ -0,0 +1,54 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using Quibble.Xunit; + +namespace Algolia.Search.Tests.Utils; + +public static class TestHelpers +{ + /// + /// Asserts that the serialized response contains at least the expected JSON structure. + /// Extra keys in objects and extra elements in arrays (beyond expected indices) are ignored. + /// Mirrors the union-based e2e assertion used by JS, Python, Ruby, PHP, Go, and Swift clients. + /// + public static void LenientJsonAssert(string expected, string actual) + { + var expectedNode = JsonNode.Parse(expected); + var actualNode = JsonNode.Parse(actual); + var unionNode = Union(expectedNode, actualNode); + var unionJson = JsonSerializer.Serialize(unionNode); + JsonAssert.EqualOverrideDefault(expected, unionJson, new JsonDiffConfig(true)); + } + + /// + /// Recursively intersects the structure of with the values of + /// . Only keys/indices present in expected are kept. + /// + private static JsonNode Union(JsonNode expected, JsonNode received) + { + if (expected is JsonObject expectedObj && received is JsonObject receivedObj) + { + var result = new JsonObject(); + foreach (var prop in expectedObj) + { + if (receivedObj[prop.Key] != null) + { + result[prop.Key] = Union(prop.Value, receivedObj[prop.Key]); + } + } + return result; + } + + if (expected is JsonArray expectedArr && received is JsonArray receivedArr) + { + var result = new JsonArray(); + for (int i = 0; i < expectedArr.Count && i < receivedArr.Count; i++) + { + result.Add(Union(expectedArr[i], receivedArr[i])); + } + return result; + } + + return received.DeepClone(); + } +} diff --git a/tests/output/java/src/test/java/com/algolia/utils/TestHelpers.java b/tests/output/java/src/test/java/com/algolia/utils/TestHelpers.java new file mode 100644 index 00000000000..9b214fde036 --- /dev/null +++ b/tests/output/java/src/test/java/com/algolia/utils/TestHelpers.java @@ -0,0 +1,54 @@ +package com.algolia.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; + +public class TestHelpers { + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * Asserts that the serialized response contains at least the expected JSON structure. Extra keys + * in objects and extra elements in arrays (beyond expected indices) are ignored. Mirrors the + * union-based e2e assertion used by JS, Python, Ruby, PHP, Go, Swift, and C# clients. + */ + public static void lenientJsonAssert(String expected, String actual) throws Exception { + JsonNode expectedNode = mapper.readTree(expected); + JsonNode actualNode = mapper.readTree(actual); + JsonNode unionNode = union(expectedNode, actualNode); + String unionJson = mapper.writeValueAsString(unionNode); + JSONAssert.assertEquals(expected, unionJson, JSONCompareMode.LENIENT); + } + + /** + * Recursively intersects the structure of {@code expected} with the values of {@code received}. + * Only keys/indices present in expected are kept. + */ + private static JsonNode union(JsonNode expected, JsonNode received) { + if (expected.isObject() && received.isObject()) { + ObjectNode result = mapper.createObjectNode(); + expected + .fieldNames() + .forEachRemaining(key -> { + if (received.has(key)) { + result.set(key, union(expected.get(key), received.get(key))); + } + }); + return result; + } + + if (expected.isArray() && received.isArray()) { + ArrayNode result = mapper.createArrayNode(); + for (int i = 0; i < expected.size() && i < received.size(); i++) { + result.add(union(expected.get(i), received.get(i))); + } + return result; + } + + return received.deepCopy(); + } +} diff --git a/tests/output/kotlin/src/commonTest/kotlin/com/algolia/utils/TestHelpers.kt b/tests/output/kotlin/src/commonTest/kotlin/com/algolia/utils/TestHelpers.kt new file mode 100644 index 00000000000..47ddbf160bf --- /dev/null +++ b/tests/output/kotlin/src/commonTest/kotlin/com/algolia/utils/TestHelpers.kt @@ -0,0 +1,46 @@ +package com.algolia.utils + +import kotlinx.serialization.json.* +import org.skyscreamer.jsonassert.JSONAssert +import org.skyscreamer.jsonassert.JSONCompareMode + +/** + * Asserts that the serialized response contains at least the expected JSON structure. Extra keys in + * objects and extra elements in arrays (beyond expected indices) are ignored. Mirrors the + * union-based e2e assertion used by JS, Python, Ruby, PHP, Go, Swift, and C# clients. + */ +public fun lenientJsonAssert(expected: String, actual: String) { + val expectedNode = Json.parseToJsonElement(expected) + val actualNode = Json.parseToJsonElement(actual) + val unionNode = union(expectedNode, actualNode) + val unionJson = Json.encodeToString(JsonElement.serializer(), unionNode) + JSONAssert.assertEquals(expected, unionJson, JSONCompareMode.LENIENT) +} + +/** + * Recursively intersects the structure of [expected] with the values of [received]. Only + * keys/indices present in expected are kept. + */ +private fun union(expected: JsonElement, received: JsonElement): JsonElement { + if (expected is JsonObject && received is JsonObject) { + return buildJsonObject { + for ((key, value) in expected) { + if (key in received) { + put(key, union(value, received[key]!!)) + } + } + } + } + + if (expected is JsonArray && received is JsonArray) { + return buildJsonArray { + for (i in expected.indices) { + if (i < received.size) { + add(union(expected[i], received[i])) + } + } + } + } + + return received +} diff --git a/tests/output/python/requirements.txt b/tests/output/python/requirements.txt index e69de29bb2d..b1783a65964 100644 --- a/tests/output/python/requirements.txt +++ b/tests/output/python/requirements.txt @@ -0,0 +1,6 @@ +argcomplete==3.6.3 +click==8.3.3 +packaging==26.2 +pipx==1.11.1 +platformdirs==4.9.6 +userpath==1.9.2 diff --git a/tests/output/python/tests/manual/test_sse.py b/tests/output/python/tests/manual/test_sse.py new file mode 100644 index 00000000000..e64b1043ac3 --- /dev/null +++ b/tests/output/python/tests/manual/test_sse.py @@ -0,0 +1,416 @@ +"""Tests for the WHATWG-compliant SSE parser. + +Covers all 16 WHATWG spec cases plus boundary/overflow edge cases, +for both sync (iter_sse_events) and async (aiter_sse_events) variants. +""" + +import asyncio +import unittest +from typing import AsyncIterator, List + +from algoliasearch.http.sse import ServerSentEvent, aiter_sse_events, iter_sse_events + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def make_byte_chunks(text: str) -> List[bytes]: + """Convert a string to a list with a single bytes chunk.""" + return [text.encode("utf-8")] + + +def make_multi_chunks(chunks: List[str]) -> List[bytes]: + """Convert multiple strings to bytes chunks.""" + return [c.encode("utf-8") for c in chunks] + + +class AsyncByteIter: + """Wraps a list of bytes into an async iterable.""" + + def __init__(self, chunks: List[bytes]) -> None: + self._chunks = chunks + + def __aiter__(self) -> AsyncIterator[bytes]: + return self._iter() + + async def _iter(self) -> AsyncIterator[bytes]: + for chunk in self._chunks: + yield chunk + + +def collect_sync(text: str) -> List[ServerSentEvent]: + """Collect all SSE events from a text string (sync).""" + return list(iter_sse_events(make_byte_chunks(text))) + + +def collect_sync_multi(chunks: List[str]) -> List[ServerSentEvent]: + """Collect all SSE events from multiple text chunks (sync).""" + return list(iter_sse_events(make_multi_chunks(chunks))) + + +def collect_sync_raw(raw_chunks: List[bytes]) -> List[ServerSentEvent]: + """Collect all SSE events from raw byte chunks (sync).""" + return list(iter_sse_events(raw_chunks)) + + +async def _collect_async(chunks: List[bytes]) -> List[ServerSentEvent]: + """Collect all SSE events from byte chunks (async).""" + result: List[ServerSentEvent] = [] + async for event in aiter_sse_events(AsyncByteIter(chunks)): + result.append(event) + return result + + +def collect_async(text: str) -> List[ServerSentEvent]: + """Collect all SSE events from a text string (async, run via asyncio).""" + return asyncio.run(_collect_async(make_byte_chunks(text))) + + +def collect_async_multi(chunks: List[str]) -> List[ServerSentEvent]: + """Collect all SSE events from multiple text chunks (async).""" + return asyncio.run(_collect_async(make_multi_chunks(chunks))) + + +def collect_async_raw(raw_chunks: List[bytes]) -> List[ServerSentEvent]: + """Collect all SSE events from raw byte chunks (async).""" + return asyncio.run(_collect_async(raw_chunks)) + + +# --------------------------------------------------------------------------- +# 1. Single data event +# --------------------------------------------------------------------------- + + +class TestSingleDataEvent(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data: hello\n\n") + assert len(events) == 1 + assert events[0] == ServerSentEvent(data="hello", event="", id=None, retry=None) + + def test_async(self) -> None: + events = collect_async("data: hello\n\n") + assert len(events) == 1 + assert events[0] == ServerSentEvent(data="hello", event="", id=None, retry=None) + + +# --------------------------------------------------------------------------- +# 2. Multi-line data +# --------------------------------------------------------------------------- + + +class TestMultiLineData(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data: line1\ndata: line2\n\n") + assert len(events) == 1 + assert events[0].data == "line1\nline2" + + def test_async(self) -> None: + events = collect_async("data: line1\ndata: line2\n\n") + assert len(events) == 1 + assert events[0].data == "line1\nline2" + + +# --------------------------------------------------------------------------- +# 3. Event type +# --------------------------------------------------------------------------- + + +class TestEventType(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("event: custom\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].event == "custom" + assert events[0].data == "hi" + + def test_async(self) -> None: + events = collect_async("event: custom\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].event == "custom" + assert events[0].data == "hi" + + +# --------------------------------------------------------------------------- +# 4. Comment ignored +# --------------------------------------------------------------------------- + + +class TestCommentIgnored(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync(": comment\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].data == "hi" + + def test_async(self) -> None: + events = collect_async(": comment\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].data == "hi" + + +# --------------------------------------------------------------------------- +# 5. Empty data suppressed (no data field → no dispatch) +# --------------------------------------------------------------------------- + + +class TestEmptyDataSuppressed(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("event: ping\n\n") + assert len(events) == 0 + + def test_async(self) -> None: + events = collect_async("event: ping\n\n") + assert len(events) == 0 + + +# --------------------------------------------------------------------------- +# 6. Field with no colon +# --------------------------------------------------------------------------- + + +class TestFieldNoColon(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data\n\n") + assert len(events) == 1 + assert events[0].data == "" + + def test_async(self) -> None: + events = collect_async("data\n\n") + assert len(events) == 1 + assert events[0].data == "" + + +# --------------------------------------------------------------------------- +# 7. Single space strip (two spaces in, one space out) +# --------------------------------------------------------------------------- + + +class TestSingleSpaceStrip(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data: hello\n\n") + assert len(events) == 1 + assert events[0].data == " hello" + + def test_async(self) -> None: + events = collect_async("data: hello\n\n") + assert len(events) == 1 + assert events[0].data == " hello" + + +# --------------------------------------------------------------------------- +# 8. Unknown field ignored +# --------------------------------------------------------------------------- + + +class TestUnknownFieldIgnored(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("foo: bar\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].data == "hi" + + def test_async(self) -> None: + events = collect_async("foo: bar\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].data == "hi" + + +# --------------------------------------------------------------------------- +# 9. id persistence +# --------------------------------------------------------------------------- + + +class TestIdPersistence(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("id: 42\ndata: a\n\ndata: b\n\n") + assert len(events) == 2 + assert events[0].id == "42" + assert events[0].data == "a" + assert events[1].id == "42" + assert events[1].data == "b" + + def test_async(self) -> None: + events = collect_async("id: 42\ndata: a\n\ndata: b\n\n") + assert len(events) == 2 + assert events[0].id == "42" + assert events[1].id == "42" + + +# --------------------------------------------------------------------------- +# 10. id with NULL +# --------------------------------------------------------------------------- + + +class TestIdWithNull(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("id: foo\x00bar\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].id is None + + def test_async(self) -> None: + events = collect_async("id: foo\x00bar\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].id is None + + +# --------------------------------------------------------------------------- +# 11. retry digits only +# --------------------------------------------------------------------------- + + +class TestRetryDigitsOnly(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("retry: 3000\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].retry == 3000 + + def test_async(self) -> None: + events = collect_async("retry: 3000\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].retry == 3000 + + +# --------------------------------------------------------------------------- +# 12. retry non-digits +# --------------------------------------------------------------------------- + + +class TestRetryNonDigits(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("retry: 3s\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].retry is None + + def test_async(self) -> None: + events = collect_async("retry: 3s\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].retry is None + + +# --------------------------------------------------------------------------- +# 13. CR line endings +# --------------------------------------------------------------------------- + + +class TestCRLineEndings(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data: hello\r\r") + assert len(events) == 1 + assert events[0].data == "hello" + + def test_async(self) -> None: + events = collect_async("data: hello\r\r") + assert len(events) == 1 + assert events[0].data == "hello" + + +# --------------------------------------------------------------------------- +# 14. CRLF line endings +# --------------------------------------------------------------------------- + + +class TestCRLFLineEndings(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data: hello\r\n\r\n") + assert len(events) == 1 + assert events[0].data == "hello" + + def test_async(self) -> None: + events = collect_async("data: hello\r\n\r\n") + assert len(events) == 1 + assert events[0].data == "hello" + + +# --------------------------------------------------------------------------- +# 15. Mixed line endings +# --------------------------------------------------------------------------- + + +class TestMixedLineEndings(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data: a\rdata: b\n\n") + assert len(events) == 1 + assert events[0].data == "a\nb" + + def test_async(self) -> None: + events = collect_async("data: a\rdata: b\n\n") + assert len(events) == 1 + assert events[0].data == "a\nb" + + +# --------------------------------------------------------------------------- +# 16. Stream ends mid-event (EOF without blank line) +# --------------------------------------------------------------------------- + + +class TestStreamEndsMidEvent(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("data: partial") + assert len(events) == 0 + + def test_async(self) -> None: + events = collect_async("data: partial") + assert len(events) == 0 + + +# --------------------------------------------------------------------------- +# 17. trailing_cr across chunk boundaries +# --------------------------------------------------------------------------- + + +class TestTrailingCRAcrossChunks(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync_raw([b"data: hello\r", b"\ndata: world\r\n\r\n"]) + assert len(events) == 1 + assert events[0].data == "hello\nworld" + + def test_async(self) -> None: + events = collect_async_raw([b"data: hello\r", b"\ndata: world\r\n\r\n"]) + assert len(events) == 1 + assert events[0].data == "hello\nworld" + + +# --------------------------------------------------------------------------- +# 18. 10MB buffer cap +# --------------------------------------------------------------------------- + + +class TestBufferCap(unittest.TestCase): + def test_sync(self) -> None: + huge_line = "data: " + "x" * (10 * 1024 * 1024 + 1) + raised = False + try: + collect_sync(huge_line) + except ValueError as e: + raised = True + assert "10 MB" in str(e) + assert raised, "Expected ValueError to be raised" + + def test_async(self) -> None: + huge_line = "data: " + "x" * (10 * 1024 * 1024 + 1) + raised = False + try: + collect_async(huge_line) + except ValueError as e: + raised = True + assert "10 MB" in str(e) + assert raised, "Expected ValueError to be raised" + + +# --------------------------------------------------------------------------- +# 19. eventType resets on suppressed dispatch +# --------------------------------------------------------------------------- + + +class TestEventTypeResetsOnSuppressedDispatch(unittest.TestCase): + def test_sync(self) -> None: + events = collect_sync("event: custom\n\ndata: hi\n\n") + # First blank line: no data → suppressed, but eventType resets + # Second event: data="hi", event="" (not "custom") + assert len(events) == 1 + assert events[0].data == "hi" + assert events[0].event == "" + + def test_async(self) -> None: + events = collect_async("event: custom\n\ndata: hi\n\n") + assert len(events) == 1 + assert events[0].data == "hi" + assert events[0].event == ""