apologist 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.fern/metadata.json +13 -0
- data/.fernignore +2 -0
- data/.rubocop.yml +108 -0
- data/CONTRIBUTING.md +120 -0
- data/README.md +169 -0
- data/Rakefile +20 -0
- data/custom.gemspec.rb +17 -0
- data/lib/Apologist/benchmarks/client.rb +160 -0
- data/lib/Apologist/benchmarks/types/benchmark_run_request.rb +39 -0
- data/lib/Apologist/benchmarks/types/benchmark_run_request_content.rb +16 -0
- data/lib/Apologist/benchmarks/types/benchmark_run_request_reasoning_effort.rb +15 -0
- data/lib/Apologist/benchmarks/types/benchmark_run_request_verbosity.rb +16 -0
- data/lib/Apologist/benchmarks/types/get_benchmark_run_request.rb +13 -0
- data/lib/Apologist/benchmarks/types/get_benchmark_run_response.rb +11 -0
- data/lib/Apologist/benchmarks/types/list_benchmark_runs_request.rb +33 -0
- data/lib/Apologist/benchmarks/types/list_benchmark_runs_response.rb +17 -0
- data/lib/Apologist/channels/client.rb +319 -0
- data/lib/Apologist/channels/types/get_discord_channel_status_request.rb +11 -0
- data/lib/Apologist/channels/types/get_discord_channel_status_response.rb +15 -0
- data/lib/Apologist/channels/types/get_instagram_privacy_policy_request.rb +11 -0
- data/lib/Apologist/channels/types/receive_discord_interaction_request.rb +17 -0
- data/lib/Apologist/channels/types/receive_facebook_message_request.rb +13 -0
- data/lib/Apologist/channels/types/receive_telegram_update_request.rb +13 -0
- data/lib/Apologist/channels/types/receive_twilio_message_request.rb +15 -0
- data/lib/Apologist/channels/types/verify_facebook_webhook_request.rb +17 -0
- data/lib/Apologist/channels/types/verify_facebook_webhook_request_hub_mode.rb +13 -0
- data/lib/Apologist/chat/client.rb +345 -0
- data/lib/Apologist/chat/types/feedback_request.rb +13 -0
- data/lib/Apologist/chat/types/flag_request.rb +13 -0
- data/lib/Apologist/chat/types/get_chat_completion_request.rb +11 -0
- data/lib/Apologist/chat/types/get_chat_completion_response.rb +11 -0
- data/lib/Apologist/chat/types/like_request.rb +13 -0
- data/lib/Apologist/chat/types/list_chat_completions_request.rb +45 -0
- data/lib/Apologist/chat/types/list_chat_completions_response.rb +17 -0
- data/lib/Apologist/chat/types/share_request.rb +17 -0
- data/lib/Apologist/client.rb +71 -0
- data/lib/Apologist/corpus/client.rb +244 -0
- data/lib/Apologist/corpus/types/corpus_search_request.rb +17 -0
- data/lib/Apologist/corpus/types/corpus_search_request_filters.rb +25 -0
- data/lib/Apologist/corpus/types/impression_request.rb +17 -0
- data/lib/Apologist/corpus/types/log_corpus_referral_redirect_request.rb +19 -0
- data/lib/Apologist/corpus/types/referral_request.rb +17 -0
- data/lib/Apologist/corpus/types/search_corpus_response.rb +11 -0
- data/lib/Apologist/corpus/types/view_request.rb +17 -0
- data/lib/Apologist/ct_as/client.rb +98 -0
- data/lib/Apologist/ct_as/types/cta_click_request.rb +13 -0
- data/lib/Apologist/ct_as/types/match_ctas_response.rb +11 -0
- data/lib/Apologist/environment.rb +7 -0
- data/lib/Apologist/errors/api_error.rb +8 -0
- data/lib/Apologist/errors/client_error.rb +17 -0
- data/lib/Apologist/errors/redirect_error.rb +8 -0
- data/lib/Apologist/errors/response_error.rb +42 -0
- data/lib/Apologist/errors/server_error.rb +11 -0
- data/lib/Apologist/errors/timeout_error.rb +8 -0
- data/lib/Apologist/evaluators/client.rb +164 -0
- data/lib/Apologist/evaluators/types/evaluate_content_response.rb +11 -0
- data/lib/Apologist/evaluators/types/evaluator_request.rb +31 -0
- data/lib/Apologist/evaluators/types/evaluator_request_content.rb +15 -0
- data/lib/Apologist/evaluators/types/evaluator_request_reasoning_effort.rb +15 -0
- data/lib/Apologist/evaluators/types/evaluator_request_verbosity.rb +16 -0
- data/lib/Apologist/evaluators/types/get_evaluation_request.rb +13 -0
- data/lib/Apologist/evaluators/types/get_evaluation_response.rb +11 -0
- data/lib/Apologist/evaluators/types/list_evaluations_request.rb +35 -0
- data/lib/Apologist/evaluators/types/list_evaluations_response.rb +17 -0
- data/lib/Apologist/internal/errors/constraint_error.rb +10 -0
- data/lib/Apologist/internal/errors/type_error.rb +10 -0
- data/lib/Apologist/internal/http/base_request.rb +51 -0
- data/lib/Apologist/internal/http/raw_client.rb +252 -0
- data/lib/Apologist/internal/iterators/cursor_item_iterator.rb +28 -0
- data/lib/Apologist/internal/iterators/cursor_page_iterator.rb +63 -0
- data/lib/Apologist/internal/iterators/item_iterator.rb +65 -0
- data/lib/Apologist/internal/iterators/offset_item_iterator.rb +30 -0
- data/lib/Apologist/internal/iterators/offset_page_iterator.rb +103 -0
- data/lib/Apologist/internal/json/request.rb +41 -0
- data/lib/Apologist/internal/json/serializable.rb +25 -0
- data/lib/Apologist/internal/multipart/multipart_encoder.rb +141 -0
- data/lib/Apologist/internal/multipart/multipart_form_data.rb +78 -0
- data/lib/Apologist/internal/multipart/multipart_form_data_part.rb +51 -0
- data/lib/Apologist/internal/multipart/multipart_request.rb +40 -0
- data/lib/Apologist/internal/types/array.rb +47 -0
- data/lib/Apologist/internal/types/boolean.rb +34 -0
- data/lib/Apologist/internal/types/enum.rb +56 -0
- data/lib/Apologist/internal/types/hash.rb +36 -0
- data/lib/Apologist/internal/types/model/field.rb +38 -0
- data/lib/Apologist/internal/types/model.rb +208 -0
- data/lib/Apologist/internal/types/type.rb +35 -0
- data/lib/Apologist/internal/types/union.rb +161 -0
- data/lib/Apologist/internal/types/unknown.rb +15 -0
- data/lib/Apologist/internal/types/utils.rb +116 -0
- data/lib/Apologist/internal/url_encoded/request.rb +41 -0
- data/lib/Apologist/shares/client.rb +52 -0
- data/lib/Apologist/shares/types/get_shared_messages_request.rb +11 -0
- data/lib/Apologist/shares/types/get_shared_messages_response.rb +11 -0
- data/lib/Apologist/types/chat_completion_request.rb +11 -0
- data/lib/Apologist/types/chat_completion_request_logprobs.rb +13 -0
- data/lib/Apologist/types/chat_completion_request_metadata.rb +29 -0
- data/lib/Apologist/types/chat_completion_request_reasoning_effort.rb +13 -0
- data/lib/Apologist/types/chat_completion_request_response_format.rb +11 -0
- data/lib/Apologist/types/chat_completion_request_response_format_json_schema.rb +17 -0
- data/lib/Apologist/types/chat_completion_request_response_format_type.rb +15 -0
- data/lib/Apologist/types/chat_completion_request_tool_choice.rb +13 -0
- data/lib/Apologist/types/chat_completion_request_tool_choice_zero.rb +13 -0
- data/lib/Apologist/types/chat_completion_request_verbosity.rb +14 -0
- data/lib/Apologist/types/chat_completion_response.rb +21 -0
- data/lib/Apologist/types/chat_completion_response_choices_item.rb +15 -0
- data/lib/Apologist/types/chat_completion_response_usage.rb +13 -0
- data/lib/Apologist/types/chat_message.rb +11 -0
- data/lib/Apologist/types/chat_message_role.rb +13 -0
- data/lib/Apologist/types/cta_match_request.rb +11 -0
- data/lib/Apologist/types/error.rb +11 -0
- data/lib/Apologist/types/success_response.rb +11 -0
- data/lib/Apologist/types/tag_ref.rb +11 -0
- data/lib/Apologist/types/user.rb +23 -0
- data/lib/Apologist/types/user_flag.rb +18 -0
- data/lib/Apologist/types/webhook_agent_ref.rb +11 -0
- data/lib/Apologist/types/webhook_cta.rb +13 -0
- data/lib/Apologist/types/webhook_evaluation.rb +14 -0
- data/lib/Apologist/types/webhook_event_info.rb +13 -0
- data/lib/Apologist/types/webhook_event_info_key.rb +28 -0
- data/lib/Apologist/types/webhook_named_ref.rb +11 -0
- data/lib/Apologist/types/webhook_notification_ref.rb +12 -0
- data/lib/Apologist/types/webhook_payload.rb +30 -0
- data/lib/Apologist/users/client.rb +193 -0
- data/lib/Apologist/users/types/get_user_request.rb +11 -0
- data/lib/Apologist/users/types/get_user_response.rb +11 -0
- data/lib/Apologist/users/types/list_user_flags_request.rb +13 -0
- data/lib/Apologist/users/types/list_user_flags_response.rb +17 -0
- data/lib/Apologist/users/types/list_users_request.rb +23 -0
- data/lib/Apologist/users/types/list_users_response.rb +17 -0
- data/lib/Apologist/users/types/update_user_response.rb +11 -0
- data/lib/Apologist/users/types/user_update_request.rb +17 -0
- data/lib/Apologist/users/types/user_update_request_tags_item.rb +15 -0
- data/lib/Apologist/version.rb +5 -0
- data/lib/Apologist.rb +131 -0
- data/reference.md +2927 -0
- metadata +183 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
class Client
|
|
6
|
+
# @param client [Apologist::Internal::Http::RawClient]
|
|
7
|
+
#
|
|
8
|
+
# @return [void]
|
|
9
|
+
def initialize(client:)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Returns a paginated list of evaluations for the evaluator, scoped to the requesting agent.
|
|
14
|
+
#
|
|
15
|
+
# @param request_options [Hash]
|
|
16
|
+
# @param params [Hash]
|
|
17
|
+
# @option request_options [String] :base_url
|
|
18
|
+
# @option request_options [Hash{String => Object}] :additional_headers
|
|
19
|
+
# @option request_options [Hash{String => Object}] :additional_query_parameters
|
|
20
|
+
# @option request_options [Hash{String => Object}] :additional_body_parameters
|
|
21
|
+
# @option request_options [Integer] :timeout_in_seconds
|
|
22
|
+
# @option params [String] :id
|
|
23
|
+
# @option params [Integer, nil] :page
|
|
24
|
+
# @option params [Integer, nil] :per_page
|
|
25
|
+
# @option params [String, nil] :min_timestamp
|
|
26
|
+
# @option params [String, nil] :max_timestamp
|
|
27
|
+
# @option params [String, nil] :min_duration
|
|
28
|
+
# @option params [String, nil] :max_duration
|
|
29
|
+
# @option params [String, nil] :min_score
|
|
30
|
+
# @option params [String, nil] :max_score
|
|
31
|
+
# @option params [String, nil] :passed
|
|
32
|
+
# @option params [String, nil] :benchmark
|
|
33
|
+
# @option params [String, nil] :benchmark_run_id
|
|
34
|
+
# @option params [String, nil] :benchmark_question_id
|
|
35
|
+
#
|
|
36
|
+
# @example
|
|
37
|
+
# client.evaluators.list_evaluations(id: "id")
|
|
38
|
+
#
|
|
39
|
+
# @return [Apologist::Evaluators::Types::ListEvaluationsResponse]
|
|
40
|
+
def list_evaluations(request_options: {}, **params)
|
|
41
|
+
params = Apologist::Internal::Types::Utils.normalize_keys(params)
|
|
42
|
+
query_params = {}
|
|
43
|
+
query_params["page"] = params[:page] if params.key?(:page)
|
|
44
|
+
query_params["per_page"] = params[:per_page] if params.key?(:per_page)
|
|
45
|
+
query_params["min_timestamp"] = params[:min_timestamp] if params.key?(:min_timestamp)
|
|
46
|
+
query_params["max_timestamp"] = params[:max_timestamp] if params.key?(:max_timestamp)
|
|
47
|
+
query_params["min_duration"] = params[:min_duration] if params.key?(:min_duration)
|
|
48
|
+
query_params["max_duration"] = params[:max_duration] if params.key?(:max_duration)
|
|
49
|
+
query_params["min_score"] = params[:min_score] if params.key?(:min_score)
|
|
50
|
+
query_params["max_score"] = params[:max_score] if params.key?(:max_score)
|
|
51
|
+
query_params["passed"] = params[:passed] if params.key?(:passed)
|
|
52
|
+
query_params["benchmark"] = params[:benchmark] if params.key?(:benchmark)
|
|
53
|
+
query_params["benchmark_run_id"] = params[:benchmark_run_id] if params.key?(:benchmark_run_id)
|
|
54
|
+
query_params["benchmark_question_id"] = params[:benchmark_question_id] if params.key?(:benchmark_question_id)
|
|
55
|
+
|
|
56
|
+
request = Apologist::Internal::JSON::Request.new(
|
|
57
|
+
base_url: request_options[:base_url],
|
|
58
|
+
method: "GET",
|
|
59
|
+
path: "evaluators/#{URI.encode_uri_component(params[:id].to_s)}/evaluations",
|
|
60
|
+
query: query_params,
|
|
61
|
+
request_options: request_options
|
|
62
|
+
)
|
|
63
|
+
begin
|
|
64
|
+
response = @client.send(request)
|
|
65
|
+
rescue Net::HTTPRequestTimeout
|
|
66
|
+
raise Apologist::Errors::TimeoutError
|
|
67
|
+
end
|
|
68
|
+
code = response.code.to_i
|
|
69
|
+
if code.between?(200, 299)
|
|
70
|
+
Apologist::Evaluators::Types::ListEvaluationsResponse.load(response.body)
|
|
71
|
+
else
|
|
72
|
+
error_class = Apologist::Errors::ResponseError.subclass_for_code(code)
|
|
73
|
+
raise error_class.new(response.body, code: code)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Runs an evaluation on the provided content using the specified evaluator
|
|
78
|
+
#
|
|
79
|
+
# @param request_options [Hash]
|
|
80
|
+
# @param params [Apologist::Evaluators::Types::EvaluatorRequest]
|
|
81
|
+
# @option request_options [String] :base_url
|
|
82
|
+
# @option request_options [Hash{String => Object}] :additional_headers
|
|
83
|
+
# @option request_options [Hash{String => Object}] :additional_query_parameters
|
|
84
|
+
# @option request_options [Hash{String => Object}] :additional_body_parameters
|
|
85
|
+
# @option request_options [Integer] :timeout_in_seconds
|
|
86
|
+
# @option params [String] :id
|
|
87
|
+
#
|
|
88
|
+
# @example
|
|
89
|
+
# client.evaluators.evaluate_content(
|
|
90
|
+
# id: "id",
|
|
91
|
+
# content: "content"
|
|
92
|
+
# )
|
|
93
|
+
#
|
|
94
|
+
# @return [Apologist::Evaluators::Types::EvaluateContentResponse]
|
|
95
|
+
def evaluate_content(request_options: {}, **params)
|
|
96
|
+
params = Apologist::Internal::Types::Utils.normalize_keys(params)
|
|
97
|
+
request_data = Apologist::Evaluators::Types::EvaluatorRequest.new(params).to_h
|
|
98
|
+
non_body_param_names = %w[id]
|
|
99
|
+
body = request_data.except(*non_body_param_names)
|
|
100
|
+
|
|
101
|
+
request = Apologist::Internal::JSON::Request.new(
|
|
102
|
+
base_url: request_options[:base_url],
|
|
103
|
+
method: "POST",
|
|
104
|
+
path: "evaluators/#{URI.encode_uri_component(params[:id].to_s)}/evaluations",
|
|
105
|
+
body: body,
|
|
106
|
+
request_options: request_options
|
|
107
|
+
)
|
|
108
|
+
begin
|
|
109
|
+
response = @client.send(request)
|
|
110
|
+
rescue Net::HTTPRequestTimeout
|
|
111
|
+
raise Apologist::Errors::TimeoutError
|
|
112
|
+
end
|
|
113
|
+
code = response.code.to_i
|
|
114
|
+
if code.between?(200, 299)
|
|
115
|
+
Apologist::Evaluators::Types::EvaluateContentResponse.load(response.body)
|
|
116
|
+
else
|
|
117
|
+
error_class = Apologist::Errors::ResponseError.subclass_for_code(code)
|
|
118
|
+
raise error_class.new(response.body, code: code)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns a single evaluation for the evaluator, scoped to the requesting agent.
|
|
123
|
+
#
|
|
124
|
+
# @param request_options [Hash]
|
|
125
|
+
# @param params [Hash]
|
|
126
|
+
# @option request_options [String] :base_url
|
|
127
|
+
# @option request_options [Hash{String => Object}] :additional_headers
|
|
128
|
+
# @option request_options [Hash{String => Object}] :additional_query_parameters
|
|
129
|
+
# @option request_options [Hash{String => Object}] :additional_body_parameters
|
|
130
|
+
# @option request_options [Integer] :timeout_in_seconds
|
|
131
|
+
# @option params [String] :id
|
|
132
|
+
# @option params [String] :evaluation_id
|
|
133
|
+
#
|
|
134
|
+
# @example
|
|
135
|
+
# client.evaluators.get_evaluation(
|
|
136
|
+
# id: "id",
|
|
137
|
+
# evaluation_id: "evaluationId"
|
|
138
|
+
# )
|
|
139
|
+
#
|
|
140
|
+
# @return [Apologist::Evaluators::Types::GetEvaluationResponse]
|
|
141
|
+
def get_evaluation(request_options: {}, **params)
|
|
142
|
+
params = Apologist::Internal::Types::Utils.normalize_keys(params)
|
|
143
|
+
request = Apologist::Internal::JSON::Request.new(
|
|
144
|
+
base_url: request_options[:base_url],
|
|
145
|
+
method: "GET",
|
|
146
|
+
path: "evaluators/#{URI.encode_uri_component(params[:id].to_s)}/evaluations/#{URI.encode_uri_component(params[:evaluation_id].to_s)}",
|
|
147
|
+
request_options: request_options
|
|
148
|
+
)
|
|
149
|
+
begin
|
|
150
|
+
response = @client.send(request)
|
|
151
|
+
rescue Net::HTTPRequestTimeout
|
|
152
|
+
raise Apologist::Errors::TimeoutError
|
|
153
|
+
end
|
|
154
|
+
code = response.code.to_i
|
|
155
|
+
if code.between?(200, 299)
|
|
156
|
+
Apologist::Evaluators::Types::GetEvaluationResponse.load(response.body)
|
|
157
|
+
else
|
|
158
|
+
error_class = Apologist::Errors::ResponseError.subclass_for_code(code)
|
|
159
|
+
raise error_class.new(response.body, code: code)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
class EvaluateContentResponse < Internal::Types::Model
|
|
7
|
+
field :result, -> { Internal::Types::Hash[String, Object] }, optional: true, nullable: false
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
class EvaluatorRequest < Internal::Types::Model
|
|
7
|
+
field :id, -> { String }, optional: false, nullable: false
|
|
8
|
+
|
|
9
|
+
field :frequency_penalty, -> { Integer }, optional: true, nullable: false
|
|
10
|
+
|
|
11
|
+
field :confidence_threshold, -> { Integer }, optional: true, nullable: false
|
|
12
|
+
|
|
13
|
+
field :content, -> { Apologist::Evaluators::Types::EvaluatorRequestContent }, optional: false, nullable: false
|
|
14
|
+
|
|
15
|
+
field :model, -> { String }, optional: true, nullable: false
|
|
16
|
+
|
|
17
|
+
field :presence_penalty, -> { Integer }, optional: true, nullable: false
|
|
18
|
+
|
|
19
|
+
field :reasoning_effort, -> { Apologist::Evaluators::Types::EvaluatorRequestReasoningEffort }, optional: true, nullable: false
|
|
20
|
+
|
|
21
|
+
field :verbosity, -> { Apologist::Evaluators::Types::EvaluatorRequestVerbosity }, optional: true, nullable: false
|
|
22
|
+
|
|
23
|
+
field :temperature, -> { Integer }, optional: true, nullable: false
|
|
24
|
+
|
|
25
|
+
field :top_p, -> { Integer }, optional: true, nullable: false
|
|
26
|
+
|
|
27
|
+
field :variables, -> { Internal::Types::Hash[String, String] }, optional: true, nullable: false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
class EvaluatorRequestContent < Internal::Types::Model
|
|
7
|
+
extend Apologist::Internal::Types::Union
|
|
8
|
+
|
|
9
|
+
member -> { String }
|
|
10
|
+
|
|
11
|
+
member -> { Internal::Types::Array[Object] }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
module EvaluatorRequestVerbosity
|
|
7
|
+
extend Apologist::Internal::Types::Enum
|
|
8
|
+
|
|
9
|
+
MINIMAL = "minimal"
|
|
10
|
+
LOW = "low"
|
|
11
|
+
MEDIUM = "medium"
|
|
12
|
+
HIGH = "high"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
class GetEvaluationRequest < Internal::Types::Model
|
|
7
|
+
field :id, -> { String }, optional: false, nullable: false
|
|
8
|
+
|
|
9
|
+
field :evaluation_id, -> { String }, optional: false, nullable: false, api_name: "evaluationId"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
class GetEvaluationResponse < Internal::Types::Model
|
|
7
|
+
field :data, -> { Internal::Types::Hash[String, Object] }, optional: true, nullable: false
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
class ListEvaluationsRequest < Internal::Types::Model
|
|
7
|
+
field :id, -> { String }, optional: false, nullable: false
|
|
8
|
+
|
|
9
|
+
field :page, -> { Integer }, optional: true, nullable: false
|
|
10
|
+
|
|
11
|
+
field :per_page, -> { Integer }, optional: true, nullable: false
|
|
12
|
+
|
|
13
|
+
field :min_timestamp, -> { String }, optional: true, nullable: false
|
|
14
|
+
|
|
15
|
+
field :max_timestamp, -> { String }, optional: true, nullable: false
|
|
16
|
+
|
|
17
|
+
field :min_duration, -> { String }, optional: true, nullable: false
|
|
18
|
+
|
|
19
|
+
field :max_duration, -> { String }, optional: true, nullable: false
|
|
20
|
+
|
|
21
|
+
field :min_score, -> { String }, optional: true, nullable: false
|
|
22
|
+
|
|
23
|
+
field :max_score, -> { String }, optional: true, nullable: false
|
|
24
|
+
|
|
25
|
+
field :passed, -> { String }, optional: true, nullable: false
|
|
26
|
+
|
|
27
|
+
field :benchmark, -> { String }, optional: true, nullable: false
|
|
28
|
+
|
|
29
|
+
field :benchmark_run_id, -> { String }, optional: true, nullable: false
|
|
30
|
+
|
|
31
|
+
field :benchmark_question_id, -> { String }, optional: true, nullable: false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Evaluators
|
|
5
|
+
module Types
|
|
6
|
+
class ListEvaluationsResponse < Internal::Types::Model
|
|
7
|
+
field :data, -> { Internal::Types::Array[Internal::Types::Hash[String, Object]] }, optional: true, nullable: false
|
|
8
|
+
|
|
9
|
+
field :total, -> { Integer }, optional: true, nullable: false
|
|
10
|
+
|
|
11
|
+
field :page, -> { Integer }, optional: true, nullable: false
|
|
12
|
+
|
|
13
|
+
field :per_page, -> { Integer }, optional: true, nullable: false
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Internal
|
|
5
|
+
module Http
|
|
6
|
+
# @api private
|
|
7
|
+
class BaseRequest
|
|
8
|
+
attr_reader :base_url, :path, :method, :headers, :query, :request_options
|
|
9
|
+
|
|
10
|
+
# @param base_url [String] The base URL for the request
|
|
11
|
+
# @param path [String] The path for the request
|
|
12
|
+
# @param method [String] The HTTP method for the request (:get, :post, etc.)
|
|
13
|
+
# @param headers [Hash] Additional headers for the request (optional)
|
|
14
|
+
# @param query [Hash] Query parameters for the request (optional)
|
|
15
|
+
# @param request_options [Apologist::RequestOptions, Hash{Symbol=>Object}, nil]
|
|
16
|
+
def initialize(base_url:, path:, method:, headers: {}, query: {}, request_options: {})
|
|
17
|
+
@base_url = base_url
|
|
18
|
+
@path = path
|
|
19
|
+
@method = method
|
|
20
|
+
@headers = headers
|
|
21
|
+
@query = query
|
|
22
|
+
@request_options = request_options
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [Hash] The query parameters merged with additional query parameters from request options.
|
|
26
|
+
def encode_query
|
|
27
|
+
additional_query = @request_options&.dig(:additional_query_parameters) || @request_options&.dig("additional_query_parameters") || {}
|
|
28
|
+
@query.merge(additional_query)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Child classes should implement:
|
|
32
|
+
# - encode_headers: Returns the encoded HTTP request headers.
|
|
33
|
+
# - encode_body: Returns the encoded HTTP request body.
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Merges additional_headers from request_options into sdk_headers, filtering out
|
|
38
|
+
# any keys that collide with SDK-set or client-protected headers (case-insensitive).
|
|
39
|
+
# @param sdk_headers [Hash] Headers set by the SDK for this request type.
|
|
40
|
+
# @param protected_keys [Array<String>] Additional header keys that must not be overridden.
|
|
41
|
+
# @return [Hash] The merged headers.
|
|
42
|
+
def merge_additional_headers(sdk_headers, protected_keys: [])
|
|
43
|
+
additional_headers = @request_options&.dig(:additional_headers) || @request_options&.dig("additional_headers") || {}
|
|
44
|
+
all_protected = (sdk_headers.keys + protected_keys).to_set { |k| k.to_s.downcase }
|
|
45
|
+
filtered = additional_headers.reject { |key, _| all_protected.include?(key.to_s.downcase) }
|
|
46
|
+
sdk_headers.merge(filtered)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apologist
|
|
4
|
+
module Internal
|
|
5
|
+
module Http
|
|
6
|
+
# @api private
|
|
7
|
+
class RawClient
|
|
8
|
+
# Default HTTP status codes that trigger a retry
|
|
9
|
+
RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504, 521, 522, 524].freeze
|
|
10
|
+
# Initial delay between retries in seconds
|
|
11
|
+
INITIAL_RETRY_DELAY = 0.5
|
|
12
|
+
# Maximum delay between retries in seconds
|
|
13
|
+
MAX_RETRY_DELAY = 60.0
|
|
14
|
+
# Jitter factor for randomizing retry delays (20%)
|
|
15
|
+
JITTER_FACTOR = 0.2
|
|
16
|
+
|
|
17
|
+
# @return [String] The base URL for requests
|
|
18
|
+
attr_reader :base_url
|
|
19
|
+
|
|
20
|
+
# @param base_url [String] The base url for the request.
|
|
21
|
+
# @param max_retries [Integer] The number of times to retry a failed request, defaults to 2.
|
|
22
|
+
# @param timeout [Float] The timeout for the request, defaults to 60.0 seconds.
|
|
23
|
+
# @param headers [Hash] The headers for the request.
|
|
24
|
+
# @param auth_provider [Object, nil] An optional auth provider responding to
|
|
25
|
+
# `auth_headers`. When present its headers are resolved on every request so
|
|
26
|
+
# token-based schemes (e.g. OAuth) can refresh an expired token mid-session.
|
|
27
|
+
def initialize(base_url:, max_retries: 2, timeout: 60.0, headers: {}, auth_provider: nil)
|
|
28
|
+
@base_url = base_url
|
|
29
|
+
@max_retries = max_retries
|
|
30
|
+
@timeout = timeout
|
|
31
|
+
@auth_provider = auth_provider
|
|
32
|
+
@default_headers = {
|
|
33
|
+
"X-Fern-Language": "Ruby",
|
|
34
|
+
"X-Fern-SDK-Name": "apologist",
|
|
35
|
+
"X-Fern-SDK-Version": "0.0.1"
|
|
36
|
+
}.merge(headers)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param request [Apologist::Internal::Http::BaseRequest] The HTTP request.
|
|
40
|
+
# @return [HTTP::Response] The HTTP response.
|
|
41
|
+
def send(request)
|
|
42
|
+
url = build_url(request)
|
|
43
|
+
# Resolve auth headers once per request (not per retry) so token-based
|
|
44
|
+
# providers refresh at most once here; static providers are cheap.
|
|
45
|
+
auth_headers = resolve_auth_headers
|
|
46
|
+
attempt = 0
|
|
47
|
+
response = nil
|
|
48
|
+
|
|
49
|
+
loop do
|
|
50
|
+
http_request = build_http_request(
|
|
51
|
+
url:,
|
|
52
|
+
method: request.method,
|
|
53
|
+
headers: request.encode_headers(protected_keys: @default_headers.keys + auth_headers.keys),
|
|
54
|
+
body: request.encode_body,
|
|
55
|
+
auth_headers: auth_headers
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
conn = connect(url)
|
|
59
|
+
conn.open_timeout = @timeout
|
|
60
|
+
conn.read_timeout = @timeout
|
|
61
|
+
conn.write_timeout = @timeout
|
|
62
|
+
conn.continue_timeout = @timeout
|
|
63
|
+
|
|
64
|
+
response = conn.request(http_request)
|
|
65
|
+
|
|
66
|
+
break unless should_retry?(response, attempt)
|
|
67
|
+
|
|
68
|
+
delay = retry_delay(response, attempt)
|
|
69
|
+
sleep(delay)
|
|
70
|
+
attempt += 1
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
response
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Determines if a request should be retried based on the response status code.
|
|
77
|
+
# @param response [Net::HTTPResponse] The HTTP response.
|
|
78
|
+
# @param attempt [Integer] The current retry attempt (0-indexed).
|
|
79
|
+
# @return [Boolean] Whether the request should be retried.
|
|
80
|
+
def should_retry?(response, attempt)
|
|
81
|
+
return false if attempt >= @max_retries
|
|
82
|
+
|
|
83
|
+
status = response.code.to_i
|
|
84
|
+
RETRYABLE_STATUSES.include?(status)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Calculates the delay before the next retry attempt using exponential backoff with jitter.
|
|
88
|
+
# Respects Retry-After header if present.
|
|
89
|
+
# @param response [Net::HTTPResponse] The HTTP response.
|
|
90
|
+
# @param attempt [Integer] The current retry attempt (0-indexed).
|
|
91
|
+
# @return [Float] The delay in seconds before the next retry.
|
|
92
|
+
def retry_delay(response, attempt)
|
|
93
|
+
# Check for Retry-After header (can be seconds or HTTP date)
|
|
94
|
+
retry_after = response["Retry-After"]
|
|
95
|
+
if retry_after
|
|
96
|
+
delay = parse_retry_after(retry_after)
|
|
97
|
+
return [delay, MAX_RETRY_DELAY].min if delay&.positive?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Exponential backoff with jitter: base_delay * 2^attempt
|
|
101
|
+
base_delay = INITIAL_RETRY_DELAY * (2**attempt)
|
|
102
|
+
add_jitter([base_delay, MAX_RETRY_DELAY].min)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Parses the Retry-After header value.
|
|
106
|
+
# @param value [String] The Retry-After header value (seconds or HTTP date).
|
|
107
|
+
# @return [Float, nil] The delay in seconds, or nil if parsing fails.
|
|
108
|
+
def parse_retry_after(value)
|
|
109
|
+
# Try parsing as integer (seconds)
|
|
110
|
+
seconds = Integer(value, exception: false)
|
|
111
|
+
return seconds.to_f if seconds
|
|
112
|
+
|
|
113
|
+
# Try parsing as HTTP date
|
|
114
|
+
begin
|
|
115
|
+
retry_time = Time.httpdate(value)
|
|
116
|
+
delay = retry_time - Time.now
|
|
117
|
+
delay.positive? ? delay : nil
|
|
118
|
+
rescue ArgumentError
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Adds random jitter to a delay value.
|
|
124
|
+
# @param delay [Float] The base delay in seconds.
|
|
125
|
+
# @return [Float] The delay with jitter applied.
|
|
126
|
+
def add_jitter(delay)
|
|
127
|
+
jitter = delay * JITTER_FACTOR * (rand - 0.5) * 2
|
|
128
|
+
[delay + jitter, 0].max
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
LOCALHOST_HOSTS = %w[localhost 127.0.0.1 [::1]].freeze
|
|
132
|
+
|
|
133
|
+
# @param request [Apologist::Internal::Http::BaseRequest] The HTTP request.
|
|
134
|
+
# @return [URI::Generic] The URL.
|
|
135
|
+
def build_url(request)
|
|
136
|
+
encoded_query = request.encode_query
|
|
137
|
+
|
|
138
|
+
# If the path is already an absolute URL, use it directly
|
|
139
|
+
if request.path.start_with?("http://", "https://")
|
|
140
|
+
url = request.path
|
|
141
|
+
url = "#{url}?#{encode_query(encoded_query)}" if encoded_query&.any?
|
|
142
|
+
parsed = URI.parse(url)
|
|
143
|
+
validate_https!(parsed)
|
|
144
|
+
return parsed
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
path = request.path.start_with?("/") ? request.path[1..] : request.path
|
|
148
|
+
base = request.base_url || @base_url
|
|
149
|
+
url = "#{base.chomp("/")}/#{path}"
|
|
150
|
+
url = "#{url}?#{encode_query(encoded_query)}" if encoded_query&.any?
|
|
151
|
+
parsed = URI.parse(url)
|
|
152
|
+
validate_https!(parsed)
|
|
153
|
+
parsed
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Raises if the URL uses http:// for a non-localhost host, which would
|
|
157
|
+
# send authentication credentials in plaintext.
|
|
158
|
+
# @param url [URI::Generic] The parsed URL.
|
|
159
|
+
def validate_https!(url)
|
|
160
|
+
return if url.scheme != "http"
|
|
161
|
+
return if LOCALHOST_HOSTS.include?(url.host)
|
|
162
|
+
|
|
163
|
+
raise ArgumentError,
|
|
164
|
+
"Refusing to send request to non-HTTPS URL: #{url}. " \
|
|
165
|
+
"HTTP is only allowed for localhost. Use HTTPS or pass a localhost URL."
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Resolves the auth headers to send with the next request. Delegates to the
|
|
169
|
+
# configured auth provider (if any) on every call so that token-based
|
|
170
|
+
# providers (e.g. OAuth client-credentials) can refresh an expired token
|
|
171
|
+
# before the request is sent. Returns an empty hash when no provider is set,
|
|
172
|
+
# which keeps the api-key / basic / bearer / no-auth paths unchanged.
|
|
173
|
+
# @return [Hash] The auth headers for the current request.
|
|
174
|
+
def resolve_auth_headers
|
|
175
|
+
return {} if @auth_provider.nil?
|
|
176
|
+
|
|
177
|
+
@auth_provider.auth_headers
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# @param url [URI::Generic] The url to the resource.
|
|
181
|
+
# @param method [String] The HTTP method to use.
|
|
182
|
+
# @param headers [Hash] The headers for the request.
|
|
183
|
+
# @param body [String, nil] The body for the request.
|
|
184
|
+
# @param auth_headers [Hash] The auth headers resolved for this request. These
|
|
185
|
+
# take precedence over the static default headers but not over per-request
|
|
186
|
+
# headers, mirroring the previous baked-header precedence.
|
|
187
|
+
# @return [HTTP::Request] The HTTP request.
|
|
188
|
+
def build_http_request(url:, method:, headers: {}, body: nil, auth_headers: {})
|
|
189
|
+
request = Net::HTTPGenericRequest.new(
|
|
190
|
+
method,
|
|
191
|
+
!body.nil?,
|
|
192
|
+
method != "HEAD",
|
|
193
|
+
url
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
request_headers = @default_headers.merge(auth_headers).merge(headers)
|
|
197
|
+
request_headers.each { |name, value| request[name] = value }
|
|
198
|
+
request.body = body if body
|
|
199
|
+
|
|
200
|
+
# Net::HTTP disables its transparent gzip/deflate decoding as soon as an
|
|
201
|
+
# Accept-Encoding header is set explicitly on the request. Re-enable it so
|
|
202
|
+
# that compressed response bodies are still inflated.
|
|
203
|
+
request.extend(DecodeContent) if request_headers.keys.any? { |name| name.to_s.casecmp("accept-encoding").zero? }
|
|
204
|
+
|
|
205
|
+
request
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Keeps Net::HTTP's transparent gzip/deflate response decoding enabled
|
|
209
|
+
# even when an Accept-Encoding header is set explicitly on the request.
|
|
210
|
+
# @api private
|
|
211
|
+
module DecodeContent
|
|
212
|
+
def decode_content # rubocop:disable Naming/PredicateMethod
|
|
213
|
+
true
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# @param query [Hash] The query for the request.
|
|
218
|
+
# @return [String, nil] The encoded query.
|
|
219
|
+
def encode_query(query)
|
|
220
|
+
query.to_h.empty? ? nil : URI.encode_www_form(query)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# @param url [URI::Generic] The url to connect to.
|
|
224
|
+
# @return [Net::HTTP] The HTTP connection.
|
|
225
|
+
def connect(url)
|
|
226
|
+
is_https = (url.scheme == "https")
|
|
227
|
+
|
|
228
|
+
port = if url.port
|
|
229
|
+
url.port
|
|
230
|
+
elsif is_https
|
|
231
|
+
Net::HTTP.https_default_port
|
|
232
|
+
else
|
|
233
|
+
Net::HTTP.http_default_port
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
http = Net::HTTP.new(url.host, port)
|
|
237
|
+
http.use_ssl = is_https
|
|
238
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER if is_https
|
|
239
|
+
# NOTE: We handle retries at the application level with HTTP status code awareness,
|
|
240
|
+
# so we set max_retries to 0 to disable Net::HTTP's built-in network-level retries.
|
|
241
|
+
http.max_retries = 0
|
|
242
|
+
http
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# @return [String]
|
|
246
|
+
def inspect
|
|
247
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} @base_url=#{@base_url.inspect}>"
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|