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,66 +1,245 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
require "delegate"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "transforms"
|
|
5
6
|
|
|
6
7
|
module ActiveAgent
|
|
7
8
|
module Providers
|
|
8
9
|
module Ollama
|
|
9
10
|
module Chat
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
# Wraps OpenAI gem's CompletionCreateParams with Ollama-specific extensions
|
|
12
|
+
#
|
|
13
|
+
# Delegates to OpenAI::Models::Chat::CompletionCreateParams for OpenAI-compatible
|
|
14
|
+
# parameters while adding support for Ollama-specific features like format,
|
|
15
|
+
# options, keep_alive, and raw mode.
|
|
16
|
+
#
|
|
17
|
+
# Ollama-specific parameters:
|
|
18
|
+
# - format: Return response in JSON or as a JSON schema (String "json" or Hash)
|
|
19
|
+
# - options: Additional model parameters (Hash of key-value pairs)
|
|
20
|
+
# - keep_alive: Controls how long model stays in memory (String duration or Integer seconds)
|
|
21
|
+
# - raw: If true, no formatting applied to prompt (Boolean)
|
|
22
|
+
#
|
|
23
|
+
# @example Basic usage
|
|
24
|
+
# request = Request.new(
|
|
25
|
+
# model: "llama2",
|
|
26
|
+
# messages: [{role: "user", content: "Hello"}]
|
|
27
|
+
# )
|
|
28
|
+
#
|
|
29
|
+
# @example With Ollama-specific features
|
|
30
|
+
# request = Request.new(
|
|
31
|
+
# model: "llama2",
|
|
32
|
+
# messages: [{role: "user", content: "Hello"}],
|
|
33
|
+
# format: "json",
|
|
34
|
+
# options: {temperature: 0.7, num_predict: 100},
|
|
35
|
+
# keep_alive: "5m"
|
|
36
|
+
# )
|
|
37
|
+
class Request < SimpleDelegator
|
|
38
|
+
# Default parameter values
|
|
39
|
+
DEFAULTS = {
|
|
40
|
+
frequency_penalty: 0,
|
|
41
|
+
logprobs: false,
|
|
42
|
+
n: 1,
|
|
43
|
+
parallel_tool_calls: true,
|
|
44
|
+
presence_penalty: 0,
|
|
45
|
+
temperature: 1,
|
|
46
|
+
top_p: 1,
|
|
47
|
+
raw: false,
|
|
48
|
+
keep_alive: "5m"
|
|
49
|
+
}.freeze
|
|
15
50
|
|
|
16
|
-
#
|
|
51
|
+
# @return [Boolean, nil]
|
|
52
|
+
attr_reader :stream
|
|
17
53
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
attribute :format
|
|
54
|
+
# @return [Hash] Ollama-specific parameters
|
|
55
|
+
attr_reader :ollama_params
|
|
21
56
|
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
|
|
57
|
+
# Creates a new Ollama request
|
|
58
|
+
#
|
|
59
|
+
# @param params [Hash] request parameters
|
|
60
|
+
# @option params [String] :model model identifier (required)
|
|
61
|
+
# @option params [Array, String, Hash] :messages required conversation messages
|
|
62
|
+
# @option params [String, Hash] :format JSON format ("json" or schema object)
|
|
63
|
+
# @option params [Hash] :options model-specific options
|
|
64
|
+
# @option params [String, Integer] :keep_alive memory duration
|
|
65
|
+
# @option params [Boolean] :raw raw prompt mode
|
|
66
|
+
# @raise [ArgumentError] when parameters are invalid
|
|
67
|
+
def initialize(**params)
|
|
68
|
+
# Step 1: Extract stream flag
|
|
69
|
+
@stream = params[:stream]
|
|
25
70
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Default: "5m"
|
|
29
|
-
attribute :keep_alive
|
|
71
|
+
# Step 2: Apply defaults
|
|
72
|
+
params = apply_defaults(params)
|
|
30
73
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
attribute :raw, :boolean, default: false
|
|
74
|
+
# Step 3: Normalize parameters and split into OpenAI vs Ollama-specific
|
|
75
|
+
openai_params, @ollama_params = Transforms.normalize_params(params)
|
|
34
76
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
77
|
+
# Step 4: Validate Ollama-specific parameters
|
|
78
|
+
validate_format(@ollama_params[:format]) if @ollama_params[:format]
|
|
79
|
+
validate_options(@ollama_params[:options]) if @ollama_params[:options]
|
|
38
80
|
|
|
39
|
-
|
|
81
|
+
# Step 5: Create gem model with OpenAI-compatible params
|
|
82
|
+
gem_model = ::OpenAI::Models::Chat::CompletionCreateParams.new(**openai_params)
|
|
83
|
+
|
|
84
|
+
# Step 6: Delegate to the gem model
|
|
85
|
+
super(gem_model)
|
|
86
|
+
rescue ArgumentError => e
|
|
87
|
+
raise ArgumentError, "Invalid Ollama Chat request parameters: #{e.message}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Serializes request for API submission
|
|
91
|
+
#
|
|
92
|
+
# Merges OpenAI-compatible parameters with Ollama-specific extensions.
|
|
93
|
+
#
|
|
94
|
+
# @return [Hash] cleaned request hash
|
|
95
|
+
def serialize
|
|
96
|
+
# Get OpenAI params from gem model
|
|
97
|
+
openai_hash = Transforms.gem_to_hash(__getobj__)
|
|
98
|
+
|
|
99
|
+
# Merge with Ollama-specific params
|
|
100
|
+
Transforms.cleanup_serialized_request(openai_hash, @ollama_params, DEFAULTS, __getobj__)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @return [Array<Hash>, nil]
|
|
104
|
+
def messages
|
|
105
|
+
__getobj__.instance_variable_get(:@data)[:messages]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Sets messages with normalization
|
|
109
|
+
#
|
|
110
|
+
# Merges new messages with existing ones for compatibility.
|
|
111
|
+
#
|
|
112
|
+
# @param value [Array, String, Hash]
|
|
113
|
+
# @return [void]
|
|
40
114
|
def messages=(value)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
115
|
+
normalized_value = Transforms.normalize_messages(value)
|
|
116
|
+
current_messages = messages || []
|
|
117
|
+
|
|
118
|
+
# Merge behavior for Ollama compatibility
|
|
119
|
+
merged = current_messages | Array(normalized_value)
|
|
120
|
+
__getobj__.instance_variable_get(:@data)[:messages] = merged
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Alias for messages (common format compatibility)
|
|
124
|
+
#
|
|
125
|
+
# @return [Array<Hash>, nil]
|
|
126
|
+
def message
|
|
127
|
+
messages
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @param value [Array, String, Hash]
|
|
131
|
+
# @return [void]
|
|
132
|
+
def message=(value)
|
|
133
|
+
self.messages = value
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Sets instructions as developer messages
|
|
137
|
+
#
|
|
138
|
+
# Prepends developer messages to the messages array.
|
|
139
|
+
#
|
|
140
|
+
# @param values [Array<String>, String]
|
|
141
|
+
# @return [void]
|
|
142
|
+
def instructions=(*values)
|
|
143
|
+
instructions_messages = Transforms.normalize_instructions(values.flatten)
|
|
144
|
+
current_messages = messages || []
|
|
145
|
+
self.messages = instructions_messages + current_messages
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Accessor for Ollama format parameter
|
|
149
|
+
#
|
|
150
|
+
# @return [String, Hash, nil]
|
|
151
|
+
def format
|
|
152
|
+
@ollama_params[:format]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Sets format parameter
|
|
156
|
+
#
|
|
157
|
+
# @param value [String, Hash]
|
|
158
|
+
# @return [void]
|
|
159
|
+
def format=(value)
|
|
160
|
+
validate_format(value)
|
|
161
|
+
@ollama_params[:format] = value
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Accessor for Ollama options parameter
|
|
165
|
+
#
|
|
166
|
+
# @return [Hash, nil]
|
|
167
|
+
def options
|
|
168
|
+
@ollama_params[:options]
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Sets options parameter
|
|
172
|
+
#
|
|
173
|
+
# @param value [Hash]
|
|
174
|
+
# @return [void]
|
|
175
|
+
def options=(value)
|
|
176
|
+
validate_options(value)
|
|
177
|
+
@ollama_params[:options] = value
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Accessor for keep_alive parameter
|
|
181
|
+
#
|
|
182
|
+
# @return [String, Integer, nil]
|
|
183
|
+
def keep_alive
|
|
184
|
+
@ollama_params[:keep_alive] || DEFAULTS[:keep_alive]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Sets keep_alive parameter
|
|
188
|
+
#
|
|
189
|
+
# @param value [String, Integer]
|
|
190
|
+
# @return [void]
|
|
191
|
+
def keep_alive=(value)
|
|
192
|
+
@ollama_params[:keep_alive] = value
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Accessor for raw parameter
|
|
196
|
+
#
|
|
197
|
+
# @return [Boolean]
|
|
198
|
+
def raw
|
|
199
|
+
@ollama_params[:raw] || DEFAULTS[:raw]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Sets raw parameter
|
|
203
|
+
#
|
|
204
|
+
# @param value [Boolean]
|
|
205
|
+
# @return [void]
|
|
206
|
+
def raw=(value)
|
|
207
|
+
@ollama_params[:raw] = value
|
|
47
208
|
end
|
|
48
209
|
|
|
49
210
|
private
|
|
50
211
|
|
|
51
|
-
|
|
212
|
+
# @api private
|
|
213
|
+
# @param params [Hash]
|
|
214
|
+
# @return [Hash]
|
|
215
|
+
def apply_defaults(params)
|
|
216
|
+
# Apply defaults
|
|
217
|
+
DEFAULTS.each do |key, value|
|
|
218
|
+
params[key] = value unless params.key?(key)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
params
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# @api private
|
|
225
|
+
# @param format [String, Hash, nil]
|
|
226
|
+
# @raise [ArgumentError]
|
|
227
|
+
def validate_format(format)
|
|
52
228
|
return if format.nil?
|
|
53
229
|
return if format == "json"
|
|
54
230
|
return if format.is_a?(Hash) # JSON schema object
|
|
55
231
|
|
|
56
|
-
|
|
232
|
+
raise ArgumentError, "format must be 'json' or a JSON schema object"
|
|
57
233
|
end
|
|
58
234
|
|
|
59
|
-
|
|
235
|
+
# @api private
|
|
236
|
+
# @param options [Hash, nil]
|
|
237
|
+
# @raise [ArgumentError]
|
|
238
|
+
def validate_options(options)
|
|
60
239
|
return if options.nil?
|
|
61
240
|
|
|
62
241
|
unless options.is_a?(Hash)
|
|
63
|
-
|
|
242
|
+
raise ArgumentError, "options must be a hash of model parameters"
|
|
64
243
|
end
|
|
65
244
|
end
|
|
66
245
|
end
|
|
@@ -0,0 +1,135 @@
|
|
|
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 Ollama
|
|
9
|
+
module Chat
|
|
10
|
+
# Provides transformation methods for normalizing Ollama parameters
|
|
11
|
+
# to work with OpenAI gem's native format plus Ollama extensions
|
|
12
|
+
#
|
|
13
|
+
# Leverages OpenAI::Chat::Transforms for base message normalization while
|
|
14
|
+
# adding handling for Ollama-specific parameters like format, options,
|
|
15
|
+
# keep_alive, and raw mode.
|
|
16
|
+
module Transforms
|
|
17
|
+
class << self
|
|
18
|
+
# Converts gem model object to hash via JSON round-trip
|
|
19
|
+
#
|
|
20
|
+
# @param gem_object [Object]
|
|
21
|
+
# @return [Hash] with symbolized keys
|
|
22
|
+
def gem_to_hash(gem_object)
|
|
23
|
+
OpenAI::Chat::Transforms.gem_to_hash(gem_object)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Normalizes all request parameters for Ollama API
|
|
27
|
+
#
|
|
28
|
+
# Handles both OpenAI-compatible parameters and Ollama-specific extensions.
|
|
29
|
+
# Ollama-specific params (format, options, keep_alive, raw) are
|
|
30
|
+
# extracted and returned separately for manual serialization.
|
|
31
|
+
#
|
|
32
|
+
# @param params [Hash]
|
|
33
|
+
# @return [Array<Hash, Hash>] tuple of [openai_params, ollama_params]
|
|
34
|
+
def normalize_params(params)
|
|
35
|
+
params = params.dup
|
|
36
|
+
|
|
37
|
+
# Extract Ollama-specific parameters
|
|
38
|
+
ollama_params = {}
|
|
39
|
+
ollama_params[:format] = params.delete(:format) if params.key?(:format)
|
|
40
|
+
ollama_params[:options] = params.delete(:options) if params.key?(:options)
|
|
41
|
+
ollama_params[:keep_alive] = params.delete(:keep_alive) if params.key?(:keep_alive)
|
|
42
|
+
ollama_params[:raw] = params.delete(:raw) if params.key?(:raw)
|
|
43
|
+
|
|
44
|
+
# Use OpenAI transforms for the base parameters
|
|
45
|
+
openai_params = OpenAI::Chat::Transforms.normalize_params(params)
|
|
46
|
+
|
|
47
|
+
[ openai_params, ollama_params ]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Normalizes messages using OpenAI transforms
|
|
51
|
+
#
|
|
52
|
+
# @param messages [Array, String, Hash, nil]
|
|
53
|
+
# @return [Array<OpenAI::Models::Chat::ChatCompletionMessageParam>, nil]
|
|
54
|
+
def normalize_messages(messages)
|
|
55
|
+
OpenAI::Chat::Transforms.normalize_messages(messages)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Normalizes instructions using OpenAI transforms
|
|
59
|
+
#
|
|
60
|
+
# @param instructions [Array<String>, String]
|
|
61
|
+
# @return [Array<OpenAI::Models::Chat::ChatCompletionMessageParam>]
|
|
62
|
+
def normalize_instructions(instructions)
|
|
63
|
+
OpenAI::Chat::Transforms.normalize_instructions(instructions)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Cleans up serialized request for API submission
|
|
67
|
+
#
|
|
68
|
+
# Merges OpenAI-compatible params with Ollama-specific params.
|
|
69
|
+
# Also groups consecutive same-role messages as required by Ollama.
|
|
70
|
+
#
|
|
71
|
+
# @param openai_hash [Hash] serialized OpenAI request
|
|
72
|
+
# @param ollama_params [Hash] Ollama-specific parameters
|
|
73
|
+
# @param defaults [Hash] default values to remove
|
|
74
|
+
# @param gem_object [Object] original gem object
|
|
75
|
+
# @return [Hash] cleaned and merged request hash
|
|
76
|
+
def cleanup_serialized_request(openai_hash, ollama_params, defaults, gem_object)
|
|
77
|
+
# Start with OpenAI cleanup
|
|
78
|
+
cleaned = OpenAI::Chat::Transforms.cleanup_serialized_request(openai_hash, defaults, gem_object)
|
|
79
|
+
|
|
80
|
+
# Group consecutive same-role messages for Ollama
|
|
81
|
+
if cleaned[:messages]
|
|
82
|
+
cleaned[:messages] = group_same_role_messages(cleaned[:messages])
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Merge Ollama-specific params, but skip default values
|
|
86
|
+
ollama_params.each do |key, value|
|
|
87
|
+
# Skip if value is nil, empty, or matches the default
|
|
88
|
+
next if value.nil?
|
|
89
|
+
next if value.respond_to?(:empty?) && value.empty?
|
|
90
|
+
next if defaults.key?(key) && defaults[key] == value
|
|
91
|
+
|
|
92
|
+
cleaned[key] = value
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
cleaned
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Groups consecutive same-role messages for Ollama
|
|
99
|
+
#
|
|
100
|
+
# Ollama requires consecutive messages with the same role to be merged
|
|
101
|
+
# by concatenating their content.
|
|
102
|
+
#
|
|
103
|
+
# @param messages [Array<Hash>] array of message hashes
|
|
104
|
+
# @return [Array<Hash>] grouped messages
|
|
105
|
+
def group_same_role_messages(messages)
|
|
106
|
+
return [] if messages.nil? || messages.empty?
|
|
107
|
+
|
|
108
|
+
grouped = []
|
|
109
|
+
messages.each do |message|
|
|
110
|
+
if grouped.empty? || grouped.last[:role] != message[:role]
|
|
111
|
+
grouped << message.deep_dup
|
|
112
|
+
else
|
|
113
|
+
# Concatenate content for same-role messages
|
|
114
|
+
last_content = grouped.last[:content]
|
|
115
|
+
new_content = message[:content]
|
|
116
|
+
|
|
117
|
+
grouped.last[:content] = if last_content.is_a?(Array) && new_content.is_a?(Array)
|
|
118
|
+
last_content + new_content
|
|
119
|
+
elsif last_content.is_a?(String) && new_content.is_a?(String)
|
|
120
|
+
last_content + new_content
|
|
121
|
+
else
|
|
122
|
+
# Mix of types, convert to array
|
|
123
|
+
Array(last_content) + Array(new_content)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
grouped
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -1,74 +1,187 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
4
|
-
|
|
3
|
+
require "delegate"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "transforms"
|
|
5
6
|
|
|
6
7
|
module ActiveAgent
|
|
7
8
|
module Providers
|
|
8
9
|
module Ollama
|
|
9
10
|
module Embedding
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
# Wraps OpenAI gem's EmbeddingCreateParams with Ollama-specific extensions
|
|
12
|
+
#
|
|
13
|
+
# Delegates to OpenAI::Models::EmbeddingCreateParams for OpenAI-compatible
|
|
14
|
+
# parameters while adding support for Ollama-specific features like options,
|
|
15
|
+
# keep_alive, and truncate.
|
|
16
|
+
#
|
|
17
|
+
# Ollama-specific parameters:
|
|
18
|
+
# - options: Additional model parameters (Hash with temperature, seed, etc.)
|
|
19
|
+
# - keep_alive: Controls how long model stays in memory (String duration)
|
|
20
|
+
# - truncate: Truncates input to fit context length (Boolean)
|
|
21
|
+
#
|
|
22
|
+
# @example Basic usage
|
|
23
|
+
# request = Request.new(
|
|
24
|
+
# model: "llama2",
|
|
25
|
+
# input: "Hello world"
|
|
26
|
+
# )
|
|
27
|
+
#
|
|
28
|
+
# @example With Ollama-specific features
|
|
29
|
+
# request = Request.new(
|
|
30
|
+
# model: "llama2",
|
|
31
|
+
# input: ["Hello", "World"],
|
|
32
|
+
# options: {temperature: 0.7, seed: 42},
|
|
33
|
+
# keep_alive: "10m",
|
|
34
|
+
# truncate: false
|
|
35
|
+
# )
|
|
36
|
+
#
|
|
37
|
+
# @example With delegated option attributes
|
|
38
|
+
# request = Request.new(
|
|
39
|
+
# model: "llama2",
|
|
40
|
+
# input: "Hello",
|
|
41
|
+
# temperature: 0.7, # Automatically goes to options
|
|
42
|
+
# seed: 42 # Automatically goes to options
|
|
43
|
+
# )
|
|
44
|
+
class Request < SimpleDelegator
|
|
45
|
+
# Default parameter values
|
|
46
|
+
DEFAULTS = {
|
|
47
|
+
truncate: true,
|
|
48
|
+
keep_alive: "5m"
|
|
49
|
+
}.freeze
|
|
50
|
+
|
|
51
|
+
# @return [Hash] Ollama-specific parameters
|
|
52
|
+
attr_reader :ollama_params
|
|
53
|
+
|
|
54
|
+
# Creates a new Ollama embedding request
|
|
55
|
+
#
|
|
56
|
+
# @param params [Hash] request parameters
|
|
57
|
+
# @option params [String] :model model identifier (required)
|
|
58
|
+
# @option params [String, Array<String>] :input text to embed (required)
|
|
59
|
+
# @option params [Hash] :options model-specific options
|
|
60
|
+
# @option params [String] :keep_alive memory duration
|
|
61
|
+
# @option params [Boolean] :truncate truncate to context length
|
|
62
|
+
# @option params [Float] :temperature delegated to options
|
|
63
|
+
# @option params [Integer] :seed delegated to options
|
|
64
|
+
# @raise [ArgumentError] when parameters are invalid
|
|
65
|
+
def initialize(**params)
|
|
66
|
+
# Step 1: Apply defaults
|
|
67
|
+
params = apply_defaults(params)
|
|
68
|
+
|
|
69
|
+
# Step 2: Validate presence of required params before normalization
|
|
70
|
+
raise ArgumentError, "model is required" unless params[:model]
|
|
71
|
+
raise ArgumentError, "input is required" unless params[:input]
|
|
72
|
+
|
|
73
|
+
# Step 3: Normalize parameters and split into OpenAI vs Ollama-specific
|
|
74
|
+
openai_params, @ollama_params = Transforms.normalize_params(params)
|
|
75
|
+
|
|
76
|
+
# Step 4: Create gem model with OpenAI-compatible params
|
|
77
|
+
gem_model = ::OpenAI::Models::EmbeddingCreateParams.new(**openai_params)
|
|
78
|
+
|
|
79
|
+
# Step 5: Delegate to the gem model
|
|
80
|
+
super(gem_model)
|
|
81
|
+
rescue ArgumentError => e
|
|
82
|
+
raise ArgumentError, "Invalid Ollama Embedding request parameters: #{e.message}"
|
|
83
|
+
end
|
|
13
84
|
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
|
|
85
|
+
# Serializes request for API submission
|
|
86
|
+
#
|
|
87
|
+
# Merges OpenAI-compatible parameters with Ollama-specific extensions.
|
|
88
|
+
#
|
|
89
|
+
# @return [Hash] cleaned request hash
|
|
90
|
+
def serialize
|
|
91
|
+
# Get OpenAI params from gem model
|
|
92
|
+
openai_hash = Transforms.gem_to_hash(__getobj__)
|
|
93
|
+
|
|
94
|
+
# Merge with Ollama-specific params
|
|
95
|
+
Transforms.cleanup_serialized_request(openai_hash, @ollama_params, DEFAULTS)
|
|
96
|
+
end
|
|
17
97
|
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
|
|
98
|
+
# Accessor for input parameter
|
|
99
|
+
#
|
|
100
|
+
# @return [Array<String>, nil]
|
|
101
|
+
def input
|
|
102
|
+
__getobj__.instance_variable_get(:@data)[:input]
|
|
103
|
+
end
|
|
22
104
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
|
|
105
|
+
# Sets input with normalization
|
|
106
|
+
#
|
|
107
|
+
# @param value [String, Array<String>]
|
|
108
|
+
# @return [void]
|
|
109
|
+
def input=(value)
|
|
110
|
+
normalized_value = Transforms.normalize_input(value)
|
|
111
|
+
__getobj__.instance_variable_get(:@data)[:input] = normalized_value
|
|
112
|
+
end
|
|
26
113
|
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
|
|
114
|
+
# Accessor for Ollama options parameter
|
|
115
|
+
#
|
|
116
|
+
# @return [Hash, nil]
|
|
117
|
+
def options
|
|
118
|
+
@ollama_params[:options]
|
|
119
|
+
end
|
|
30
120
|
|
|
31
|
-
#
|
|
32
|
-
|
|
121
|
+
# Sets options parameter
|
|
122
|
+
#
|
|
123
|
+
# @param value [Hash]
|
|
124
|
+
# @return [void]
|
|
125
|
+
def options=(value)
|
|
126
|
+
@ollama_params[:options] = value
|
|
127
|
+
end
|
|
33
128
|
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
|
|
129
|
+
# Accessor for keep_alive parameter
|
|
130
|
+
#
|
|
131
|
+
# @return [String, nil]
|
|
132
|
+
def keep_alive
|
|
133
|
+
@ollama_params[:keep_alive] || DEFAULTS[:keep_alive]
|
|
134
|
+
end
|
|
37
135
|
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
136
|
+
# Sets keep_alive parameter
|
|
137
|
+
#
|
|
138
|
+
# @param value [String]
|
|
139
|
+
# @return [void]
|
|
140
|
+
def keep_alive=(value)
|
|
141
|
+
@ollama_params[:keep_alive] = value
|
|
142
|
+
end
|
|
42
143
|
|
|
43
|
-
|
|
144
|
+
# Accessor for truncate parameter
|
|
145
|
+
#
|
|
146
|
+
# @return [Boolean]
|
|
147
|
+
def truncate
|
|
148
|
+
@ollama_params.fetch(:truncate, DEFAULTS[:truncate])
|
|
149
|
+
end
|
|
44
150
|
|
|
45
|
-
|
|
46
|
-
|
|
151
|
+
# Sets truncate parameter
|
|
152
|
+
#
|
|
153
|
+
# @param value [Boolean]
|
|
154
|
+
# @return [void]
|
|
155
|
+
def truncate=(value)
|
|
156
|
+
@ollama_params[:truncate] = value
|
|
157
|
+
end
|
|
47
158
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
159
|
+
# Delegated option attribute accessors
|
|
160
|
+
# These allow setting option values at the top level
|
|
161
|
+
%i[mirostat mirostat_eta mirostat_tau num_ctx repeat_last_n
|
|
162
|
+
repeat_penalty temperature seed num_predict top_k top_p min_p stop].each do |attr|
|
|
163
|
+
define_method(attr) do
|
|
164
|
+
options&.dig(attr)
|
|
51
165
|
end
|
|
52
166
|
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
errors.add(:input, "array elements must be strings at index #{index}")
|
|
57
|
-
next
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
if item.empty?
|
|
61
|
-
errors.add(:input, "cannot contain empty strings at index #{index}")
|
|
62
|
-
end
|
|
167
|
+
define_method(:"#{attr}=") do |value|
|
|
168
|
+
@ollama_params[:options] ||= {}
|
|
169
|
+
@ollama_params[:options][attr] = value
|
|
63
170
|
end
|
|
64
171
|
end
|
|
65
172
|
|
|
66
|
-
|
|
67
|
-
return if input.nil?
|
|
173
|
+
private
|
|
68
174
|
|
|
69
|
-
|
|
70
|
-
|
|
175
|
+
# @api private
|
|
176
|
+
# @param params [Hash]
|
|
177
|
+
# @return [Hash]
|
|
178
|
+
def apply_defaults(params)
|
|
179
|
+
# Apply defaults
|
|
180
|
+
DEFAULTS.each do |key, value|
|
|
181
|
+
params[key] = value unless params.key?(key)
|
|
71
182
|
end
|
|
183
|
+
|
|
184
|
+
params
|
|
72
185
|
end
|
|
73
186
|
end
|
|
74
187
|
end
|