activeagent 1.0.0.rc1 → 1.0.1

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.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -1
  3. data/lib/active_agent/providers/_base_provider.rb +94 -82
  4. data/lib/active_agent/providers/anthropic/_types.rb +2 -2
  5. data/lib/active_agent/providers/anthropic/options.rb +4 -6
  6. data/lib/active_agent/providers/anthropic/request.rb +157 -78
  7. data/lib/active_agent/providers/anthropic/transforms.rb +482 -0
  8. data/lib/active_agent/providers/anthropic_provider.rb +159 -59
  9. data/lib/active_agent/providers/common/messages/_types.rb +46 -3
  10. data/lib/active_agent/providers/common/messages/assistant.rb +20 -4
  11. data/lib/active_agent/providers/common/responses/base.rb +118 -70
  12. data/lib/active_agent/providers/common/usage.rb +385 -0
  13. data/lib/active_agent/providers/concerns/instrumentation.rb +263 -0
  14. data/lib/active_agent/providers/concerns/previewable.rb +39 -5
  15. data/lib/active_agent/providers/concerns/tool_choice_clearing.rb +62 -0
  16. data/lib/active_agent/providers/log_subscriber.rb +64 -246
  17. data/lib/active_agent/providers/mock_provider.rb +23 -23
  18. data/lib/active_agent/providers/ollama/chat/request.rb +214 -35
  19. data/lib/active_agent/providers/ollama/chat/transforms.rb +135 -0
  20. data/lib/active_agent/providers/ollama/embedding/request.rb +160 -47
  21. data/lib/active_agent/providers/ollama/embedding/transforms.rb +160 -0
  22. data/lib/active_agent/providers/ollama_provider.rb +0 -1
  23. data/lib/active_agent/providers/open_ai/_base.rb +3 -2
  24. data/lib/active_agent/providers/open_ai/chat/_types.rb +13 -1
  25. data/lib/active_agent/providers/open_ai/chat/request.rb +132 -186
  26. data/lib/active_agent/providers/open_ai/chat/transforms.rb +444 -0
  27. data/lib/active_agent/providers/open_ai/chat_provider.rb +95 -36
  28. data/lib/active_agent/providers/open_ai/embedding/_types.rb +13 -2
  29. data/lib/active_agent/providers/open_ai/embedding/request.rb +38 -70
  30. data/lib/active_agent/providers/open_ai/embedding/transforms.rb +88 -0
  31. data/lib/active_agent/providers/open_ai/responses/_types.rb +1 -7
  32. data/lib/active_agent/providers/open_ai/responses/request.rb +116 -135
  33. data/lib/active_agent/providers/open_ai/responses/transforms.rb +363 -0
  34. data/lib/active_agent/providers/open_ai/responses_provider.rb +115 -30
  35. data/lib/active_agent/providers/open_ai_provider.rb +0 -3
  36. data/lib/active_agent/providers/open_router/_types.rb +27 -1
  37. data/lib/active_agent/providers/open_router/options.rb +49 -1
  38. data/lib/active_agent/providers/open_router/request.rb +252 -66
  39. data/lib/active_agent/providers/open_router/requests/_types.rb +0 -1
  40. data/lib/active_agent/providers/open_router/requests/messages/_types.rb +37 -40
  41. data/lib/active_agent/providers/open_router/requests/messages/content/file.rb +19 -3
  42. data/lib/active_agent/providers/open_router/requests/messages/content/files/details.rb +15 -4
  43. data/lib/active_agent/providers/open_router/requests/plugin.rb +19 -3
  44. data/lib/active_agent/providers/open_router/requests/plugins/pdf_config.rb +30 -8
  45. data/lib/active_agent/providers/open_router/requests/prediction.rb +17 -0
  46. data/lib/active_agent/providers/open_router/requests/provider_preferences/max_price.rb +41 -7
  47. data/lib/active_agent/providers/open_router/requests/provider_preferences.rb +60 -19
  48. data/lib/active_agent/providers/open_router/requests/response_format.rb +30 -2
  49. data/lib/active_agent/providers/open_router/transforms.rb +164 -0
  50. data/lib/active_agent/providers/open_router_provider.rb +23 -0
  51. data/lib/active_agent/version.rb +1 -1
  52. metadata +17 -160
  53. data/lib/active_agent/generation_provider/open_router/types.rb +0 -505
  54. data/lib/active_agent/generation_provider/xai_provider.rb +0 -144
  55. data/lib/active_agent/providers/anthropic/requests/_types.rb +0 -190
  56. data/lib/active_agent/providers/anthropic/requests/container_params.rb +0 -19
  57. data/lib/active_agent/providers/anthropic/requests/content/base.rb +0 -21
  58. data/lib/active_agent/providers/anthropic/requests/content/sources/base.rb +0 -22
  59. data/lib/active_agent/providers/anthropic/requests/context_management_config.rb +0 -18
  60. data/lib/active_agent/providers/anthropic/requests/messages/_types.rb +0 -189
  61. data/lib/active_agent/providers/anthropic/requests/messages/assistant.rb +0 -23
  62. data/lib/active_agent/providers/anthropic/requests/messages/base.rb +0 -63
  63. data/lib/active_agent/providers/anthropic/requests/messages/content/_types.rb +0 -143
  64. data/lib/active_agent/providers/anthropic/requests/messages/content/base.rb +0 -21
  65. data/lib/active_agent/providers/anthropic/requests/messages/content/document.rb +0 -26
  66. data/lib/active_agent/providers/anthropic/requests/messages/content/image.rb +0 -23
  67. data/lib/active_agent/providers/anthropic/requests/messages/content/redacted_thinking.rb +0 -21
  68. data/lib/active_agent/providers/anthropic/requests/messages/content/search_result.rb +0 -27
  69. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/_types.rb +0 -171
  70. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/base.rb +0 -22
  71. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_base64.rb +0 -25
  72. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_file.rb +0 -23
  73. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_text.rb +0 -25
  74. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_url.rb +0 -23
  75. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_base64.rb +0 -27
  76. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_file.rb +0 -23
  77. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_url.rb +0 -23
  78. data/lib/active_agent/providers/anthropic/requests/messages/content/text.rb +0 -22
  79. data/lib/active_agent/providers/anthropic/requests/messages/content/thinking.rb +0 -23
  80. data/lib/active_agent/providers/anthropic/requests/messages/content/tool_result.rb +0 -24
  81. data/lib/active_agent/providers/anthropic/requests/messages/content/tool_use.rb +0 -28
  82. data/lib/active_agent/providers/anthropic/requests/messages/user.rb +0 -21
  83. data/lib/active_agent/providers/anthropic/requests/metadata.rb +0 -18
  84. data/lib/active_agent/providers/anthropic/requests/response_format.rb +0 -22
  85. data/lib/active_agent/providers/anthropic/requests/thinking_config/_types.rb +0 -60
  86. data/lib/active_agent/providers/anthropic/requests/thinking_config/base.rb +0 -20
  87. data/lib/active_agent/providers/anthropic/requests/thinking_config/disabled.rb +0 -16
  88. data/lib/active_agent/providers/anthropic/requests/thinking_config/enabled.rb +0 -20
  89. data/lib/active_agent/providers/anthropic/requests/tool_choice/_types.rb +0 -78
  90. data/lib/active_agent/providers/anthropic/requests/tool_choice/any.rb +0 -17
  91. data/lib/active_agent/providers/anthropic/requests/tool_choice/auto.rb +0 -17
  92. data/lib/active_agent/providers/anthropic/requests/tool_choice/base.rb +0 -20
  93. data/lib/active_agent/providers/anthropic/requests/tool_choice/none.rb +0 -16
  94. data/lib/active_agent/providers/anthropic/requests/tool_choice/tool.rb +0 -20
  95. data/lib/active_agent/providers/ollama/chat/requests/_types.rb +0 -3
  96. data/lib/active_agent/providers/ollama/chat/requests/messages/_types.rb +0 -116
  97. data/lib/active_agent/providers/ollama/chat/requests/messages/assistant.rb +0 -19
  98. data/lib/active_agent/providers/ollama/chat/requests/messages/user.rb +0 -19
  99. data/lib/active_agent/providers/ollama/embedding/requests/_types.rb +0 -83
  100. data/lib/active_agent/providers/ollama/embedding/requests/options.rb +0 -104
  101. data/lib/active_agent/providers/open_ai/chat/requests/_types.rb +0 -229
  102. data/lib/active_agent/providers/open_ai/chat/requests/audio.rb +0 -24
  103. data/lib/active_agent/providers/open_ai/chat/requests/messages/_types.rb +0 -123
  104. data/lib/active_agent/providers/open_ai/chat/requests/messages/assistant.rb +0 -42
  105. data/lib/active_agent/providers/open_ai/chat/requests/messages/base.rb +0 -78
  106. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/_types.rb +0 -133
  107. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/audio.rb +0 -35
  108. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/base.rb +0 -24
  109. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/file.rb +0 -26
  110. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/_types.rb +0 -60
  111. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/details.rb +0 -41
  112. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/image.rb +0 -37
  113. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/refusal.rb +0 -25
  114. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/text.rb +0 -25
  115. data/lib/active_agent/providers/open_ai/chat/requests/messages/developer.rb +0 -25
  116. data/lib/active_agent/providers/open_ai/chat/requests/messages/function.rb +0 -25
  117. data/lib/active_agent/providers/open_ai/chat/requests/messages/system.rb +0 -25
  118. data/lib/active_agent/providers/open_ai/chat/requests/messages/tool.rb +0 -26
  119. data/lib/active_agent/providers/open_ai/chat/requests/messages/user.rb +0 -32
  120. data/lib/active_agent/providers/open_ai/chat/requests/prediction.rb +0 -46
  121. data/lib/active_agent/providers/open_ai/chat/requests/response_format.rb +0 -53
  122. data/lib/active_agent/providers/open_ai/chat/requests/stream_options.rb +0 -24
  123. data/lib/active_agent/providers/open_ai/chat/requests/tool_choice.rb +0 -26
  124. data/lib/active_agent/providers/open_ai/chat/requests/tools/_types.rb +0 -5
  125. data/lib/active_agent/providers/open_ai/chat/requests/tools/base.rb +0 -22
  126. data/lib/active_agent/providers/open_ai/chat/requests/tools/custom_tool.rb +0 -41
  127. data/lib/active_agent/providers/open_ai/chat/requests/tools/function_tool.rb +0 -51
  128. data/lib/active_agent/providers/open_ai/chat/requests/web_search_options.rb +0 -45
  129. data/lib/active_agent/providers/open_ai/embedding/requests/_types.rb +0 -49
  130. data/lib/active_agent/providers/open_ai/responses/requests/_types.rb +0 -231
  131. data/lib/active_agent/providers/open_ai/responses/requests/conversation.rb +0 -23
  132. data/lib/active_agent/providers/open_ai/responses/requests/inputs/_types.rb +0 -264
  133. data/lib/active_agent/providers/open_ai/responses/requests/inputs/assistant_message.rb +0 -22
  134. data/lib/active_agent/providers/open_ai/responses/requests/inputs/base.rb +0 -89
  135. data/lib/active_agent/providers/open_ai/responses/requests/inputs/code_interpreter_tool_call.rb +0 -30
  136. data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call.rb +0 -28
  137. data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call_output.rb +0 -33
  138. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/_types.rb +0 -207
  139. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/base.rb +0 -22
  140. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_audio.rb +0 -26
  141. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_file.rb +0 -28
  142. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_image.rb +0 -28
  143. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_text.rb +0 -25
  144. data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call.rb +0 -28
  145. data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call_output.rb +0 -27
  146. data/lib/active_agent/providers/open_ai/responses/requests/inputs/developer_message.rb +0 -20
  147. data/lib/active_agent/providers/open_ai/responses/requests/inputs/file_search_tool_call.rb +0 -25
  148. data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_call_output.rb +0 -32
  149. data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_tool_call.rb +0 -28
  150. data/lib/active_agent/providers/open_ai/responses/requests/inputs/image_gen_tool_call.rb +0 -27
  151. data/lib/active_agent/providers/open_ai/responses/requests/inputs/input_message.rb +0 -31
  152. data/lib/active_agent/providers/open_ai/responses/requests/inputs/item_reference.rb +0 -23
  153. data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call.rb +0 -26
  154. data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call_output.rb +0 -33
  155. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_request.rb +0 -30
  156. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_response.rb +0 -28
  157. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_list_tools.rb +0 -29
  158. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_tool_call.rb +0 -35
  159. data/lib/active_agent/providers/open_ai/responses/requests/inputs/output_message.rb +0 -35
  160. data/lib/active_agent/providers/open_ai/responses/requests/inputs/reasoning.rb +0 -33
  161. data/lib/active_agent/providers/open_ai/responses/requests/inputs/system_message.rb +0 -20
  162. data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_call_base.rb +0 -27
  163. data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_message.rb +0 -23
  164. data/lib/active_agent/providers/open_ai/responses/requests/inputs/user_message.rb +0 -20
  165. data/lib/active_agent/providers/open_ai/responses/requests/inputs/web_search_tool_call.rb +0 -24
  166. data/lib/active_agent/providers/open_ai/responses/requests/prompt_reference.rb +0 -23
  167. data/lib/active_agent/providers/open_ai/responses/requests/reasoning.rb +0 -23
  168. data/lib/active_agent/providers/open_ai/responses/requests/stream_options.rb +0 -20
  169. data/lib/active_agent/providers/open_ai/responses/requests/text/_types.rb +0 -89
  170. data/lib/active_agent/providers/open_ai/responses/requests/text/base.rb +0 -22
  171. data/lib/active_agent/providers/open_ai/responses/requests/text/json_object.rb +0 -20
  172. data/lib/active_agent/providers/open_ai/responses/requests/text/json_schema.rb +0 -48
  173. data/lib/active_agent/providers/open_ai/responses/requests/text/plain.rb +0 -20
  174. data/lib/active_agent/providers/open_ai/responses/requests/text.rb +0 -41
  175. data/lib/active_agent/providers/open_ai/responses/requests/tool_choice.rb +0 -26
  176. data/lib/active_agent/providers/open_ai/responses/requests/tools/_types.rb +0 -112
  177. data/lib/active_agent/providers/open_ai/responses/requests/tools/base.rb +0 -25
  178. data/lib/active_agent/providers/open_ai/responses/requests/tools/code_interpreter_tool.rb +0 -23
  179. data/lib/active_agent/providers/open_ai/responses/requests/tools/computer_tool.rb +0 -27
  180. data/lib/active_agent/providers/open_ai/responses/requests/tools/custom_tool.rb +0 -28
  181. data/lib/active_agent/providers/open_ai/responses/requests/tools/file_search_tool.rb +0 -27
  182. data/lib/active_agent/providers/open_ai/responses/requests/tools/function_tool.rb +0 -29
  183. data/lib/active_agent/providers/open_ai/responses/requests/tools/image_generation_tool.rb +0 -37
  184. data/lib/active_agent/providers/open_ai/responses/requests/tools/local_shell_tool.rb +0 -21
  185. data/lib/active_agent/providers/open_ai/responses/requests/tools/mcp_tool.rb +0 -41
  186. data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_preview_tool.rb +0 -24
  187. data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_tool.rb +0 -25
  188. data/lib/active_agent/providers/open_ai/schema.yml +0 -65937
  189. data/lib/active_agent/providers/open_router/requests/message.rb +0 -1
  190. data/lib/active_agent/providers/open_router/requests/messages/assistant.rb +0 -20
  191. data/lib/active_agent/providers/open_router/requests/messages/user.rb +0 -30
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "requests/_types"
4
3
  require_relative "request"
