Skip to content

Commit 12b4f1a

Browse files
committed
Refactor request handling across various providers to improve parameter normalization and cleanup processes
1 parent 7088a6f commit 12b4f1a

10 files changed

Lines changed: 131 additions & 79 deletions

File tree

lib/active_agent/providers/anthropic/request.rb

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,61 +69,42 @@ class Request < SimpleDelegator
6969
# @option params [Hash] :response_format custom field for JSON mode simulation
7070
# @raise [ArgumentError] when gem model validation fails
7171
def initialize(**params)
72-
# Extract custom fields that gem doesn't support
72+
# Step 1: Extract custom fields that gem doesn't support
7373
@response_format = params.delete(:response_format)
7474
@stream = params.delete(:stream)
7575

76-
# Map common format 'instructions' to Anthropic's 'system'
76+
# Step 2: Map common format 'instructions' to Anthropic's 'system'
7777
if params.key?(:instructions)
7878
params[:system] = params.delete(:instructions)
7979
end
8080

81-
# Apply defaults
81+
# Step 3: Apply defaults
8282
params = apply_defaults(params)
8383

84-
# Transform params for gem compatibility
84+
# Step 4: Transform params for gem compatibility
8585
transformed = Transforms.normalize_params(params)
8686

87-
# Create gem model - this validates all parameters!
87+
# Step 5: Create gem model - this validates all parameters!
8888
gem_model = ::Anthropic::Models::MessageCreateParams.new(**transformed)
8989

90-
# Delegate all method calls to gem model
90+
# Step 6: Delegate all method calls to gem model
9191
super(gem_model)
9292
rescue ArgumentError => e
9393
# Re-raise with more context
9494
raise ArgumentError, "Invalid Anthropic request parameters: #{e.message}"
9595
end
9696

97-
# Serializes request for API call with content compression.
97+
# Serializes request for API call.
9898
#
99-
# Uses gem's JSON serialization, then removes response-only fields
100-
# and compresses single text blocks to string shorthand for efficiency.
99+
# Uses gem's JSON serialization and delegates cleanup to Transforms module.
101100
#
102101
# @return [Hash]
103102
def serialize
104103
# Use gem's JSON serialization (handles all nested objects)
105104
hash = Anthropic::Transforms.gem_to_hash(__getobj__)
106105

107-
# Clean up messages - remove response-only fields
108-
if hash[:messages]
109-
hash[:messages].each do |msg|
110-
msg.delete(:id)
111-
msg.delete(:model)
112-
msg.delete(:stop_reason)
113-
msg.delete(:stop_sequence)
114-
msg.delete(:type)
115-
msg.delete(:usage)
116-
end
117-
end
118-
119-
# Apply content compression for API efficiency
120-
Transforms.compress_content(hash)
121-
122-
# Remove provider-internal fields that should not be in API request
123-
hash.delete(:mcp_servers) # Provider-level config, not API param
124-
hash.delete(:stop_sequences) if hash[:stop_sequences] == []
125-
126-
hash
106+
# Delegate cleanup to transforms module
107+
Transforms.cleanup_serialized_request(hash, DEFAULTS, __getobj__)
127108
end
128109

129110
# Accessor for system instructions.

lib/active_agent/providers/anthropic/transforms.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,45 @@ def compress_message_content!(msg)
291291
end
292292
end
293293

294+
# Cleans up serialized request for API submission.
295+
#
296+
# Removes response-only fields, applies content compression,
297+
# removes provider-internal fields, and removes default values.
298+
# Note: max_tokens is kept even if it matches default as Anthropic API requires it.
299+
#
300+
# @param hash [Hash] serialized request
301+
# @param defaults [Hash] default values to remove
302+
# @param gem_object [Object] original gem object (unused but for consistency)
303+
# @return [Hash] cleaned request hash
304+
def cleanup_serialized_request(hash, defaults, gem_object = nil)
305+
# Remove response-only fields from messages
306+
if hash[:messages]
307+
hash[:messages].each do |msg|
308+
msg.delete(:id)
309+
msg.delete(:model)
310+
msg.delete(:stop_reason)
311+
msg.delete(:stop_sequence)
312+
msg.delete(:type)
313+
msg.delete(:usage)
314+
end
315+
end
316+
317+
# Apply content compression for API efficiency
318+
compress_content(hash)
319+
320+
# Remove provider-internal fields that should not be in API request
321+
hash.delete(:mcp_servers) # Provider-level config, not API param
322+
hash.delete(:stop_sequences) if hash[:stop_sequences] == []
323+
324+
# Remove default values (except max_tokens which is required by API)
325+
defaults.each do |key, value|
326+
next if key == :max_tokens # Anthropic API requires max_tokens
327+
hash.delete(key) if hash[key] == value
328+
end
329+
330+
hash
331+
end
332+
294333
private
295334

