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
@@ -4,8 +4,25 @@ module ActiveAgent
4
4
  module Providers
5
5
  module OpenRouter
6
6
  module Requests
7
+ # Prediction configuration for prefilling responses
8
+ #
9
+ # Allows prefilling the start of the model's response. When provided,
10
+ # the model continues from this predicted content.
11
+ #
12
+ # @example Content prediction
13
+ # prediction = Prediction.new(
14
+ # type: 'content',
15
+ # content: 'Once upon a time'
16
+ # )
17
+ #
18
+ # @see https://platform.openai.com/docs/api-reference/chat/create#chat-create-prediction
7
19
  class Prediction < Common::BaseModel
20
+ # @!attribute type
21
+ # @return [String] prediction type (currently only 'content' is supported)
8
22
  attribute :type, :string
23
+
24
+ # @!attribute content
25
+ # @return [String] predicted content to prefill the response
9
26
  attribute :content, :string
10
27
 
11
28
  validates :type, inclusion: { in: %w[content] }, allow_nil: true
@@ -5,14 +5,48 @@ module ActiveAgent
5
5
  module OpenRouter
6
6
  module Requests
7
7
  # Maximum price configuration for provider routing
8
- # Specifies USD price per million tokens for different operations
9
- # See: https://openrouter.ai/docs/provider-routing
8
+ #
9
+ # Specifies maximum acceptable prices (in USD per million tokens or per
10
+ # operation) for filtering providers. OpenRouter will only route to
11
+ # providers within these price constraints.
12
+ #
13
+ # @example Setting prompt and completion limits
14
+ # max_price = MaxPrice.new(
15
+ # prompt: 0.01, # $0.01 per million input tokens
16
+ # completion: 0.03 # $0.03 per million output tokens
17
+ # )
18
+ #
19
+ # @example Setting all constraints
20
+ # max_price = MaxPrice.new(
21
+ # prompt: 0.01,
22
+ # completion: 0.03,
23
+ # image: 0.001,
24
+ # audio: 0.002,
25
+ # request: 0.0001
26
+ # )
27
+ #
28
+ # @see https://openrouter.ai/docs/provider-routing OpenRouter Provider Routing
29
+ # @see ProviderPreferences
10
30
  class MaxPrice < Common::BaseModel
11
- attribute :prompt, :float # Price per million prompt tokens (input)
12
- attribute :completion, :float # Price per million completion tokens (output)
13
- attribute :image, :float # Price per image
14
- attribute :audio, :float # Price per audio unit
15
- attribute :request, :float # Price per request
31
+ # @!attribute prompt
32
+ # @return [Float, nil] maximum price per million prompt tokens (input)
33
+ attribute :prompt, :float
34
+
35
+ # @!attribute completion
36
+ # @return [Float, nil] maximum price per million completion tokens (output)
37
+ attribute :completion, :float
38
+
39
+ # @!attribute image
40
+ # @return [Float, nil] maximum price per image operation
41
+ attribute :image, :float
42
+
43
+ # @!attribute audio
44
+ # @return [Float, nil] maximum price per audio operation
45
+ attribute :audio, :float
46
+
47
+ # @!attribute request
48
+ # @return [Float, nil] maximum price per request
49
+ attribute :request, :float
16
50
 
17
51
  validates :prompt, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
18
52
  validates :completion, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
@@ -7,44 +7,85 @@ module ActiveAgent
7
7
  module OpenRouter
8
8
  module Requests
9
9
  # Provider preferences for routing requests to specific providers
10
- # See: https://openrouter.ai/docs/provider-routing
10
+ #
11
+ # Controls how OpenRouter selects and routes requests to underlying model
12
+ # providers. Enables filtering by parameters, cost constraints, privacy
13
+ # settings, and provider-specific preferences.
14
+ #
15
+ # @example Basic provider routing
16
+ # prefs = ProviderPreferences.new(
17
+ # require_parameters: true,
18
+ # allow_fallbacks: false
19
+ # )
20
+ #
21
+ # @example Privacy-focused routing
22
+ # prefs = ProviderPreferences.new(
23
+ # data_collection: 'deny',
24
+ # zdr: true
25
+ # )
26
+ #
27
+ # @example Cost-optimized routing
28
+ # prefs = ProviderPreferences.new(
29
+ # sort: 'price',
30
+ # max_price: { prompt: 0.01, completion: 0.03 }
31
+ # )
32
+ #
33
+ # @example Provider ordering
34
+ # prefs = ProviderPreferences.new(
35
+ # order: ['OpenAI', 'Anthropic'],
36
+ # ignore: ['Together']
37
+ # )
38
+ #
39
+ # @see https://openrouter.ai/docs/provider-routing OpenRouter Provider Routing
40
+ # @see MaxPrice
11
41
  class ProviderPreferences < Common::BaseModel