5
4
 
6
5
  module ActiveAgent
7
6
  module Providers
8
7
  module OpenAI
9
8
  module Embedding
10
- # Type for Request model
9
+ # ActiveModel type for casting and serializing embedding requests
11
10
  class RequestType < ActiveModel::Type::Value
11
+ # Casts value to Request object
12
+ #
13
+ # @param value [Request, Hash, nil]
14
+ # @return [Request, nil]
15
+ # @raise [ArgumentError] when value cannot be cast
12
16
  def cast(value)
13
17
  case value
14
18
  when Request
@@ -22,6 +26,11 @@ module ActiveAgent
22
26
  end
23
27
  end
24
28
 
29
+ # Serializes Request to hash for API submission
30
+ #
31
+ # @param value [Request, Hash, nil]
32
+ # @return [Hash, nil]
33
+ # @raise [ArgumentError] when value cannot be serialized
25
34
  def serialize(value)
26
35
  case value
27
36
  when Request
@@ -35,6 +44,8 @@ module ActiveAgent
35
44
  end
36
45
  end
37
46
 
47
+ # @param value [Object]
48
+ # @return [Request, nil]
38
49
  def deserialize(value)
39
50
  cast(value)
40
51
  end
@@ -1,82 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_agent/providers/common/model"
4
- require_relative "_types"
3
+ require "json"
4
+ require_relative "transforms"
5
5
 