296335
# Converts single text block to string.

lib/active_agent/providers/ollama/chat/request.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,26 +65,26 @@ class Request < SimpleDelegator
6565
# @option params [Boolean] :raw raw prompt mode
6666
# @raise [ArgumentError] when parameters are invalid
6767
def initialize(**params)
68-
# Extract stream flag
68+
# Step 1: Extract stream flag
6969
@stream = params[:stream]
7070

71-
# Apply defaults
71+
# Step 2: Apply defaults
7272
params = apply_defaults(params)
7373

74-
# Normalize parameters and split into OpenAI vs Ollama-specific
74+
# Step 3: Normalize parameters and split into OpenAI vs Ollama-specific
7575
openai_params, @ollama_params = Transforms.normalize_params(params)
7676

77-
# Validate Ollama-specific parameters
77+
# Step 4: Validate Ollama-specific parameters
7878
validate_format(@ollama_params[:format]) if @ollama_params[:format]
7979
validate_options(@ollama_params[:options]) if @ollama_params[:options]
8080

81-
# Create gem model with OpenAI-compatible params
81+
# Step 5: Create gem model with OpenAI-compatible params
8282
gem_model = ::OpenAI::Models::Chat::CompletionCreateParams.new(**openai_params)
8383

84-
# Delegate to the gem model
84+
# Step 6: Delegate to the gem model
8585
super(gem_model)
8686
rescue ArgumentError => e
87-
raise ArgumentError, "Invalid Ollama request parameters: #{e.message}"
87+
raise ArgumentError, "Invalid Ollama Chat request parameters: #{e.message}"
8888
end
8989

9090
# Serializes request for API submission

lib/active_agent/providers/ollama/embedding/request.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,23 @@ class Request < SimpleDelegator
6363
# @option params [Integer] :seed delegated to options
6464
# @raise [ArgumentError] when parameters are invalid
6565
def initialize(**params)
66-
# Apply defaults
66+
# Step 1: Apply defaults
6767
params = apply_defaults(params)
6868

69-
# Validate presence of required params before normalization
69+
# Step 2: Validate presence of required params before normalization
7070
raise ArgumentError, "model is required" unless params[:model]
7171
raise ArgumentError, "input is required" unless params[:input]
7272

73-
# Normalize parameters and split into OpenAI vs Ollama-specific
73+
# Step 3: Normalize parameters and split into OpenAI vs Ollama-specific
7474
openai_params, @ollama_params = Transforms.normalize_params(params)
7575

76-
# Create gem model with OpenAI-compatible params
76+
# Step 4: Create gem model with OpenAI-compatible params
7777
gem_model = ::OpenAI::Models::EmbeddingCreateParams.new(**openai_params)
7878

79-
# Delegate to the gem model
79+
# Step 5: Delegate to the gem model
8080
super(gem_model)
8181
rescue ArgumentError => e
82-
raise ArgumentError, "Invalid Ollama embedding request parameters: #{e.message}"
82+
raise ArgumentError, "Invalid Ollama Embedding request parameters: #{e.message}"
8383
end
8484

8585
# Serializes request for API submission

lib/active_agent/providers/open_ai/chat/request.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,19 @@ class Request < SimpleDelegator
6969
# @option params [Array] :tools available tool definitions
7070
# @raise [ArgumentError] when parameters are invalid
7171
def initialize(**params)
72-
# Extract stream flag
72+
# Step 1: Extract stream flag
7373
@stream = params[:stream]
7474

75-
# Apply defaults
75+
# Step 2: Apply defaults
7676
params = apply_defaults(params)
7777

