activeagent 1.0.0.rc1 → 1.0.0
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 +4 -4
- data/CHANGELOG.md +31 -1
- data/lib/active_agent/providers/_base_provider.rb +92 -82
- data/lib/active_agent/providers/anthropic/_types.rb +2 -2
- data/lib/active_agent/providers/anthropic/request.rb +135 -81
- data/lib/active_agent/providers/anthropic/transforms.rb +353 -0
- data/lib/active_agent/providers/anthropic_provider.rb +96 -53
- data/lib/active_agent/providers/common/messages/_types.rb +37 -1
- data/lib/active_agent/providers/common/responses/base.rb +118 -70
- data/lib/active_agent/providers/common/usage.rb +385 -0
- data/lib/active_agent/providers/concerns/instrumentation.rb +263 -0
- data/lib/active_agent/providers/log_subscriber.rb +64 -246
- data/lib/active_agent/providers/mock_provider.rb +23 -23
- data/lib/active_agent/providers/ollama/chat/request.rb +214 -35
- data/lib/active_agent/providers/ollama/chat/transforms.rb +135 -0
- data/lib/active_agent/providers/ollama/embedding/request.rb +160 -47
- data/lib/active_agent/providers/ollama/embedding/transforms.rb +160 -0
- data/lib/active_agent/providers/ollama_provider.rb +0 -1
- data/lib/active_agent/providers/open_ai/_base.rb +3 -2
- data/lib/active_agent/providers/open_ai/chat/_types.rb +13 -1
- data/lib/active_agent/providers/open_ai/chat/request.rb +132 -186
- data/lib/active_agent/providers/open_ai/chat/transforms.rb +364 -0
- data/lib/active_agent/providers/open_ai/chat_provider.rb +57 -36
- data/lib/active_agent/providers/open_ai/embedding/_types.rb +13 -2
- data/lib/active_agent/providers/open_ai/embedding/request.rb +38 -70
- data/lib/active_agent/providers/open_ai/embedding/transforms.rb +88 -0
- data/lib/active_agent/providers/open_ai/responses/_types.rb +1 -7
- data/lib/active_agent/providers/open_ai/responses/request.rb +100 -134
- data/lib/active_agent/providers/open_ai/responses/transforms.rb +228 -0
- data/lib/active_agent/providers/open_ai/responses_provider.rb +77 -30
- data/lib/active_agent/providers/open_ai_provider.rb +0 -3
- data/lib/active_agent/providers/open_router/_types.rb +27 -1
- data/lib/active_agent/providers/open_router/options.rb +49 -1
- data/lib/active_agent/providers/open_router/request.rb +232 -66
- data/lib/active_agent/providers/open_router/requests/_types.rb +0 -1
- data/lib/active_agent/providers/open_router/requests/messages/_types.rb +37 -40
- data/lib/active_agent/providers/open_router/requests/messages/content/file.rb +19 -3
- data/lib/active_agent/providers/open_router/requests/messages/content/files/details.rb +15 -4
- data/lib/active_agent/providers/open_router/requests/plugin.rb +19 -3
- data/lib/active_agent/providers/open_router/requests/plugins/pdf_config.rb +30 -8
- data/lib/active_agent/providers/open_router/requests/prediction.rb +17 -0
- data/lib/active_agent/providers/open_router/requests/provider_preferences/max_price.rb +41 -7
- data/lib/active_agent/providers/open_router/requests/provider_preferences.rb +60 -19
- data/lib/active_agent/providers/open_router/requests/response_format.rb +30 -2
- data/lib/active_agent/providers/open_router/transforms.rb +134 -0
- data/lib/active_agent/providers/open_router_provider.rb +9 -0
- data/lib/active_agent/version.rb +1 -1
- metadata +15 -159
- data/lib/active_agent/generation_provider/open_router/types.rb +0 -505
- data/lib/active_agent/generation_provider/xai_provider.rb +0 -144
- data/lib/active_agent/providers/anthropic/requests/_types.rb +0 -190
- data/lib/active_agent/providers/anthropic/requests/container_params.rb +0 -19
- data/lib/active_agent/providers/anthropic/requests/content/base.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/content/sources/base.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/context_management_config.rb +0 -18
- data/lib/active_agent/providers/anthropic/requests/messages/_types.rb +0 -189
- data/lib/active_agent/providers/anthropic/requests/messages/assistant.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/base.rb +0 -63
- data/lib/active_agent/providers/anthropic/requests/messages/content/_types.rb +0 -143
- data/lib/active_agent/providers/anthropic/requests/messages/content/base.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/messages/content/document.rb +0 -26
- data/lib/active_agent/providers/anthropic/requests/messages/content/image.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/redacted_thinking.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/messages/content/search_result.rb +0 -27
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/_types.rb +0 -171
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/base.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_base64.rb +0 -25
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_file.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_text.rb +0 -25
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_url.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_base64.rb +0 -27
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_file.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_url.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/text.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/messages/content/thinking.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_result.rb +0 -24
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_use.rb +0 -28
- data/lib/active_agent/providers/anthropic/requests/messages/user.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/metadata.rb +0 -18
- data/lib/active_agent/providers/anthropic/requests/response_format.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/thinking_config/_types.rb +0 -60
- data/lib/active_agent/providers/anthropic/requests/thinking_config/base.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/thinking_config/disabled.rb +0 -16
- data/lib/active_agent/providers/anthropic/requests/thinking_config/enabled.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/tool_choice/_types.rb +0 -78
- data/lib/active_agent/providers/anthropic/requests/tool_choice/any.rb +0 -17
- data/lib/active_agent/providers/anthropic/requests/tool_choice/auto.rb +0 -17
- data/lib/active_agent/providers/anthropic/requests/tool_choice/base.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/tool_choice/none.rb +0 -16
- data/lib/active_agent/providers/anthropic/requests/tool_choice/tool.rb +0 -20
- data/lib/active_agent/providers/ollama/chat/requests/_types.rb +0 -3
- data/lib/active_agent/providers/ollama/chat/requests/messages/_types.rb +0 -116
- data/lib/active_agent/providers/ollama/chat/requests/messages/assistant.rb +0 -19
- data/lib/active_agent/providers/ollama/chat/requests/messages/user.rb +0 -19
- data/lib/active_agent/providers/ollama/embedding/requests/_types.rb +0 -83
- data/lib/active_agent/providers/ollama/embedding/requests/options.rb +0 -104
- data/lib/active_agent/providers/open_ai/chat/requests/_types.rb +0 -229
- data/lib/active_agent/providers/open_ai/chat/requests/audio.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/messages/_types.rb +0 -123
- data/lib/active_agent/providers/open_ai/chat/requests/messages/assistant.rb +0 -42
- data/lib/active_agent/providers/open_ai/chat/requests/messages/base.rb +0 -78
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/_types.rb +0 -133
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/audio.rb +0 -35
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/base.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/file.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/_types.rb +0 -60
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/details.rb +0 -41
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/image.rb +0 -37
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/refusal.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/text.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/developer.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/function.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/system.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/tool.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/messages/user.rb +0 -32
- data/lib/active_agent/providers/open_ai/chat/requests/prediction.rb +0 -46
- data/lib/active_agent/providers/open_ai/chat/requests/response_format.rb +0 -53
- data/lib/active_agent/providers/open_ai/chat/requests/stream_options.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/tool_choice.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/tools/_types.rb +0 -5
- data/lib/active_agent/providers/open_ai/chat/requests/tools/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/chat/requests/tools/custom_tool.rb +0 -41
- data/lib/active_agent/providers/open_ai/chat/requests/tools/function_tool.rb +0 -51
- data/lib/active_agent/providers/open_ai/chat/requests/web_search_options.rb +0 -45
- data/lib/active_agent/providers/open_ai/embedding/requests/_types.rb +0 -49
- data/lib/active_agent/providers/open_ai/responses/requests/_types.rb +0 -231
- data/lib/active_agent/providers/open_ai/responses/requests/conversation.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/_types.rb +0 -264
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/assistant_message.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/base.rb +0 -89
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/code_interpreter_tool_call.rb +0 -30
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call_output.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/_types.rb +0 -207
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_audio.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_file.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_image.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_text.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call_output.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/developer_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/file_search_tool_call.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_call_output.rb +0 -32
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/image_gen_tool_call.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/input_message.rb +0 -31
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/item_reference.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call_output.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_request.rb +0 -30
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_response.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_list_tools.rb +0 -29
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_tool_call.rb +0 -35
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/output_message.rb +0 -35
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/reasoning.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/system_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_call_base.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_message.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/user_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/web_search_tool_call.rb +0 -24
- data/lib/active_agent/providers/open_ai/responses/requests/prompt_reference.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/reasoning.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/stream_options.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text/_types.rb +0 -89
- data/lib/active_agent/providers/open_ai/responses/requests/text/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_object.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_schema.rb +0 -48
- data/lib/active_agent/providers/open_ai/responses/requests/text/plain.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text.rb +0 -41
- data/lib/active_agent/providers/open_ai/responses/requests/tool_choice.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/tools/_types.rb +0 -112
- data/lib/active_agent/providers/open_ai/responses/requests/tools/base.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/tools/code_interpreter_tool.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/tools/computer_tool.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/tools/custom_tool.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/tools/file_search_tool.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/tools/function_tool.rb +0 -29
- data/lib/active_agent/providers/open_ai/responses/requests/tools/image_generation_tool.rb +0 -37
- data/lib/active_agent/providers/open_ai/responses/requests/tools/local_shell_tool.rb +0 -21
- data/lib/active_agent/providers/open_ai/responses/requests/tools/mcp_tool.rb +0 -41
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_preview_tool.rb +0 -24
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_tool.rb +0 -25
- data/lib/active_agent/providers/open_ai/schema.yml +0 -65937
- data/lib/active_agent/providers/open_router/requests/message.rb +0 -1
- data/lib/active_agent/providers/open_router/requests/messages/assistant.rb +0 -20
- data/lib/active_agent/providers/open_router/requests/messages/user.rb +0 -30
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module ActiveAgent
|
|
4
|
-
module GenerationProvider
|
|
5
|
-
module OpenRouter
|
|
6
|
-
# Base class for OpenRouter request objects with ActiveModel support
|
|
7
|
-
class ConfigObject
|
|
8
|
-
include ActiveModel::Model
|
|
9
|
-
include ActiveModel::Attributes
|
|
10
|
-
include ActiveModel::Validations
|
|
11
|
-
|
|
12
|
-
def self.alias_names = []
|
|
13
|
-
|
|
14
|
-
def self.accepts_attributes_for(association_name, klass)
|
|
15
|
-
attributes = klass.attribute_names | klass.alias_names
|
|
16
|
-
|
|
17
|
-
attributes.each do |attribute|
|
|
18
|
-
define_method(attribute) do
|
|
19
|
-
public_send(association_name)&.public_send(attribute)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
define_method("#{attribute}=") do |value|
|
|
23
|
-
unless public_send(association_name)
|
|
24
|
-
public_send("#{association_name}=", {})
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
public_send(association_name).public_send("#{attribute}=", value)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def to_h
|
|
33
|
-
attributes.compact.transform_values do |value|
|
|
34
|
-
case value
|
|
35
|
-
when ConfigObject
|
|
36
|
-
value.to_h
|
|
37
|
-
when Array
|
|
38
|
-
value.map { |v| v.is_a?(ConfigObject) ? v.to_h : v }
|
|
39
|
-
else
|
|
40
|
-
value
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
alias_method :to_hash, :to_h
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Text content part for messages
|
|
49
|
-
class TextContent < ConfigObject
|
|
50
|
-
attribute :type, :string, default: "text"
|
|
51
|
-
attribute :text, :string
|
|
52
|
-
|
|
53
|
-
validates :type, inclusion: { in: %w[text] }
|
|
54
|
-
validates :text, presence: true
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Image content part for messages
|
|
58
|
-
class ImageContentPart < ConfigObject
|
|
59
|
-
attribute :type, :string, default: "image_url"
|
|
60
|
-
attribute :image_url, default: -> { {} }
|
|
61
|
-
|
|
62
|
-
validates :type, inclusion: { in: %w[image_url] }
|
|
63
|
-
validates :image_url, presence: true
|
|
64
|
-
|
|
65
|
-
def image_url=(value)
|
|
66
|
-
if value.is_a?(Hash)
|
|
67
|
-
super({
|
|
68
|
-
url: value[:url] || value["url"],
|
|
69
|
-
detail: value[:detail] || value["detail"]
|
|
70
|
-
}.compact)
|
|
71
|
-
else
|
|
72
|
-
super(value)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
validate :validate_image_url_structure
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
def validate_image_url_structure
|
|
81
|
-
return if image_url.blank?
|
|
82
|
-
return unless image_url.is_a?(Hash)
|
|
83
|
-
|
|
84
|
-
if image_url[:url].blank? && image_url["url"].blank?
|
|
85
|
-
errors.add(:image_url, "must contain a url")
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Message object for chat completions
|
|
91
|
-
class Message < ConfigObject
|
|
92
|
-
attribute :role, :string
|
|
93
|
-
attribute :content # Can be string or array of ContentParts
|
|
94
|
-
attribute :name, :string
|
|
95
|
-
attribute :tool_call_id, :string
|
|
96
|
-
|
|
97
|
-
validates :role, presence: true, inclusion: { in: %w[user assistant system tool] }
|
|
98
|
-
validates :content, presence: true
|
|
99
|
-
|
|
100
|
-
validate :validate_role_specific_fields
|
|
101
|
-
|
|
102
|
-
def content=(value)
|
|
103
|
-
if value.is_a?(Array)
|
|
104
|
-
super(value.map do |part|
|
|
105
|
-
next part if part.is_a?(TextContent) || part.is_a?(ImageContentPart)
|
|
106
|
-
|
|
107
|
-
case part
|
|
108
|
-
when Hash
|
|
109
|
-
if part[:type] == "text" || part["type"] == "text"
|
|
110
|
-
TextContent.new(part)
|
|
111
|
-
elsif part[:type] == "image_url" || part["type"] == "image_url"
|
|
112
|
-
ImageContentPart.new(part)
|
|
113
|
-
else
|
|
114
|
-
part
|
|
115
|
-
end
|
|
116
|
-
else
|
|
117
|
-
part
|
|
118
|
-
end
|
|
119
|
-
end)
|
|
120
|
-
else
|
|
121
|
-
super(value)
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
private
|
|
126
|
-
|
|
127
|
-
def validate_role_specific_fields
|
|
128
|
-
if role == "tool"
|
|
129
|
-
errors.add(:tool_call_id, "is required for tool role") if tool_call_id.blank?
|
|
130
|
-
elsif role == "user" && content.is_a?(Array)
|
|
131
|
-
# ContentParts are only valid for user role
|
|
132
|
-
content.each_with_index do |part, index|
|
|
133
|
-
next if part.is_a?(String)
|
|
134
|
-
next if part.is_a?(TextContent) || part.is_a?(ImageContentPart)
|
|
135
|
-
|
|
136
|
-
errors.add(:content, "invalid content part at index #{index}")
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Function description for tools
|
|
143
|
-
class FunctionDescription < ConfigObject
|
|
144
|
-
attribute :name, :string
|
|
145
|
-
attribute :description, :string
|
|
146
|
-
attribute :parameters, default: -> { {} } # JSON Schema object
|
|
147
|
-
|
|
148
|
-
validates :name, presence: true
|
|
149
|
-
validates :parameters, presence: true
|
|
150
|
-
|
|
151
|
-
validate :validate_parameters_is_object
|
|
152
|
-
|
|
153
|
-
private
|
|
154
|
-
|
|
155
|
-
def validate_parameters_is_object
|
|
156
|
-
return if parameters.blank?
|
|
157
|
-
unless parameters.is_a?(Hash)
|
|
158
|
-
errors.add(:parameters, "must be a JSON Schema object (Hash)")
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# Tool definition
|
|
164
|
-
class Tool < ConfigObject
|
|
165
|
-
attribute :type, :string, default: "function"
|
|
166
|
-
attribute :function
|
|
167
|
-
|
|
168
|
-
validates :type, inclusion: { in: %w[function] }
|
|
169
|
-
validates :function, presence: true
|
|
170
|
-
|
|
171
|
-
def function=(value)
|
|
172
|
-
if value.is_a?(Hash) && !value.is_a?(FunctionDescription)
|
|
173
|
-
super(FunctionDescription.new(value))
|
|
174
|
-
else
|
|
175
|
-
super(value)
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
validate :validate_function_object
|
|
180
|
-
|
|
181
|
-
private
|
|
182
|
-
|
|
183
|
-
def validate_function_object
|
|
184
|
-
return if function.blank?
|
|
185
|
-
|
|
186
|
-
if function.is_a?(FunctionDescription)
|
|
187
|
-
unless function.valid?
|
|
188
|
-
function.errors.full_messages.each do |msg|
|
|
189
|
-
errors.add(:function, msg)
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
elsif !function.is_a?(Hash)
|
|
193
|
-
errors.add(:function, "must be a FunctionDescription or Hash")
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# Tool choice for controlling tool usage
|
|
199
|
-
class ToolChoice < ConfigObject
|
|
200
|
-
attribute :type, :string
|
|
201
|
-
attribute :function, default: -> { {} }
|
|
202
|
-
|
|
203
|
-
validates :type, inclusion: { in: %w[function] }, if: -> { type.present? }
|
|
204
|
-
|
|
205
|
-
validate :validate_tool_choice_format
|
|
206
|
-
|
|
207
|
-
# Allow simple string values like "none" or "auto"
|
|
208
|
-
def self.from_value(value)
|
|
209
|
-
case value
|
|
210
|
-
when String
|
|
211
|
-
# Return the string directly for "none" or "auto"
|
|
212
|
-
value
|
|
213
|
-
when Hash
|
|
214
|
-
new(value)
|
|
215
|
-
else
|
|
216
|
-
value
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
private
|
|
221
|
-
|
|
222
|
-
def validate_tool_choice_format
|
|
223
|
-
return if type.blank?
|
|
224
|
-
|
|
225
|
-
if type == "function"
|
|
226
|
-
if function.blank? || (function.is_a?(Hash) && function[:name].blank? && function["name"].blank?)
|
|
227
|
-
errors.add(:function, "must contain a name when type is 'function'")
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
# Provider preferences for routing
|
|
234
|
-
class ProviderPreferences < ConfigObject
|
|
235
|
-
attribute :allow_fallbacks, :boolean
|
|
236
|
-
attribute :require_parameters, :boolean
|
|
237
|
-
attribute :data_collection, :string
|
|
238
|
-
attribute :order, default: -> { [] }
|
|
239
|
-
attribute :ignore, default: -> { [] }
|
|
240
|
-
attribute :quantizations, default: -> { [] }
|
|
241
|
-
|
|
242
|
-
validates :data_collection,
|
|
243
|
-
inclusion: { in: %w[allow deny] },
|
|
244
|
-
allow_nil: true
|
|
245
|
-
|
|
246
|
-
validate :validate_order_and_ignore_arrays
|
|
247
|
-
|
|
248
|
-
private
|
|
249
|
-
|
|
250
|
-
def validate_order_and_ignore_arrays
|
|
251
|
-
if order.present? && !order.is_a?(Array)
|
|
252
|
-
errors.add(:order, "must be an array of provider names")
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
if ignore.present? && !ignore.is_a?(Array)
|
|
256
|
-
errors.add(:ignore, "must be an array of provider names")
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
if quantizations.present? && !quantizations.is_a?(Array)
|
|
260
|
-
errors.add(:quantizations, "must be an array")
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
# Response format specification
|
|
266
|
-
class ResponseFormat < ConfigObject
|
|
267
|
-
attribute :type, :string
|
|
268
|
-
|
|
269
|
-
validates :type, inclusion: { in: %w[json_object] }
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# Prediction for latency optimization
|
|
273
|
-
class Prediction < ConfigObject
|
|
274
|
-
attribute :type, :string
|
|
275
|
-
attribute :content, :string
|
|
276
|
-
|
|
277
|
-
validates :type, inclusion: { in: %w[content] }
|
|
278
|
-
validates :content, presence: true
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
# Extra Headers
|
|
282
|
-
class Header < ConfigObject
|
|
283
|
-
attribute :http_referer, :string
|
|
284
|
-
attribute :x_title, :string
|
|
285
|
-
|
|
286
|
-
validates :http_referer, format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]), message: "must be a valid URL" }, allow_nil: true
|
|
287
|
-
|
|
288
|
-
alias_attribute :site_url, :http_referer
|
|
289
|
-
alias_attribute :app_name, :x_title
|
|
290
|
-
|
|
291
|
-
def self.alias_names
|
|
292
|
-
[ "site_url", "app_name" ]
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
def to_h
|
|
296
|
-
{
|
|
297
|
-
"HTTP-Referer" => http_referer,
|
|
298
|
-
"X-Title" => x_title
|
|
299
|
-
}.compact
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
# Main request object for OpenRouter API
|
|
304
|
-
class Request < ConfigObject
|
|
305
|
-
# Required (either messages or prompt)
|
|
306
|
-
attribute :messages, default: -> { [] }
|
|
307
|
-
attribute :prompt, :string
|
|
308
|
-
|
|
309
|
-
# Model specification
|
|
310
|
-
attribute :model, :string
|
|
311
|
-
|
|
312
|
-
# Response format
|
|
313
|
-
attribute :response_format
|
|
314
|
-
|
|
315
|
-
# Generation parameters
|
|
316
|
-
attribute :stop # Can be string or array
|
|
317
|
-
attribute :stream, :boolean, default: false
|
|
318
|
-
attribute :max_tokens, :integer
|
|
319
|
-
attribute :temperature, :float
|
|
320
|
-
attribute :seed, :integer
|
|
321
|
-
attribute :top_p, :float
|
|
322
|
-
attribute :top_k, :integer
|
|
323
|
-
attribute :frequency_penalty, :float
|
|
324
|
-
attribute :presence_penalty, :float
|
|
325
|
-
attribute :repetition_penalty, :float
|
|
326
|
-
attribute :logit_bias, default: -> { {} }
|
|
327
|
-
attribute :top_logprobs, :integer
|
|
328
|
-
attribute :min_p, :float
|
|
329
|
-
attribute :top_a, :float
|
|
330
|
-
|
|
331
|
-
# Tool calling
|
|
332
|
-
attribute :tools, default: -> { [] }
|
|
333
|
-
attribute :tool_choice
|
|
334
|
-
|
|
335
|
-
# Latency optimization
|
|
336
|
-
attribute :prediction
|
|
337
|
-
|
|
338
|
-
# OpenRouter-specific parameters
|
|
339
|
-
attribute :transforms, default: -> { [] }
|
|
340
|
-
attribute :models, default: -> { [] } # For fallback routing
|
|
341
|
-
attribute :route, :string
|
|
342
|
-
attribute :provider
|
|
343
|
-
attribute :user, :string
|
|
344
|
-
attribute :headers
|
|
345
|
-
|
|
346
|
-
# Validations
|
|
347
|
-
validate :validate_messages_or_prompt
|
|
348
|
-
validates :max_tokens, numericality: { greater_than: 0 }, allow_nil: true
|
|
349
|
-
validates :temperature, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 2 }, allow_nil: true
|
|
350
|
-
validates :top_p, numericality: { greater_than: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
351
|
-
validates :top_k, numericality: { greater_than_or_equal_to: 1 }, allow_nil: true
|
|
352
|
-
validates :frequency_penalty, numericality: { greater_than_or_equal_to: -2, less_than_or_equal_to: 2 }, allow_nil: true
|
|
353
|
-
validates :presence_penalty, numericality: { greater_than_or_equal_to: -2, less_than_or_equal_to: 2 }, allow_nil: true
|
|
354
|
-
validates :repetition_penalty, numericality: { greater_than: 0, less_than_or_equal_to: 2 }, allow_nil: true
|
|
355
|
-
validates :min_p, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
356
|
-
validates :top_a, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
357
|
-
validates :route, inclusion: { in: %w[fallback] }, allow_nil: true
|
|
358
|
-
|
|
359
|
-
accepts_attributes_for :response_format, ResponseFormat
|
|
360
|
-
accepts_attributes_for :provider, ProviderPreferences
|
|
361
|
-
accepts_attributes_for :prediction, Prediction
|
|
362
|
-
accepts_attributes_for :tool_choice, ToolChoice
|
|
363
|
-
accepts_attributes_for :headers, Header
|
|
364
|
-
|
|
365
|
-
def build_parameters
|
|
366
|
-
to_h.deep_transform_values do |value|
|
|
367
|
-
case value
|
|
368
|
-
when Array
|
|
369
|
-
value.empty? ? nil : value
|
|
370
|
-
else
|
|
371
|
-
value
|
|
372
|
-
end
|
|
373
|
-
end.compact
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
# Setters with type coercion
|
|
377
|
-
def messages=(value)
|
|
378
|
-
if value.is_a?(Array)
|
|
379
|
-
super(value.map do |msg|
|
|
380
|
-
msg.is_a?(Message) ? msg : Message.new(msg)
|
|
381
|
-
end)
|
|
382
|
-
else
|
|
383
|
-
super(value)
|
|
384
|
-
end
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
def tools=(value)
|
|
388
|
-
if value.is_a?(Array)
|
|
389
|
-
super(value.map do |tool|
|
|
390
|
-
tool.is_a?(Tool) ? tool : Tool.new(tool)
|
|
391
|
-
end)
|
|
392
|
-
else
|
|
393
|
-
super(value)
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
def response_format=(value)
|
|
398
|
-
if value.is_a?(Hash) && !value.is_a?(ResponseFormat)
|
|
399
|
-
super(ResponseFormat.new(value))
|
|
400
|
-
else
|
|
401
|
-
super(value)
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
def provider=(value)
|
|
406
|
-
if value.is_a?(Hash) && !value.is_a?(ProviderPreferences)
|
|
407
|
-
super(ProviderPreferences.new(value))
|
|
408
|
-
else
|
|
409
|
-
super(value)
|
|
410
|
-
end
|
|
411
|
-
end
|
|
412
|
-
|
|
413
|
-
def prediction=(value)
|
|
414
|
-
if value.is_a?(Hash) && !value.is_a?(Prediction)
|
|
415
|
-
super(Prediction.new(value))
|
|
416
|
-
else
|
|
417
|
-
super(value)
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
def tool_choice=(value)
|
|
422
|
-
super(ToolChoice.from_value(value))
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
def headers=(value)
|
|
426
|
-
if value.is_a?(Hash) && !value.is_a?(Header)
|
|
427
|
-
super(Header.new(value))
|
|
428
|
-
else
|
|
429
|
-
super(value)
|
|
430
|
-
end
|
|
431
|
-
end
|
|
432
|
-
|
|
433
|
-
validate :validate_nested_objects
|
|
434
|
-
|
|
435
|
-
private
|
|
436
|
-
|
|
437
|
-
def validate_messages_or_prompt
|
|
438
|
-
if messages.blank? && prompt.blank?
|
|
439
|
-
errors.add(:base, "Either messages or prompt is required")
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
if messages.present? && prompt.present?
|
|
443
|
-
errors.add(:base, "Cannot specify both messages and prompt")
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
def validate_nested_objects
|
|
448
|
-
# Validate messages
|
|
449
|
-
if messages.present?
|
|
450
|
-
messages.each_with_index do |message, index|
|
|
451
|
-
next unless message.is_a?(Message)
|
|
452
|
-
|
|
453
|
-
unless message.valid?
|
|
454
|
-
message.errors.full_messages.each do |msg|
|
|
455
|
-
errors.add(:messages, "at index #{index}: #{msg}")
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
end
|
|
460
|
-
|
|
461
|
-
# Validate tools
|
|
462
|
-
if tools.present?
|
|
463
|
-
tools.each_with_index do |tool, index|
|
|
464
|
-
next unless tool.is_a?(Tool)
|
|
465
|
-
|
|
466
|
-
unless tool.valid?
|
|
467
|
-
tool.errors.full_messages.each do |msg|
|
|
468
|
-
errors.add(:tools, "at index #{index}: #{msg}")
|
|
469
|
-
end
|
|
470
|
-
end
|
|
471
|
-
end
|
|
472
|
-
end
|
|
473
|
-
|
|
474
|
-
# Validate response_format
|
|
475
|
-
if response_format.is_a?(ResponseFormat) && !response_format.valid?
|
|
476
|
-
response_format.errors.full_messages.each do |msg|
|
|
477
|
-
errors.add(:response_format, msg)
|
|
478
|
-
end
|
|
479
|
-
end
|
|
480
|
-
|
|
481
|
-
# Validate provider
|
|
482
|
-
if provider.is_a?(ProviderPreferences) && !provider.valid?
|
|
483
|
-
provider.errors.full_messages.each do |msg|
|
|
484
|
-
errors.add(:provider, msg)
|
|
485
|
-
end
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
# Validate prediction
|
|
489
|
-
if prediction.is_a?(Prediction) && !prediction.valid?
|
|
490
|
-
prediction.errors.full_messages.each do |msg|
|
|
491
|
-
errors.add(:prediction, msg)
|
|
492
|
-
end
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
# Validate headers
|
|
496
|
-
if headers.is_a?(Header) && !headers.valid?
|
|
497
|
-
headers.errors.full_messages.each do |msg|
|
|
498
|
-
errors.add(:headers, msg)
|
|
499
|
-
end
|
|
500
|
-
end
|
|
501
|
-
end
|
|
502
|
-
end
|
|
503
|
-
end
|
|
504
|
-
end
|
|
505
|
-
end
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
begin
|
|
4
|
-
gem "ruby-openai", ">= 8.1.0"
|
|
5
|
-
require "openai"
|
|
6
|
-
rescue LoadError
|
|
7
|
-
raise LoadError, "The 'ruby-openai >= 8.1.0' gem is required for XAIProvider. Please add it to your Gemfile and run `bundle install`."
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
require "active_agent/action_prompt/action"
|
|
11
|
-
require_relative "base"
|
|
12
|
-
require_relative "response"
|
|
13
|
-
require_relative "stream_processing"
|
|
14
|
-
require_relative "message_formatting"
|
|
15
|
-
require_relative "tool_management"
|
|
16
|
-
|
|
17
|
-
module ActiveAgent
|
|
18
|
-
module GenerationProvider
|
|
19
|
-
# XAI (Grok) Generation Provider
|
|
20
|
-
# Uses OpenAI-compatible API format with xAI's endpoint
|
|
21
|
-
class XAIProvider < Base
|
|
22
|
-
include StreamProcessing
|
|
23
|
-
include MessageFormatting
|
|
24
|
-
include ToolManagement
|
|
25
|
-
|
|
26
|
-
XAI_API_HOST = "https://api.x.ai"
|
|
27
|
-
|
|
28
|
-
def initialize(config)
|
|
29
|
-
super
|
|
30
|
-
# Support both api_key and access_token for backwards compatibility
|
|
31
|
-
@access_token = config["api_key"] || config["access_token"] || ENV["XAI_API_KEY"] || ENV["GROK_API_KEY"]
|
|
32
|
-
|
|
33
|
-
unless @access_token
|
|
34
|
-
raise ArgumentError, "XAI API key is required. Set it in config as 'api_key', 'access_token', or via XAI_API_KEY/GROK_API_KEY environment variable."
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# xAI uses OpenAI-compatible client with custom endpoint
|
|
38
|
-
@client = OpenAI::Client.new(
|
|
39
|
-
access_token: @access_token,
|
|
40
|
-
uri_base: config["host"] || XAI_API_HOST,
|
|
41
|
-
log_errors: Rails.env.development?
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
# Default to grok-2-latest but allow configuration
|
|
45
|
-
@model_name = config["model"] || "grok-2-latest"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def generate(prompt)
|
|
49
|
-
@prompt = prompt
|
|
50
|
-
|
|
51
|
-
with_error_handling do
|
|
52
|
-
chat_prompt(parameters: prompt_parameters)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def embed(prompt)
|
|
57
|
-
# xAI doesn't currently provide embedding models
|
|
58
|
-
raise NotImplementedError, "xAI does not currently support embeddings. Use a different provider for embedding tasks."
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
protected
|
|
62
|
-
|
|
63
|
-
# Override from StreamProcessing module - uses OpenAI format
|
|
64
|
-
def process_stream_chunk(chunk, message, agent_stream)
|
|
65
|
-
new_content = chunk.dig("choices", 0, "delta", "content")
|
|
66
|
-
if new_content && !new_content.blank?
|
|
67
|
-
message.generation_id = chunk.dig("id")
|
|
68
|
-
message.content += new_content
|
|
69
|
-
agent_stream&.call(message, new_content, false, prompt.action_name)
|
|
70
|
-
elsif chunk.dig("choices", 0, "delta", "tool_calls") && chunk.dig("choices", 0, "delta", "role")
|
|
71
|
-
message = handle_message(chunk.dig("choices", 0, "delta"))
|
|
72
|
-
prompt.messages << message
|
|
73
|
-
@response = ActiveAgent::GenerationProvider::Response.new(
|
|
74
|
-
prompt:,
|
|
75
|
-
message:,
|
|
76
|
-
raw_response: chunk,
|
|
77
|
-
raw_request: @streaming_request_params
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
if chunk.dig("choices", 0, "finish_reason")
|
|
82
|
-
finalize_stream(message, agent_stream)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Override from MessageFormatting module to handle image format (if xAI adds vision support)
|
|
87
|
-
def format_image_content(message)
|
|
88
|
-
[ {
|
|
89
|
-
type: "image_url",
|
|
90
|
-
image_url: { url: message.content }
|
|
91
|
-
} ]
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
private
|
|
95
|
-
|
|
96
|
-
# Override from ParameterBuilder to add xAI-specific parameters if needed
|
|
97
|
-
def build_provider_parameters
|
|
98
|
-
params = {}
|
|
99
|
-
|
|
100
|
-
# Add any xAI-specific parameters here
|
|
101
|
-
# For now, xAI follows OpenAI's format closely
|
|
102
|
-
|
|
103
|
-
params
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def chat_response(response, request_params = nil)
|
|
107
|
-
return @response if prompt.options[:stream]
|
|
108
|
-
|
|
109
|
-
message_json = response.dig("choices", 0, "message")
|
|
110
|
-
message_json["id"] = response.dig("id") if message_json["id"].blank?
|
|
111
|
-
message = handle_message(message_json)
|
|
112
|
-
|
|
113
|
-
update_context(prompt: prompt, message: message, response: response)
|
|
114
|
-
|
|
115
|
-
@response = ActiveAgent::GenerationProvider::Response.new(
|
|
116
|
-
prompt: prompt,
|
|
117
|
-
message: message,
|
|
118
|
-
raw_response: response,
|
|
119
|
-
raw_request: request_params
|
|
120
|
-
)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def handle_message(message_json)
|
|
124
|
-
ActiveAgent::ActionPrompt::Message.new(
|
|
125
|
-
generation_id: message_json["id"],
|
|
126
|
-
content: message_json["content"],
|
|
127
|
-
role: message_json["role"].intern,
|
|
128
|
-
action_requested: message_json["finish_reason"] == "tool_calls",
|
|
129
|
-
raw_actions: message_json["tool_calls"] || [],
|
|
130
|
-
requested_actions: handle_actions(message_json["tool_calls"]),
|
|
131
|
-
content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
|
|
132
|
-
)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def chat_prompt(parameters: prompt_parameters)
|
|
136
|
-
if prompt.options[:stream] || config["stream"]
|
|
137
|
-
parameters[:stream] = provider_stream
|
|
138
|
-
@streaming_request_params = parameters
|
|
139
|
-
end
|
|
140
|
-
chat_response(@client.chat(parameters: parameters), parameters)
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|