6
6
  module ActiveAgent
7
7
  module Providers
8
8
  module OpenAI
9
9
  module Embedding
10
- class Request < Common::BaseModel
11
- # Input text to embed (required)
12
- # Can be a string or array of strings or array of token arrays
13
- # - Must not exceed max input tokens for the model (8192 for all embedding models)
14
- # - Cannot be an empty string
15
- # - Arrays must be 2048 dimensions or less
16
- # - Maximum of 300,000 tokens summed across all inputs in a single request
17
- attribute :input, Requests::InputType.new
18
-
19
- # Model ID (required)
20
- attribute :model, :string
21
-
22
- # Number of dimensions for output embeddings (optional)
23
- # Only supported in text-embedding-3 and later models
24
- attribute :dimensions, :integer
25
-
26
- # Format for returned embeddings (optional)
27
- # Can be "float" or "base64"
28
- attribute :encoding_format, :string, default: "float"
29
-
30
- # Unique identifier for end-user (optional, deprecated)
31
- # Can help OpenAI monitor and detect abuse
32
- attribute :user, :string
33
-
34
- # Validations
35
- validates :input, :model, presence: true
36
- validates :encoding_format, inclusion: { in: %w[float base64] }, allow_nil: true
37
- validates :dimensions, numericality: { greater_than: 0 }, allow_nil: true
38
-
39
- # Custom validations
40
- validate :validate_input_format
41
- validate :validate_input_not_empty
42
-
43
- private
44
-
45
- def validate_input_format
46
- return if input.nil?
47
-
48
- unless input.is_a?(Array)
49
- errors.add(:input, "must be stored as an array internally")
50
- return
51
- end
52
-
53
- # Validate array contents
54
- input.each_with_index do |item, index|
55
- case item
56
- when String
57
- if item.empty?
58
- errors.add(:input, "cannot contain empty strings at index #{index}")
59
- end
60
- when Array
61
- # Token array validation
62
- if item.length > 2048
63
- errors.add(:input, "token arrays must be 2048 dimensions or less at index #{index}")
64
- end
65
- if item.empty?
66
- errors.add(:input, "cannot contain empty token arrays at index #{index}")
67
- end
68
- else
69
- errors.add(:input, "array elements must be strings or token arrays at index #{index}")
70
- end
71
- end
10
+ # Wraps OpenAI gem's EmbeddingCreateParams with normalization
11
+ #
12
+ # Delegates to OpenAI::Models::EmbeddingCreateParams while providing
13
+ # parameter normalization via the Transforms module
14
+ class Request < SimpleDelegator
15
+ # Default parameter values applied during initialization
16
+ DEFAULTS = {}.freeze
17
+
18
+ # Creates a new embedding request
19
+ #
20
+ # @param params [Hash] embedding parameters
21
+ # @option params [String, Array<String>, Array<Integer>, Array<Array<Integer>>] :input
22
+ # text or token array(s) to embed
23
+ # @option params [String] :model embedding model identifier
24
+ # @option params [Integer, nil] :dimensions number of dimensions for output (text-embedding-3 only)
25
+ # @option params [String, nil] :encoding_format "float" or "base64"
26
+ # @option params [String, nil] :user unique user identifier
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
36
+ super(gem_model)
37
+ rescue ArgumentError => e
38
+ # Re-raise with more context
39
+ raise ArgumentError, "Invalid OpenAI Embedding request parameters: #{e.message}"
72
40
  end