78-
# Normalize all parameters (instructions, messages, response_format)
78+
# Step 3: Normalize all parameters (instructions, messages, response_format)
7979
params = Chat::Transforms.normalize_params(params)
8080

81-
# Create gem model - this validates all parameters!
81+
# Step 4: Create gem model - this validates all parameters!
8282
gem_model = ::OpenAI::Models::Chat::CompletionCreateParams.new(**params)
8383

84-
# Delegate all method calls to gem model
84+
# Step 5: Delegate all method calls to gem model
8585
super(gem_model)
8686
rescue ArgumentError => e
8787
# Re-raise with more context

lib/active_agent/providers/open_ai/embedding/request.rb

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ module Embedding
1212
# Delegates to OpenAI::Models::EmbeddingCreateParams while providing
1313
# parameter normalization via the Transforms module
1414
class Request < SimpleDelegator
15+
# Default parameter values applied during initialization
16+
DEFAULTS = {}.freeze
17+
1518
# Creates a new embedding request
1619
#
1720
# @param params [Hash] embedding parameters
@@ -21,18 +24,27 @@ class Request < SimpleDelegator
2124
# @option params [Integer, nil] :dimensions number of dimensions for output (text-embedding-3 only)
2225
# @option params [String, nil] :encoding_format "float" or "base64"
2326
# @option params [String, nil] :user unique user identifier
24-
def initialize(params = {})
25-
normalized_params = Transforms.normalize_params(params)
26-
gem_model = ::OpenAI::Models::EmbeddingCreateParams.new(**normalized_params)
27+
# @raise [ArgumentError] when parameters are invalid
28+
def initialize(**params)
29+
# Step 1: Normalize parameters
30+
params = Transforms.normalize_params(params)
31+
32+
# Step 2: Create gem model - this validates all parameters!
33+
gem_model = ::OpenAI::Models::EmbeddingCreateParams.new(**params)
34+
35+
# Step 3: Delegate all method calls to gem model
2736
super(gem_model)
37+
rescue ArgumentError => e
38+
# Re-raise with more context
39+
raise ArgumentError, "Invalid OpenAI Embedding request parameters: #{e.message}"
2840
end
2941

3042
# Serializes request for API submission
3143
#
3244
# @return [Hash] cleaned request hash without nil values
3345
def serialize
3446
serialized = Transforms.gem_to_hash(__getobj__)
35-
Transforms.cleanup_serialized_request(serialized)
47+
Transforms.cleanup_serialized_request(serialized, DEFAULTS, __getobj__)
3648
end
3749
end
3850
end

lib/active_agent/providers/open_ai/embedding/transforms.rb

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,20 @@ def self.normalize_input(input)
6666

6767
# Removes nil values from serialized request
6868
#
69-
# @param serialized [Hash]
69+
# @param serialized [Hash] serialized request
70+
# @param defaults [Hash] default values to remove
71+
# @param gem_object [Object] original gem object (unused but for consistency)
7072
# @return [Hash] cleaned request hash
71-
def self.cleanup_serialized_request(serialized)
72-
serialized.compact
73+
def self.cleanup_serialized_request(serialized, defaults = {}, gem_object = nil)
74+
# Remove nil values
75+
cleaned = serialized.compact
76+
77+
# Remove default values
78+
defaults.each do |key, value|
79+
cleaned.delete(key) if cleaned[key] == value
80+
end
81+
82+
cleaned
7383
end
7484
end
7585
end

lib/active_agent/providers/open_ai/responses/request.rb

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,35 +48,35 @@ class Request < SimpleDelegator
4848
# @option params [Integer] :max_output_tokens
4949
# @raise [ArgumentError] when parameters are invalid
5050
def initialize(**params)
51-
# Extract custom fields
51+
# Step 1: Extract custom fields
5252
@stream = params[:stream]
5353
@response_format = params.delete(:response_format)
5454

55-
# Map common format 'messages' to OpenAI Responses 'input'
55+
# Step 2: Map common format 'messages' to OpenAI Responses 'input'
5656
if params.key?(:messages)
5757
params[:input] = params.delete(:messages)
5858
end
5959

60-
# Join instructions array into string (like Chat API)
60+
# Step 3: Join instructions array into string (like Chat API)
6161
if params[:instructions].is_a?(Array)
6262
params[:instructions] = params[:instructions].join("\n")
6363
end
6464