12
- # Whether to allow backup providers to serve requests
13
- # - true: (default) when primary provider is unavailable, use next best provider
14
- # - false: use only primary/custom provider, return upstream error if unavailable
42
+ # @!attribute allow_fallbacks
43
+ # @return [Boolean, nil] whether to allow backup providers when primary is unavailable
44
+ # - true: (default) use next best provider when primary unavailable
45
+ # - false: only use primary/custom provider, return error if unavailable
15
46
  attribute :allow_fallbacks, :boolean
16
47
 
17
- # Whether to filter providers to only those that support provided parameters
18
- # If false, providers receive only parameters they support and ignore the rest
48
+ # @!attribute require_parameters
49
+ # @return [Boolean, nil] whether to filter to providers supporting all parameters
50
+ # - true: only use providers that support all provided parameters
51
+ # - false: providers receive only the parameters they support
19
52
  attribute :require_parameters, :boolean
20
53
 
21
- # Data collection setting
22
- # - allow: (default) allow providers which store user data and may train on it
23
- # - deny: use only providers which do not collect user data
54
+ # @!attribute data_collection
55
+ # @return [String, nil] data collection preference
56
+ # - 'allow': (default) allow providers which store/train on user data
57
+ # - 'deny': only use providers that don't collect user data
24
58
  attribute :data_collection, :string
25
59
 
26
- # Zero Data Retention - stricter privacy mode
60
+ # @!attribute zdr
61
+ # @return [Boolean, nil] zero data retention mode (stricter privacy)
27
62
  attribute :zdr, :boolean
28
63
 
29
- # Ordered list of provider slugs to try in order
64
+ # @!attribute order
65
+ # @return [Array<String>] ordered list of provider slugs to try in sequence
30
66
  attribute :order, default: -> { [] }
31
67
 
32
- # List of provider slugs to allow (merged with account-wide settings)
68
+ # @!attribute only
69
+ # @return [Array<String>] allowlist of provider slugs (merged with account settings)
33
70
  attribute :only, default: -> { [] }
34
71
 
35
- # List of provider slugs to ignore (merged with account-wide settings)
72
+ # @!attribute ignore
73
+ # @return [Array<String>] blocklist of provider slugs (merged with account settings)
36
74
  attribute :ignore, default: -> { [] }
37
75
 
38
- # List of quantization levels to filter providers by
39
- # Options: int4, int8, fp4, fp6, fp8, fp16, bf16, fp32, unknown
76
+ # @!attribute quantizations
77
+ # @return [Array<String>] quantization levels to filter providers by
78
+ # Options: int4, int8, fp4, fp6, fp8, fp16, bf16, fp32, unknown
40
79
  attribute :quantizations, default: -> { [] }
41
80
 
42
- # Sorting strategy to use if "order" is not specified
43
- # Options: price, throughput, latency
44
- # When set, no load balancing is performed
81
+ # @!attribute sort
82
+ # @return [String, nil] sorting strategy when order not specified
83
+ # Options: price, throughput, latency
84
+ # Note: disables load balancing when set
45
85
  attribute :sort, :string
46
86
 
47
- # Maximum price constraints (USD per million tokens)
87
+ # @!attribute max_price
88
+ # @return [MaxPrice, nil] maximum price constraints per token/operation
48
89
  attribute :max_price, MaxPriceType.new
49
90
 
50
91
  # Validations matching the schema
@@ -4,11 +4,39 @@ module ActiveAgent
4
4
  module Providers
5
5
  module OpenRouter
6
6
  module Requests
7
+ # Response format configuration for structured output
8
+ #
9
+ # Enables JSON-formatted responses using OpenAI's structured output format.
10
+ # When using structured output, OpenRouter automatically sets
11
+ # provider.require_parameters=true to route to compatible models.
12
+ #
13
+ # @example JSON object format
14
+ # format = ResponseFormat.new(type: 'json_object')
15
+ #
16
+ # @example JSON schema format
17
+ # format = ResponseFormat.new(
18
+ # type: 'json_schema',
19
+ # json_schema: {
20
+ # name: 'user_profile',
21
+ # description: 'A user profile',
22
+ # schema: { type: 'object', properties: { name: { type: 'string' } } },
23
+ # strict: true
24
+ # }
25
+ # )
26
+ #
27
+ # @see https://openrouter.ai/docs/structured-outputs OpenRouter Structured Outputs
28
+ # @see https://platform.openai.com/docs/guides/structured-outputs OpenAI Structured Outputs
7
29
  class ResponseFormat < Common::BaseModel
8
- # Type of response format (json_object, json_schema)
30
+ # @!attribute type
31
+ # @return [String] response format type ('json_object' or 'json_schema')
9
32
  attribute :type, :string