73
41
 
74
- def validate_input_not_empty
75
- return if input.nil?
76
-
77
- if input.is_a?(Array) && input.empty?
78
- errors.add(:input, "cannot be an empty array")
79
- end
42
+ # Serializes request for API submission
43
+ #
44
+ # @return [Hash] cleaned request hash without nil values
45
+ def serialize
46
+ serialized = Transforms.gem_to_hash(__getobj__)
47
+ Transforms.cleanup_serialized_request(serialized, DEFAULTS, __getobj__)
80
48
  end
81
49
  end
82
50
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAgent
4
+ module Providers
5
+ module OpenAI
6
+ # Handles transformation and normalization of embedding request parameters
7
+ # for the OpenAI Embeddings API
8
+ module Embedding
9
+ # Provides transformation methods for normalizing embedding parameters
10
+ # to OpenAI gem's native format
11
+ module Transforms
12
+ # Converts OpenAI gem objects to hash representation
13
+ #
14
+ # @param obj [Object] gem object or primitive value
15
+ # @return [Hash, Object] hash if object supports JSON serialization, otherwise original object
16
+ def self.gem_to_hash(obj)
17
+ if obj.respond_to?(:to_json)
18
+ JSON.parse(obj.to_json)
19
+ else
20
+ obj
21
+ end
22
+ end
23
+
24
+ # Normalizes all embedding request parameters
25
+ #
26
+ # @param params [Hash] raw request parameters
27
+ # @return [Hash] normalized parameters
28
+ def self.normalize_params(params)
29
+ normalized = params.dup
30
+
31
+ if normalized[:input]
32
+ normalized[:input] = normalize_input(normalized[:input])
33
+ end
34
+
35
+ normalized
36
+ end
37
+
38
+ # Normalizes input parameter to supported format
39
+ #
40
+ # Handles multiple input formats:
41
+ # - `"text"` - single string for one embedding
42
+ # - `["text1", "text2"]` - array of strings for multiple embeddings
43
+ # - `[1, 2, 3]` - token array for single embedding
44
+ # - `[[1, 2], [3, 4]]` - array of token arrays for multiple embeddings
45
+ #
46
+ # @param input [String, Array<String>, Array<Integer>, Array<Array<Integer>>]
47
+ # @return [String, Array] normalized input in gem-compatible format
48
+ def self.normalize_input(input)
49
+ case input
50
+ when String
51
+ input
52
+ when Array
53
+ if input.empty?
54
+ input
55
+ elsif input.first.is_a?(Integer)
56
+ input
57
+ elsif input.first.is_a?(Array)
58
+ input
59
+ else
60
+ input
61
+ end
62
+ else
63
+ input
64
+ end
65
+ end
66
+
67
+ # Removes nil values from serialized request
68
+ #
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)
72
+ # @return [Hash] cleaned request hash
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
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -24,13 +24,7 @@ module ActiveAgent
24
24
  def serialize(value)