65-
# Map response_format to text parameter for Responses API
65+
# Step 4: Map response_format to text parameter for Responses API
6666
if @response_format
6767
params[:text] = Responses::Transforms.normalize_response_format(@response_format)
6868
end
6969

70-
# Apply defaults
70+
# Step 5: Apply defaults
7171
params = apply_defaults(params)
7272

73-
# Normalize input content for gem compatibility
73+
# Step 6: Normalize input content for gem compatibility
7474
params[:input] = Responses::Transforms.normalize_input(params[:input]) if params[:input]
7575

76-
# Create gem model - delegates to OpenAI gem
76+
# Step 7: Create gem model - delegates to OpenAI gem
7777
gem_model = ::OpenAI::Models::Responses::ResponseCreateParams.new(**params)
7878

79-
# Delegate all method calls to gem model
79+
# Step 8: Delegate all method calls to gem model
8080
super(gem_model)
8181
rescue ArgumentError => e
8282
# Re-raise with more context
@@ -85,22 +85,12 @@ def initialize(**params)
8585

8686
# Serializes request for API call
8787
#
88-
# Removes default values for minimal request body and simplifies
89-
# single-element input arrays to strings where possible.
88+
# Uses gem's JSON serialization and delegates cleanup to Transforms module.
9089
#
9190
# @return [Hash] cleaned request hash
9291
def serialize
9392
hash = Responses::Transforms.gem_to_hash(__getobj__)
94-
95-
# Remove default values that shouldn't be in the request body
96-
DEFAULTS.each do |key, value|
97-
hash.delete(key) if hash[key] == value
98-
end
99-
100-
# Simplify input when possible for cleaner API requests
101-
hash[:input] = Responses::Transforms.simplify_input(hash[:input]) if hash[:input]
102-
103-
hash
93+
Responses::Transforms.cleanup_serialized_request(hash, DEFAULTS, __getobj__)
10494
end
10595

10696
# @return [Array, String, Hash, nil]

lib/active_agent/providers/open_ai/responses/transforms.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,26 @@ def normalize_message(message, context: :content)
200200
message
201201
end
202202
end
203+
204+
# Cleans up serialized request for API submission
205+
#
206+
# Removes default values and simplifies input where possible.
207+
#
208+
# @param hash [Hash] serialized request
209+
# @param defaults [Hash] default values to remove
210+
# @param gem_object [Object] original gem object
211+
# @return [Hash] cleaned request hash
212+
def cleanup_serialized_request(hash, defaults, gem_object)
213+
# Remove default values that shouldn't be in the request body
214+
defaults.each do |key, value|
215+
hash.delete(key) if hash[key] == value
216+
end
217+
218+
# Simplify input when possible for cleaner API requests
219+
hash[:input] = simplify_input(hash[:input]) if hash[:input]
220+
221+
hash
222+
end
203223
end
204224
end
205225
end

lib/active_agent/providers/open_router/request.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,20 @@ class Request < SimpleDelegator
7171
# @option params [Float] :repetition_penalty repetition penalty
7272
# @raise [ArgumentError] when parameters are invalid
7373
def initialize(**params)
74-
# Extract stream flag
74+
# Step 1: Extract stream flag
7575
@stream = params[:stream]
7676

77-
# Apply defaults
77+
# Step 2: Apply defaults
7878
params = apply_defaults(params)
7979

80-
# Normalize parameters and split into OpenAI vs OpenRouter-specific
80+
# Step 3: Normalize parameters and split into OpenAI vs OpenRouter-specific
8181
# This handles response_format special logic for structured output
8282
openai_params, @openrouter_params = Transforms.normalize_params(params)
8383

84-
# Create gem model with OpenAI-compatible params
84+
# Step 4: Create gem model with OpenAI-compatible params
8585
gem_model = ::OpenAI::Models::Chat::CompletionCreateParams.new(**openai_params)
8686

87-
# Delegate to the gem model
87+
# Step 5: Delegate to the gem model
8888
super(gem_model)
8989
rescue ArgumentError => e
9090
raise ArgumentError, "Invalid OpenRouter request parameters: #{e.message}"

0 commit comments

Comments
 (0)