activeagent 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +71 -0
- data/README.md +10 -4
- data/lib/active_agent/base.rb +3 -2
- data/lib/active_agent/concerns/provider.rb +6 -2
- data/lib/active_agent/concerns/rescue.rb +39 -0
- data/lib/active_agent/concerns/streaming.rb +2 -1
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/api/traces_controller.rb +117 -0
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/application_controller.rb +54 -0
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/dashboard_controller.rb +126 -0
- data/lib/active_agent/dashboard/app/controllers/active_agent/dashboard/traces_controller.rb +103 -0
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/agent_execution_job.rb +56 -0
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/application_job.rb +14 -0
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_cleanup_job.rb +49 -0
- data/lib/active_agent/dashboard/app/jobs/active_agent/dashboard/sandbox_provision_job.rb +65 -0
- data/lib/active_agent/dashboard/app/jobs/active_agent/process_telemetry_traces_job.rb +77 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent.rb +256 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_run.rb +113 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_template.rb +208 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/agent_version.rb +60 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/application_record.rb +46 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_action.rb +125 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/recording_snapshot.rb +83 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_run.rb +52 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/sandbox_session.rb +169 -0
- data/lib/active_agent/dashboard/app/models/active_agent/dashboard/session_recording.rb +193 -0
- data/lib/active_agent/dashboard/app/models/active_agent/telemetry_trace.rb +198 -0
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/_trace_detail.html.erb +105 -0
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/index.html.erb +135 -0
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/metrics.html.erb +143 -0
- data/lib/active_agent/dashboard/app/views/active_agent/dashboard/traces/show.html.erb +36 -0
- data/lib/active_agent/dashboard/app/views/layouts/active_agent/dashboard/application.html.erb +94 -0
- data/lib/active_agent/dashboard/config/routes.rb +78 -0
- data/lib/active_agent/dashboard/engine.rb +39 -0
- data/lib/active_agent/dashboard.rb +151 -0
- data/lib/active_agent/providers/_base_provider.rb +4 -1
- data/lib/active_agent/providers/anthropic/options.rb +4 -6
- data/lib/active_agent/providers/anthropic/request.rb +28 -3
- data/lib/active_agent/providers/anthropic/transforms.rb +131 -2
- data/lib/active_agent/providers/anthropic_provider.rb +97 -30
- data/lib/active_agent/providers/azure/_types.rb +5 -0
- data/lib/active_agent/providers/azure/options.rb +111 -0
- data/lib/active_agent/providers/azure_open_ai_provider.rb +2 -0
- data/lib/active_agent/providers/azure_openai_provider.rb +2 -0
- data/lib/active_agent/providers/azure_provider.rb +133 -0
- data/lib/active_agent/providers/azureopenai_provider.rb +2 -0
- data/lib/active_agent/providers/bedrock/_types.rb +8 -0
- data/lib/active_agent/providers/bedrock/bearer_client.rb +109 -0
- data/lib/active_agent/providers/bedrock/options.rb +77 -0
- data/lib/active_agent/providers/bedrock_provider.rb +84 -0
- data/lib/active_agent/providers/common/messages/_types.rb +42 -31
- data/lib/active_agent/providers/common/messages/assistant.rb +20 -4
- data/lib/active_agent/providers/concerns/exception_handler.rb +1 -0
- data/lib/active_agent/providers/concerns/previewable.rb +39 -5
- data/lib/active_agent/providers/concerns/tool_choice_clearing.rb +62 -0
- data/lib/active_agent/providers/gemini/_types.rb +19 -0
- data/lib/active_agent/providers/gemini/options.rb +41 -0
- data/lib/active_agent/providers/gemini_provider.rb +94 -0
- data/lib/active_agent/providers/open_ai/chat/transforms.rb +120 -4
- data/lib/active_agent/providers/open_ai/chat_provider.rb +40 -0
- data/lib/active_agent/providers/open_ai/responses/request.rb +17 -2
- data/lib/active_agent/providers/open_ai/responses/transforms.rb +135 -0
- data/lib/active_agent/providers/open_ai/responses_provider.rb +38 -0
- data/lib/active_agent/providers/open_router/request.rb +20 -0
- data/lib/active_agent/providers/open_router/transforms.rb +30 -0
- data/lib/active_agent/providers/open_router_provider.rb +14 -0
- data/lib/active_agent/providers/ruby_llm/_types.rb +77 -0
- data/lib/active_agent/providers/ruby_llm/embedding_request.rb +16 -0
- data/lib/active_agent/providers/ruby_llm/messages/_types.rb +109 -0
- data/lib/active_agent/providers/ruby_llm/messages/assistant.rb +27 -0
- data/lib/active_agent/providers/ruby_llm/messages/base.rb +48 -0
- data/lib/active_agent/providers/ruby_llm/messages/system.rb +18 -0
- data/lib/active_agent/providers/ruby_llm/messages/tool.rb +24 -0
- data/lib/active_agent/providers/ruby_llm/messages/user.rb +18 -0
- data/lib/active_agent/providers/ruby_llm/options.rb +28 -0
- data/lib/active_agent/providers/ruby_llm/request.rb +30 -0
- data/lib/active_agent/providers/ruby_llm/tool_proxy.rb +45 -0
- data/lib/active_agent/providers/ruby_llm_provider.rb +407 -0
- data/lib/active_agent/railtie.rb +32 -1
- data/lib/active_agent/telemetry/configuration.rb +213 -0
- data/lib/active_agent/telemetry/instrumentation.rb +155 -0
- data/lib/active_agent/telemetry/reporter.rb +176 -0
- data/lib/active_agent/telemetry/span.rb +267 -0
- data/lib/active_agent/telemetry/tracer.rb +184 -0
- data/lib/active_agent/telemetry.rb +162 -0
- data/lib/active_agent/version.rb +1 -1
- data/lib/active_agent.rb +2 -0
- data/lib/generators/active_agent/dashboard/install/install_generator.rb +96 -0
- data/lib/generators/active_agent/dashboard/install/templates/initializer.rb +89 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_agent_runs.rb +42 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_agent_templates.rb +38 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_agent_versions.rb +22 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_agents.rb +53 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_sandbox_runs.rb +28 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_sandbox_sessions.rb +43 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_session_recordings.rb +44 -0
- data/lib/generators/active_agent/dashboard/install/templates/migrations/create_active_agent_telemetry_traces.rb +56 -0
- data/lib/generators/active_agent/dashboard/install_generator.rb +64 -0
- data/lib/generators/active_agent/dashboard/templates/active_agent_dashboard.rb.erb +30 -0
- data/lib/generators/active_agent/dashboard/templates/create_active_agent_telemetry_traces.rb.erb +30 -0
- metadata +101 -14
|
@@ -148,6 +148,26 @@ module ActiveAgent
|
|
|
148
148
|
self.messages = instructions_messages + current_messages
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
# Gets tool_choice bypassing gem validation
|
|
152
|
+
#
|
|
153
|
+
# OpenRouter supports "any" which isn't valid in OpenAI gem types.
|
|
154
|
+
#
|
|
155
|
+
# @return [String, Hash, nil]
|
|
156
|
+
def tool_choice
|
|
157
|
+
__getobj__.instance_variable_get(:@data)[:tool_choice]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Sets tool_choice bypassing gem validation
|
|
161
|
+
#
|
|
162
|
+
# OpenRouter supports "any" which isn't valid in OpenAI gem types,
|
|
163
|
+
# so we bypass the gem's type validation by setting @data directly.
|
|
164
|
+
#
|
|
165
|
+
# @param value [String, Hash, nil]
|
|
166
|
+
# @return [void]
|
|
167
|
+
def tool_choice=(value)
|
|
168
|
+
__getobj__.instance_variable_get(:@data)[:tool_choice] = value
|
|
169
|
+
end
|
|
170
|
+
|
|
151
171
|
# Accessor for OpenRouter-specific provider preferences
|
|
152
172
|
#
|
|
153
173
|
# @return [Hash, nil]
|
|
@@ -62,9 +62,39 @@ module ActiveAgent
|
|
|
62
62
|
# Use OpenAI transforms for the base parameters
|
|
63
63
|
openai_params = OpenAI::Chat::Transforms.normalize_params(params)
|
|
64
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
|
+
|
|
65
70
|
[ openai_params, openrouter_params ]
|
|
66
71
|
end
|
|
67
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
|
+
|
|
68
98
|
# Normalizes messages using OpenAI transforms
|
|
69
99
|
#
|
|
70
100
|
# @param messages [Array, String, Hash, nil]
|
|
@@ -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
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "options"
|
|
4
|
+
require_relative "request"
|
|
5
|
+
require_relative "embedding_request"
|
|
6
|
+
|
|
7
|
+
module ActiveAgent
|
|
8
|
+
module Providers
|
|
9
|
+
module RubyLLM
|
|
10
|
+
# Type for Request model
|
|
11
|
+
class RequestType < ActiveModel::Type::Value
|
|
12
|
+
def cast(value)
|
|
13
|
+
case value
|
|
14
|
+
when Request
|
|
15
|
+
value
|
|
16
|
+
when Hash
|
|
17
|
+
Request.new(**value.deep_symbolize_keys)
|
|
18
|
+
when nil
|
|
19
|
+
nil
|
|
20
|
+
else
|
|
21
|
+
raise ArgumentError, "Cannot cast #{value.class} to Request"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def serialize(value)
|
|
26
|
+
case value
|
|
27
|
+
when Request
|
|
28
|
+
value.serialize
|
|
29
|
+
when Hash
|
|
30
|
+
value
|
|
31
|
+
when nil
|
|
32
|
+
nil
|
|
33
|
+
else
|
|
34
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def deserialize(value)
|
|
39
|
+
cast(value)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Type for embedding requests
|
|
44
|
+
class EmbeddingRequestType < ActiveModel::Type::Value
|
|
45
|
+
def cast(value)
|
|
46
|
+
case value
|
|
47
|
+
when RubyLLM::EmbeddingRequest
|
|
48
|
+
value
|
|
49
|
+
when Hash
|
|
50
|
+
RubyLLM::EmbeddingRequest.new(**value.deep_symbolize_keys)
|
|
51
|
+
when nil
|
|
52
|
+
nil
|
|
53
|
+
else
|
|
54
|
+
raise ArgumentError, "Cannot cast #{value.class} to EmbeddingRequest"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def serialize(value)
|
|
59
|
+
case value
|
|
60
|
+
when RubyLLM::EmbeddingRequest
|
|
61
|
+
value.serialize
|
|
62
|
+
when Hash
|
|
63
|
+
value
|
|
64
|
+
when nil
|
|
65
|
+
nil
|
|
66
|
+
else
|
|
67
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def deserialize(value)
|
|
72
|
+
cast(value)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module RubyLLM
|
|
8
|
+
# Embedding request model for RubyLLM provider.
|
|
9
|
+
class EmbeddingRequest < Common::BaseModel
|
|
10
|
+
attribute :model, :string
|
|
11
|
+
attribute :input
|
|
12
|
+
attribute :dimensions, :integer
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Load all message classes
|
|
4
|
+
require_relative "base"
|
|
5
|
+
require_relative "system"
|
|
6
|
+
require_relative "user"
|
|
7
|
+
require_relative "assistant"
|
|
8
|
+
require_relative "tool"
|
|
9
|
+
|
|
10
|
+
module ActiveAgent
|
|
11
|
+
module Providers
|
|
12
|
+
module RubyLLM
|
|
13
|
+
module Messages
|
|
14
|
+
# Type for Messages array
|
|
15
|
+
class MessagesType < ActiveModel::Type::Value
|
|
16
|
+
def initialize
|
|
17
|
+
super
|
|
18
|
+
@message_type = MessageType.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def cast(value)
|
|
22
|
+
case value
|
|
23
|
+
when Array
|
|
24
|
+
value.map { |v| @message_type.cast(v) }
|
|
25
|
+
when nil
|
|
26
|
+
nil
|
|
27
|
+
else
|
|
28
|
+
raise ArgumentError, "Cannot cast #{value.class} to Messages array"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def serialize(value)
|
|
33
|
+
case value
|
|
34
|
+
when Array
|
|
35
|
+
grouped = []
|
|
36
|
+
|
|
37
|
+
value.each do |message|
|
|
38
|
+
if grouped.empty? || grouped.last.role != message.role
|
|
39
|
+
grouped << message.deep_dup
|
|
40
|
+
else
|
|
41
|
+
grouped.last.content += message.content.deep_dup
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
grouped.map { |v| @message_type.serialize(v) }
|
|
46
|
+
when nil
|
|
47
|
+
nil
|
|
48
|
+
else
|
|
49
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def deserialize(value)
|
|
54
|
+
cast(value)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Type for individual Message
|
|
59
|
+
class MessageType < ActiveModel::Type::Value
|
|
60
|
+
def cast(value)
|
|
61
|
+
case value
|
|
62
|
+
when Base
|
|
63
|
+
value
|
|
64
|
+
when String
|
|
65
|
+
User.new(content: value)
|
|
66
|
+
when Hash
|
|
67
|
+
hash = value.deep_symbolize_keys
|
|
68
|
+
role = hash[:role]&.to_sym
|
|
69
|
+
|
|
70
|
+
case role
|
|
71
|
+
when :user, nil
|
|
72
|
+
User.new(**hash)
|
|
73
|
+
when :assistant
|
|
74
|
+
Assistant.new(**hash)
|
|
75
|
+
when :system
|
|
76
|
+
System.new(**hash)
|
|
77
|
+
when :tool
|
|
78
|
+
Tool.new(**hash)
|
|
79
|
+
else
|
|
80
|
+
raise ArgumentError, "Unknown message role: #{role}"
|
|
81
|
+
end
|
|
82
|
+
when nil
|
|
83
|
+
nil
|
|
84
|
+
else
|
|
85
|
+
raise ArgumentError, "Cannot cast #{value.class} to Message"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def serialize(value)
|
|
90
|
+
case value
|
|
91
|
+
when Base
|
|
92
|
+
value.serialize
|
|
93
|
+
when Hash
|
|
94
|
+
value
|
|
95
|
+
when nil
|
|
96
|
+
nil
|
|
97
|
+
else
|
|
98
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def deserialize(value)
|
|
103
|
+
cast(value)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module RubyLLM
|
|
8
|
+
module Messages
|
|
9
|
+
# Assistant message for RubyLLM provider.
|
|
10
|
+
#
|
|
11
|
+
# Drops extra fields that are part of the API response but not
|
|
12
|
+
# part of the message structure.
|
|
13
|
+
class Assistant < Base
|
|
14
|
+
attribute :role, :string, as: "assistant"
|
|
15
|
+
attribute :content
|
|
16
|
+
attribute :tool_calls
|
|
17
|
+
|
|
18
|
+
validates :content, presence: true, unless: :tool_calls
|
|
19
|
+
|
|
20
|
+
# Drop API response fields that aren't part of the message
|
|
21
|
+
drop_attributes :usage, :id, :model, :stop_reason, :stop_sequence, :type,
|
|
22
|
+
:input_tokens, :output_tokens
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module RubyLLM
|
|
8
|
+
module Messages
|
|
9
|
+
# Base class for RubyLLM messages.
|
|
10
|
+
class Base < Common::BaseModel
|
|
11
|
+
attribute :role, :string
|
|
12
|
+
attribute :content
|
|
13
|
+
|
|
14
|
+
validates :role, presence: true
|
|
15
|
+
|
|
16
|
+
# Converts to common format.
|
|
17
|
+
#
|
|
18
|
+
# @return [Hash] message in canonical format with role and text content
|
|
19
|
+
def to_common
|
|
20
|
+
{
|
|
21
|
+
role: role,
|
|
22
|
+
content: extract_text_content,
|
|
23
|
+
name: nil
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Extracts text content from the content structure.
|
|
30
|
+
#
|
|
31
|
+
# @return [String] extracted text content
|
|
32
|
+
def extract_text_content
|
|
33
|
+
case content
|
|
34
|
+
when String
|
|
35
|
+
content
|
|
36
|
+
when Array
|
|
37
|
+
content.select { |block| block.is_a?(Hash) && block[:type] == "text" }
|
|
38
|
+
.map { |block| block[:text] }
|
|
39
|
+
.join("\n")
|
|
40
|
+
else
|
|
41
|
+
content.to_s
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module RubyLLM
|
|
8
|
+
module Messages
|
|
9
|
+
# System message for RubyLLM provider.
|
|
10
|
+
class System < Base
|
|
11
|
+
attribute :role, :string, as: "system"
|
|
12
|
+
|
|
13
|
+
validates :content, presence: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module RubyLLM
|
|
8
|
+
module Messages
|
|
9
|
+
# Tool result message for RubyLLM provider.
|
|
10
|
+
class Tool < Base
|
|
11
|
+
attribute :role, :string, as: "tool"
|
|
12
|
+
attribute :content
|
|
13
|
+
attribute :tool_call_id, :string
|
|
14
|
+
|
|
15
|
+
def to_common
|
|
16
|
+
common = super
|
|
17
|
+
common[:tool_call_id] = tool_call_id if tool_call_id
|
|
18
|
+
common
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module RubyLLM
|
|
8
|
+
module Messages
|
|
9
|
+
# User message for RubyLLM provider.
|
|
10
|
+
class User < Base
|
|
11
|
+
attribute :role, :string, as: "user"
|
|
12
|
+
|
|
13
|
+
validates :content, presence: true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module RubyLLM
|
|
8
|
+
# Configuration options for the RubyLLM provider.
|
|
9
|
+
#
|
|
10
|
+
# RubyLLM manages its own API keys via RubyLLM.configure, so no
|
|
11
|
+
# provider-specific API key attributes are needed here.
|
|
12
|
+
class Options < Common::BaseModel
|
|
13
|
+
attribute :model, :string
|
|
14
|
+
attribute :temperature, :float
|
|
15
|
+
attribute :max_tokens, :integer
|
|
16
|
+
|
|
17
|
+
def initialize(kwargs = {})
|
|
18
|
+
kwargs = kwargs.deep_symbolize_keys if kwargs.respond_to?(:deep_symbolize_keys)
|
|
19
|
+
super(**deep_compact(kwargs))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def extra_headers
|
|
23
|
+
{}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
|
|
5
|
+
require_relative "messages/_types"
|
|
6
|
+
|
|
7
|
+
module ActiveAgent
|
|
8
|
+
module Providers
|
|
9
|
+
module RubyLLM
|
|
10
|
+
# Request model for RubyLLM provider.
|
|
11
|
+
class Request < Common::BaseModel
|
|
12
|
+
attribute :model, :string
|
|
13
|
+
attribute :messages, Messages::MessagesType.new
|
|
14
|
+
attribute :instructions
|
|
15
|
+
attribute :tools
|
|
16
|
+
attribute :tool_choice
|
|
17
|
+
attribute :temperature, :float
|
|
18
|
+
attribute :max_tokens, :integer
|
|
19
|
+
attribute :stream, :boolean, default: false
|
|
20
|
+
attribute :response_format
|
|
21
|
+
|
|
22
|
+
# Common Format Compatibility
|
|
23
|
+
def message=(value)
|
|
24
|
+
self.messages ||= []
|
|
25
|
+
self.messages << value
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module RubyLLM
|
|
6
|
+
# Bridges ActiveAgent tool definitions to RubyLLM's expected tool interface.
|
|
7
|
+
# RubyLLM expects tools as { "name" => tool } where each tool responds to
|
|
8
|
+
# #name, #description, #parameters, #params_schema, and #provider_params.
|
|
9
|
+
class ToolProxy
|
|
10
|
+
attr_reader :name, :description, :parameters
|
|
11
|
+
|
|
12
|
+
def initialize(name:, description:, parameters:)
|
|
13
|
+
@name = name
|
|
14
|
+
@description = description
|
|
15
|
+
@parameters = parameters
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# RubyLLM checks this first; returns the JSON Schema directly so
|
|
19
|
+
# RubyLLM doesn't try to interpret our parameters as Parameter objects.
|
|
20
|
+
# Deep-stringifies keys to match RubyLLM's internal schema format.
|
|
21
|
+
def params_schema
|
|
22
|
+
deep_stringify(@parameters) if @parameters.is_a?(Hash) && @parameters.any?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# RubyLLM merges this into the tool definition
|
|
26
|
+
def provider_params
|
|
27
|
+
{}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def deep_stringify(obj)
|
|
33
|
+
case obj
|
|
34
|
+
when Hash
|
|
35
|
+
obj.each_with_object({}) { |(k, v), h| h[k.to_s] = deep_stringify(v) }
|
|
36
|
+
when Array
|
|
37
|
+
obj.map { |v| deep_stringify(v) }
|
|
38
|
+
else
|
|
39
|
+
obj
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|