25
25
  case value
26
26
  when Request
27
- hash = value.serialize
28
-
29
- if hash[:input] in [ { role: "user", content: String } ]
30
- hash[:input] = hash[:input][0][:content]
31
- end
32
-
33
- hash
27
+ value.serialize
34
28
  when Hash
35
29
  value
36
30
  when nil
@@ -1,160 +1,141 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_agent/providers/common/model"
4
- require_relative "requests/_types"
3
+ require_relative "transforms"
5
4
 
6
5
  module ActiveAgent
7
6
  module Providers
8
7
  module OpenAI
9
8
  module Responses
10
- class Request < Common::BaseModel
11
- # Background execution
12
- attribute :background, :boolean, default: false
13
-
14
- # Conversation
15
- attribute :conversation, Requests::ConversationType.new
16
-
17
- # Include additional output data
18
- attribute :include, default: -> { [] } # Array of strings
19
-
20
- # Input items (text, image, or file inputs)
21
- attribute :input, Requests::Inputs::MessagesType.new
22
-
23
- # Instructions (system/developer message)
24
- attribute :instructions, :string
25
-
26
- # Token limits
27
- attribute :max_output_tokens, :integer
28
- attribute :max_tool_calls, :integer
29
-
30
- # Metadata
31
- attribute :metadata # Hash of key-value pairs
32
-
33
- # Model ID
34
- attribute :model, :string
35
-
36
- # Parallel tool calls
37
- attribute :parallel_tool_calls, :boolean, default: true
38
-
39
- # Previous response ID for multi-turn conversations
40
- attribute :previous_response_id, :string
41
-
42
- # Prompt template reference
43
- attribute :prompt, Requests::PromptReferenceType.new
44
-
45
- # Cache key for optimization
46
- attribute :prompt_cache_key, :string
47
-
48
- # Reasoning configuration (for o-series and gpt-5 models)
49
- attribute :reasoning, Requests::ReasoningType.new
50
-
51
- # Safety identifier for usage policy detection
52
- attribute :safety_identifier, :string
53
-
54
- # Service tier
55
- attribute :service_tier, :string, default: "auto"
56
-
57
- # Storage
58
- attribute :store, :boolean, default: true
59
-
60
- # Streaming
61
- attribute :stream, :boolean, default: false
62
- attribute :stream_options, Requests::StreamOptionsType.new
63
-
64
- # Temperature sampling
65
- attribute :temperature, :float, default: 1
66
-
67
- # Text response configuration
68
- attribute :text, Requests::TextType.new
69
-
70
- # Tool choice
71
- attribute :tool_choice, Requests::ToolChoiceType.new
72
-
73
- # Tools array
74
- attribute :tools, Requests::Tools::ToolsType.new
75
-
76
- # Top logprobs
77
- attribute :top_logprobs, :integer
78
-
79
- # Top P sampling
80
- attribute :top_p, :float, default: 1
81
-
82
- # Truncation strategy
83
- attribute :truncation, :string, default: "disabled"
9
+ # Wraps OpenAI gem's ResponseCreateParams with field mapping and normalization
10
+ #
11
+ # Delegates to OpenAI::Models::Responses::ResponseCreateParams while providing
12
+ # compatibility layer for common format fields and content normalization:
13
+ # - `messages` → `input`
14
+ # - `response_format` → `text` (via ResponseTextConfig)
15
+ # - Instructions array joined to string
16
+ class Request < SimpleDelegator
17
+ # Default parameter values applied during initialization
18
+ DEFAULTS = {
19
+ service_tier: "auto",
20
+ store: true,
21
+ temperature: 1.0,
22
+ top_p: 1.0,
23
+ truncation: "disabled",
24
+ parallel_tool_calls: true,
25
+ background: false,
26
+ include: []
27
+ }.freeze
28
+
29
+ # @return [Boolean, nil]
30
+ attr_reader :stream
31
+
32
+ # @return [Hash, nil]
33
+ attr_reader :response_format
34
+
35
+ # Creates a new response creation request
36
+ #
37
+ # Maps common format fields to Responses API format:
38
+ # - `messages` `input`
39
+ # - `response_format` → `text` parameter
40
+ # - Instructions array → joined string
41
+ #
42
+ # @param params [Hash] request parameters
43
+ # @option params [String] :model required model identifier
44
+ # @option params [Array, String, Hash] :input messages or content
45
+ # @option params [Array, String, Hash] :messages alternative to :input (mapped automatically)
46
+ # @option params [Hash, String, Symbol] :response_format
47
+ # @option params [Array<String>, String] :instructions
48
+ # @option params [Integer] :max_output_tokens
49
+ # @raise [ArgumentError] when parameters are invalid
50
+ def initialize(**params)
51
+ # Step 1: Extract custom fields
52
+ @stream = params[:stream]
53
+ @response_format = params.delete(:response_format)
54
+
55
+ # Step 2: Map common format 'messages' to OpenAI Responses 'input'
56
+ if params.key?(:messages)
57
+ params[:input] = params.delete(:messages)
58
+ end
84
59
 