10
33
 
11
- # JSON schema configuration (only for json_schema type)
34
+ # @!attribute json_schema
35
+ # @return [Hash, nil] JSON schema configuration (required when type is 'json_schema')
36
+ # @option json_schema [String] :name schema name (max 64 chars, alphanumeric/underscore/dash)
37
+ # @option json_schema [String] :description schema description
38
+ # @option json_schema [Hash] :schema JSON schema definition
39
+ # @option json_schema [Boolean] :strict whether to enforce strict schema adherence
12
40
  attribute :json_schema # Hash with name, description, schema, strict
13
41
 
14
42
  validates :type, inclusion: { in: %w[json_object json_schema] }, allow_nil: true
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/keys"
4
+ require_relative "../open_ai/chat/transforms"
5
+
6
+ module ActiveAgent
7
+ module Providers
8
+ module OpenRouter
9
+ # Provides transformation methods for normalizing OpenRouter parameters
10
+ # to work with OpenAI gem's native format plus OpenRouter extensions
11
+ #
12
+ # Leverages OpenAI::Chat::Transforms for base message normalization while
13
+ # adding handling for OpenRouter-specific parameters like plugins, provider
14
+ # preferences, and model fallbacks.
15
+ module Transforms
16
+ class << self
17
+ # Converts gem model object to hash via JSON round-trip
18
+ #
19
+ # @param gem_object [Object]
20
+ # @return [Hash] with symbolized keys
21
+ def gem_to_hash(gem_object)
22
+ OpenAI::Chat::Transforms.gem_to_hash(gem_object)
23
+ end
24
+
25
+ # Normalizes all request parameters for OpenRouter API
26
+ #
27
+ # Handles both OpenAI-compatible parameters and OpenRouter-specific extensions.
28
+ # OpenRouter-specific params (plugins, provider, transforms, models, route) are
29
+ # extracted and returned separately for manual serialization.
30
+ #
31
+ # @param params [Hash]
32
+ # @return [Array<Hash, Hash>] tuple of [openai_params, openrouter_params]
33
+ def normalize_params(params)
34
+ params = params.dup
35
+
36
+ # Extract OpenRouter-specific parameters
37
+ openrouter_params = {}
38
+ openrouter_params[:plugins] = params.delete(:plugins) if params.key?(:plugins)
39
+ openrouter_params[:provider] = params.delete(:provider) if params.key?(:provider)
40
+ openrouter_params[:transforms] = params.delete(:transforms) if params.key?(:transforms)
41
+ openrouter_params[:models] = params.delete(:models) if params.key?(:models)
42
+ openrouter_params[:route] = params.delete(:route) if params.key?(:route)
43
+
44
+ # Extract OpenRouter-specific sampling parameters not in OpenAI
45
+ openrouter_params[:top_k] = params.delete(:top_k) if params.key?(:top_k)
46
+ openrouter_params[:min_p] = params.delete(:min_p) if params.key?(:min_p)
47
+ openrouter_params[:top_a] = params.delete(:top_a) if params.key?(:top_a)
48
+ openrouter_params[:repetition_penalty] = params.delete(:repetition_penalty) if params.key?(:repetition_penalty)
49
+
50
+ # Handle response_format special logic for OpenRouter
51
+ # OpenRouter requires provider.require_parameters=true for structured output
52
+ if params[:response_format]
53
+ response_format = params[:response_format]
54
+ response_format_hash = response_format.is_a?(Hash) ? response_format : { type: response_format }
55
+
56
+ if %i[json_object json_schema].include?(response_format_hash[:type].to_sym)
57
+ openrouter_params[:provider] ||= {}
58
+ openrouter_params[:provider][:require_parameters] = true
59
+ end
60
+ end
61
+
62
+ # Use OpenAI transforms for the base parameters
63
+ openai_params = OpenAI::Chat::Transforms.normalize_params(params)
64
+
65
+ # Override tool_choice normalization for OpenRouter's "any" vs "required" difference
66
+ if openai_params[:tool_choice]
67
+ openai_params[:tool_choice] = normalize_tool_choice(openai_params[:tool_choice])
68
+ end
69
+
70
+ [ openai_params, openrouter_params ]
71
+ end
72
+
73
+ # Normalizes tools using OpenAI transforms
74
+ #
75
+ # @param tools [Array<Hash>]
76
+ # @return [Array<Hash>]
77
+ def normalize_tools(tools)
78
+ OpenAI::Chat::Transforms.normalize_tools(tools)
79
+ end
80
+
81
+ # Normalizes tool_choice for OpenRouter API differences
82
+ #
83
+ # OpenRouter uses "any" instead of OpenAI's "required" for forcing tool use.
84
+ # Converts common format to OpenRouter-specific format:
85
+ # - "required" (common) → "any" (OpenRouter)
86
+ # - Everything else delegates to OpenAI transforms
87
+ #
88
+ # @param tool_choice [String, Hash, Symbol]
89
+ # @return [String, Hash, Symbol]
90
+ def normalize_tool_choice(tool_choice)
91
+ # Convert "required" to OpenRouter's "any"
92
+ return "any" if tool_choice.to_s == "required"
93
+
94
+ # For everything else, use OpenAI transforms
95
+ OpenAI::Chat::Transforms.normalize_tool_choice(tool_choice)
96
+ end
97
+
98
+ # Normalizes messages using OpenAI transforms
99
+ #
100
+ # @param messages [Array, String, Hash, nil]
101
+ # @return [Array<OpenAI::Models::Chat::ChatCompletionMessageParam>, nil]
102
+ def normalize_messages(messages)
103
+ OpenAI::Chat::Transforms.normalize_messages(messages)
104
+ end
105
+
106
+ # Normalizes response_format for OpenRouter
107
+ #
108
+ # Delegates to OpenAI transforms. The special handling for structured output
109
+ # (setting provider.require_parameters=true) is handled in normalize_params.
110
+ #
111
+ # @param format [Hash, Symbol, String]
112
+ # @return [Hash]
113
+ def normalize_response_format(format)
114
+ OpenAI::Chat::Transforms.normalize_response_format(format)
115
+ end
116
+
117
+ # Cleans up serialized request for API submission
118
+ #
119
+ # Merges OpenAI-compatible params with OpenRouter-specific params.
120
+ #
121
+ # @param openai_hash [Hash] serialized OpenAI request
122
+ # @param openrouter_params [Hash] OpenRouter-specific parameters
123
+ # @param defaults [Hash] default values to remove
124
+ # @param gem_object [Object] original gem object
125
+ # @return [Hash] cleaned and merged request hash
126
+ def cleanup_serialized_request(openai_hash, openrouter_params, defaults, gem_object)
127
+ # Start with OpenAI cleanup
128
+ cleaned = OpenAI::Chat::Transforms.cleanup_serialized_request(openai_hash, defaults, gem_object)
129
+
130
+ # Merge OpenRouter-specific params, but skip default values
131
+ openrouter_params.each do |key, value|
132
+ # Skip if value is nil, empty, or matches the default
133
+ next if value.nil?
134
+ next if value.respond_to?(:empty?) && value.empty?
135
+ next if defaults.key?(key) && defaults[key] == value
136
+
137
+ cleaned[key] = serialize_openrouter_param(key, value)
138
+ end
139
+
140
+ cleaned
141
+ end
142
+
143
+ # Serializes OpenRouter-specific parameters
144
+ #
145
+ # @param key [Symbol]
146
+ # @param value [Object]
147
+ # @return [Object] serialized value
148
+ def serialize_openrouter_param(key, value)
149
+ case key
150
+ when :provider
151
+ # Serialize provider preferences object
152
+ value.respond_to?(:serialize) ? value.serialize : value
153
+ when :plugins
154
+ # Serialize plugins array
155
+ value.respond_to?(:map) ? value.map { |p| p.respond_to?(:serialize) ? p.serialize : p } : value
156
+ else
157
+ value
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -33,6 +33,20 @@ module ActiveAgent
33
33
 