85
- # User identifier (deprecated, use safety_identifier or prompt_cache_key)
86
- attribute :user, :string
60
+ # Step 3: Join instructions array into string (like Chat API)
61
+ if params[:instructions].is_a?(Array)
62
+ params[:instructions] = params[:instructions].join("\n")
63
+ end
87
64
 
88
- # Validations
89
- validates :max_output_tokens, numericality: { greater_than: 0 }, allow_nil: true
90
- validates :max_tool_calls, numericality: { greater_than: 0 }, allow_nil: true
91
- validates :temperature, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 2 }, allow_nil: true
92
- validates :top_p, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
93
- validates :top_logprobs, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 20 }, allow_nil: true
94
- validates :service_tier, inclusion: { in: %w[auto default flex priority] }, allow_nil: true
95
- validates :truncation, inclusion: { in: %w[auto disabled] }, allow_nil: true
65
+ # Step 4: Map response_format to text parameter for Responses API
66
+ if @response_format
67
+ params[:text] = Responses::Transforms.normalize_response_format(@response_format)
68
+ end
96
69
 
97
- validate :validate_conversation_exclusivity
98
- validate :validate_metadata_format
99
- validate :validate_include_values
70
+ # Step 5: Apply defaults
71
+ params = apply_defaults(params)
72
+
73
+ # Step 6: Normalize input content for gem compatibility
74
+ params[:input] = Responses::Transforms.normalize_input(params[:input]) if params[:input]
75
+
76
+ # Step 7: Normalize tools and tool_choice from common format
77
+ params[:tools] = Responses::Transforms.normalize_tools(params[:tools]) if params[:tools]
78
+ params[:tool_choice] = Responses::Transforms.normalize_tool_choice(params[:tool_choice]) if params[:tool_choice]
79
+
80
+ # Step 8: Normalize MCP servers from common format (mcps parameter)
81
+ # OpenAI treats MCP servers as a special type of tool in the tools array
82
+ mcp_param = params[:mcps] || params[:mcp_servers]
83
+ if mcp_param&.any?
84
+ normalized_mcp_tools = Responses::Transforms.normalize_mcp_servers(mcp_param)
85
+ params.delete(:mcps)
86
+ params.delete(:mcp_servers)
87
+ # Merge MCP servers into tools array
88
+ params[:tools] = (params[:tools] || []) + normalized_mcp_tools
89
+ end
100
90
 
101
- # Common Format Mapping
102
- alias_attribute :messages, :input
103
- alias_attribute :message, :input
104
- alias_attribute :response_format, :text
91
+ # Step 9: Create gem model - delegates to OpenAI gem
92
+ gem_model = ::OpenAI::Models::Responses::ResponseCreateParams.new(**params)
105
93
 
106
- # Common Format Compatability
107
- def instructions=(value)
108
- super(value.is_a?(Array) ? value.join("\n") : value)
94
+ # Step 10: Delegate all method calls to gem model
95
+ super(gem_model)
96
+ rescue ArgumentError => e
97
+ # Re-raise with more context
98
+ raise ArgumentError, "Invalid OpenAI Responses request parameters: #{e.message}"
109
99
  end
110
100
 
111
- private
101
+ # Serializes request for API call
102
+ #
103
+ # Uses gem's JSON serialization and delegates cleanup to Transforms module.
104
+ #
105
+ # @return [Hash] cleaned request hash
106
+ def serialize
107
+ hash = Responses::Transforms.gem_to_hash(__getobj__)
108
+ Responses::Transforms.cleanup_serialized_request(hash, DEFAULTS, __getobj__)
109
+ end
112
110
 
113
- def validate_conversation_exclusivity
114
- if conversation.present? && previous_response_id.present?
115
- errors.add(:base, "Cannot use both conversation and previous_response_id")
116
- end
111
+ # @return [Array, String, Hash, nil]
112
+ def messages
113
+ __getobj__.instance_variable_get(:@data)[:input]
117
114
  end