34
34
  protected
35
35
 
36
+ # @see BaseProvider#prepare_prompt_request
37
+ # @return [Request]
38
+ def prepare_prompt_request
39
+ prepare_prompt_request_tools
40
+ super
41
+ end
42
+
43
+ # Returns true if tool_choice == "any" (OpenRouter's equivalent of "required").
44
+ #
45
+ # @return [Boolean]
46
+ def tool_choice_forces_required?
47
+ request.tool_choice == "any"
48
+ end
49
+
36
50
  # Merges streaming delta into the message with role cleanup.
37
51
  #
38
52
  # Overrides parent to handle OpenRouter's role copying behavior which duplicates
@@ -48,6 +62,15 @@ module ActiveAgent
48
62
 
49
63
  hash_merge_delta(message, delta)
50
64
  end
65
+
66
+ # @see BaseProvider#api_response_normalize
67
+ # @param api_response [OpenAI::Models::ChatCompletion]
68
+ # @return [Hash] normalized response hash
69
+ def api_response_normalize(api_response)
70
+ return api_response unless api_response
71
+
72
+ OpenAI::Chat::Transforms.gem_to_hash(api_response)
73
+ end
51
74
  end
52
75
  end
53
76
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveAgent
2
- VERSION = "1.0.0.rc1"
2
+ VERSION = "1.0.1"
3
3
  end