118
115
 
119
- def validate_metadata_format
120
- return if metadata.nil?
116
+ # Sets input messages with normalization
117
+ #
118
+ # @param value [Array, String, Hash]
119
+ # @return [void]
120
+ def messages=(value)
121
+ normalized_value = Responses::Transforms.normalize_input(value)
122
+ __getobj__.instance_variable_get(:@data)[:input] = normalized_value
123
+ end
121
124
 
122
- unless metadata.is_a?(Hash)
123
- errors.add(:metadata, "must be a hash")
124
- return
125
- end
125
+ alias_method :message, :messages
126
+ alias_method :message=, :messages=
126
127
 
127
- metadata.each do |key, value|
128
- if key.to_s.length > 64
129
- errors.add(:metadata, "keys must be 64 characters or less")
130
- end
131
- if value.to_s.length > 512
132
- errors.add(:metadata, "values must be 512 characters or less")
133
- end
134
- end
128
+ private
135
129
 
136
- if metadata.size > 16
137
- errors.add(:metadata, "must have 16 key-value pairs or less")
130
+ # @api private
131
+ # @param params [Hash]
132
+ # @return [Hash]
133
+ def apply_defaults(params)
134
+ DEFAULTS.each do |key, value|
135
+ params[key] = value unless params.key?(key)
138
136
  end
139
- end
140
137
 
141
- def validate_include_values
142
- return if include.nil? || include.empty?
143
-
144
- valid_include_values = [
145
- "web_search_call.action.sources",
146
- "code_interpreter_call.outputs",
147
- "computer_call_output.output.image_url",
148
- "file_search_call.results",
149
- "message.input_image.image_url",
150
- "message.output_text.logprobs",
151
- "reasoning.encrypted_content"
152
- ]
153
-
154
- invalid_values = include - valid_include_values
155
- if invalid_values.any?
156
- errors.add(:include, "contains invalid values: #{invalid_values.join(', ')}")
157
- end
138
+ params
158
139
  end
159
140
  end
160
141
  end