activeagent 1.0.0.rc1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +102 -1
- data/lib/active_agent/providers/_base_provider.rb +94 -82
- data/lib/active_agent/providers/anthropic/_types.rb +2 -2
- data/lib/active_agent/providers/anthropic/options.rb +4 -6
- data/lib/active_agent/providers/anthropic/request.rb +157 -78
- data/lib/active_agent/providers/anthropic/transforms.rb +482 -0
- data/lib/active_agent/providers/anthropic_provider.rb +159 -59
- data/lib/active_agent/providers/common/messages/_types.rb +46 -3
- data/lib/active_agent/providers/common/messages/assistant.rb +20 -4
- 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/concerns/previewable.rb +39 -5
- data/lib/active_agent/providers/concerns/tool_choice_clearing.rb +62 -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 +444 -0
- data/lib/active_agent/providers/open_ai/chat_provider.rb +95 -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 +116 -135
- data/lib/active_agent/providers/open_ai/responses/transforms.rb +363 -0
- data/lib/active_agent/providers/open_ai/responses_provider.rb +115 -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 +252 -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 +164 -0
- data/lib/active_agent/providers/open_router_provider.rb +23 -0
- data/lib/active_agent/version.rb +1 -1
- metadata +17 -160
- 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
|
@@ -8,11 +8,48 @@ require_relative "requests/provider_preferences"
|
|
|
8
8
|
module ActiveAgent
|
|
9
9
|
module Providers
|
|
10
10
|
module OpenRouter
|
|
11
|
+
# Configuration options for OpenRouter provider
|
|
12
|
+
#
|
|
13
|
+
# Extends OpenAI::Options with OpenRouter-specific settings including
|
|
14
|
+
# HTTP-Referer and X-Title headers for app identification and ranking.
|
|
15
|
+
#
|
|
16
|
+
# @example Basic configuration
|
|
17
|
+
# options = Options.new(
|
|
18
|
+
# api_key: 'sk-or-v1-...',
|
|
19
|
+
# app_name: 'MyApp',
|
|
20
|
+
# site_url: 'https://myapp.com'
|
|
21
|
+
# )
|
|
22
|
+
#
|
|
23
|
+
# @example Rails auto-configuration
|
|
24
|
+
# # Automatically resolves app_name from Rails.application
|
|
25
|
+
# # and site_url from routes.default_url_options
|
|
26
|
+
# options = Options.new(api_key: ENV['OPENROUTER_API_KEY'])
|
|
27
|
+
#
|
|
28
|
+
# @see https://openrouter.ai/docs/api-keys OpenRouter API Keys
|
|
29
|
+
# @see https://openrouter.ai/docs/rankings OpenRouter App Rankings
|
|
11
30
|
class Options < ActiveAgent::Providers::OpenAI::Options
|
|
31
|
+
# @!attribute base_url
|
|
32
|
+
# @return [String] API endpoint (default: "https://openrouter.ai/api/v1")
|
|
12
33
|
attribute :base_url, :string, as: "https://openrouter.ai/api/v1"
|
|
34
|
+
|
|
35
|
+
# @!attribute app_name
|
|
36
|
+
# @return [String] application name for X-Title header (default: "ActiveAgent" or Rails app name)
|
|
13
37
|
attribute :app_name, :string, fallback: "ActiveAgent"
|
|
38
|
+
|
|
39
|
+
# @!attribute site_url
|
|
40
|
+
# @return [String] site URL for HTTP-Referer header (default: "https://activeagents.ai/" or Rails URL)
|
|
14
41
|
attribute :site_url, :string, fallback: "https://activeagents.ai/"
|
|
15
42
|
|
|
43
|
+
# Creates new OpenRouter options with auto-resolution
|
|
44
|
+
#
|
|
45
|
+
# Automatically resolves app_name from Rails application name and
|
|
46
|
+
# site_url from Rails routes/ActionMailer default_url_options.
|
|
47
|
+
#
|
|
48
|
+
# @param kwargs [Hash] configuration options
|
|
49
|
+
# @option kwargs [String] :api_key OpenRouter API key
|
|
50
|
+
# @option kwargs [String] :app_name application name for rankings
|
|
51
|
+
# @option kwargs [String] :site_url site URL for rankings
|
|
52
|
+
# @return [Options]
|
|
16
53
|
def initialize(kwargs = {})
|
|
17
54
|
kwargs = kwargs.deep_symbolize_keys if kwargs.respond_to?(:deep_symbolize_keys)
|
|
18
55
|
|
|
@@ -22,11 +59,22 @@ module ActiveAgent
|
|
|
22
59
|
)))
|
|
23
60
|
end
|
|
24
61
|
|
|
62
|
+
# Serializes options for API requests
|
|
63
|
+
#
|
|
64
|
+
# Excludes app_name and site_url as they're sent via headers.
|
|
65
|
+
#
|
|
66
|
+
# @return [Hash] serialized options
|
|
25
67
|
def serialize
|
|
26
68
|
super.except(:app_name, :site_url)
|
|
27
69
|
end
|
|
28
70
|
|
|
29
|
-
#
|
|
71
|
+
# Returns extra headers for OpenRouter API
|
|
72
|
+
#
|
|
73
|
+
# Maps app_name and site_url to OpenRouter's required headers:
|
|
74
|
+
# - HTTP-Referer: site_url
|
|
75
|
+
# - X-Title: app_name
|
|
76
|
+
#
|
|
77
|
+
# @return [Hash] headers hash
|
|
30
78
|
def extra_headers
|
|
31
79
|
deep_compact(
|
|
32
80
|
"http-referer" => site_url.presence,
|
|
@@ -1,81 +1,267 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require "delegate"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "transforms"
|
|
4
6
|
require_relative "requests/_types"
|
|
5
7
|
|
|
6
8
|
module ActiveAgent
|
|
7
9
|
module Providers
|
|
8
10
|
module OpenRouter
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#
|
|
11
|
+
# Wraps OpenAI gem's CompletionCreateParams with OpenRouter-specific extensions
|
|
12
|
+
#
|
|
13
|
+
# Delegates to OpenAI::Models::Chat::CompletionCreateParams for OpenAI-compatible
|
|
14
|
+
# parameters while adding support for OpenRouter-specific features like plugins,
|
|
15
|
+
# provider preferences, model fallbacks, and extended sampling parameters.
|
|
16
|
+
#
|
|
17
|
+
# OpenRouter-specific parameters:
|
|
18
|
+
# - plugins: Array of plugin configurations (e.g., file-parser for PDFs)
|
|
19
|
+
# - provider: ProviderPreferences object with require_parameters, data_collection, etc.
|
|
20
|
+
# - transforms: Array of transformation strings
|
|
21
|
+
# - models: Array of model strings for fallback routing
|
|
22
|
+
# - route: Routing strategy (default: "fallback")
|
|
23
|
+
# - top_k, min_p, top_a, repetition_penalty: Extended sampling parameters
|
|
24
|
+
#
|
|
25
|
+
# @example Basic usage
|
|
26
|
+
# request = Request.new(
|
|
27
|
+
# model: "openai/gpt-4",
|
|
28
|
+
# messages: [{role: "user", content: "Hello"}]
|
|
29
|
+
# )
|
|
30
|
+
#
|
|
31
|
+
# @example With OpenRouter-specific features
|
|
32
|
+
# request = Request.new(
|
|
33
|
+
# model: "openai/gpt-4",
|
|
34
|
+
# messages: [{role: "user", content: "Hello"}],
|
|
35
|
+
# models: ["anthropic/claude-3", "openai/gpt-4"],
|
|
36
|
+
# provider: {require_parameters: true}
|
|
37
|
+
# )
|
|
38
|
+
class Request < SimpleDelegator
|
|
39
|
+
# Default parameter values
|
|
40
|
+
DEFAULTS = {
|
|
41
|
+
frequency_penalty: 0,
|
|
42
|
+
logprobs: false,
|
|
43
|
+
n: 1,
|
|
44
|
+
presence_penalty: 0,
|
|
45
|
+
temperature: 1,
|
|
46
|
+
top_p: 1,
|
|
47
|
+
route: "fallback",
|
|
48
|
+
models: [],
|
|
49
|
+
transforms: []
|
|
50
|
+
}.freeze
|
|
51
|
+
|
|
52
|
+
# @return [Boolean, nil]
|
|
53
|
+
attr_reader :stream
|
|
54
|
+
|
|
55
|
+
# @return [Hash] OpenRouter-specific parameters
|
|
56
|
+
attr_reader :openrouter_params
|
|
57
|
+
|
|
58
|
+
# Creates a new OpenRouter request
|
|
59
|
+
#
|
|
60
|
+
# @param params [Hash] request parameters
|
|
61
|
+
# @option params [String] :model model identifier (default: "openrouter/auto")
|
|
62
|
+
# @option params [Array, String, Hash] :messages required conversation messages
|
|
63
|
+
# @option params [Array] :plugins plugin configurations
|
|
64
|
+
# @option params [Hash] :provider provider preferences
|
|
65
|
+
# @option params [Array<String>] :transforms transformation strings
|
|
66
|
+
# @option params [Array<String>] :models fallback model list
|
|
67
|
+
# @option params [String] :route routing strategy
|
|
68
|
+
# @option params [Integer] :top_k sampling parameter
|
|
69
|
+
# @option params [Float] :min_p minimum probability sampling
|
|
70
|
+
# @option params [Float] :top_a top-a sampling
|
|
71
|
+
# @option params [Float] :repetition_penalty repetition penalty
|
|
72
|
+
# @raise [ArgumentError] when parameters are invalid
|
|
73
|
+
def initialize(**params)
|
|
74
|
+
# Step 1: Extract stream flag
|
|
75
|
+
@stream = params[:stream]
|
|
76
|
+
|
|
77
|
+
# Step 2: Apply defaults
|
|
78
|
+
params = apply_defaults(params)
|
|
79
|
+
|
|
80
|
+
# Step 3: Normalize parameters and split into OpenAI vs OpenRouter-specific
|
|
81
|
+
# This handles response_format special logic for structured output
|
|
82
|
+
openai_params, @openrouter_params = Transforms.normalize_params(params)
|
|
83
|
+
|
|
84
|
+
# Step 4: Create gem model with OpenAI-compatible params
|
|
85
|
+
gem_model = ::OpenAI::Models::Chat::CompletionCreateParams.new(**openai_params)
|
|
86
|
+
|
|
87
|
+
# Step 5: Delegate to the gem model
|
|
88
|
+
super(gem_model)
|
|
89
|
+
rescue ArgumentError => e
|
|
90
|
+
raise ArgumentError, "Invalid OpenRouter request parameters: #{e.message}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Serializes request for API submission
|
|
94
|
+
#
|
|
95
|
+
# Merges OpenAI-compatible parameters with OpenRouter-specific extensions.
|
|
96
|
+
#
|
|
97
|
+
# @return [Hash] cleaned request hash
|
|
98
|
+
def serialize
|
|
99
|
+
# Get OpenAI params from gem model
|
|
100
|
+
openai_hash = Transforms.gem_to_hash(__getobj__)
|
|
101
|
+
|
|
102
|
+
# Merge with OpenRouter-specific params
|
|
103
|
+
Transforms.cleanup_serialized_request(openai_hash, @openrouter_params, DEFAULTS, __getobj__)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [Array<Hash>, nil]
|
|
107
|
+
def messages
|
|
108
|
+
__getobj__.instance_variable_get(:@data)[:messages]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Sets messages with normalization
|
|
112
|
+
#
|
|
113
|
+
# Merges new messages with existing ones for compatibility.
|
|
114
|
+
#
|
|
115
|
+
# @param value [Array, String, Hash]
|
|
116
|
+
# @return [void]
|
|
62
117
|
def messages=(value)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
118
|
+
normalized_value = Transforms.normalize_messages(value)
|
|
119
|
+
current_messages = messages || []
|
|
120
|
+
|
|
121
|
+
# Merge behavior for OpenRouter compatibility
|
|
122
|
+
merged = current_messages | Array(normalized_value)
|
|
123
|
+
__getobj__.instance_variable_get(:@data)[:messages] = merged
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Alias for messages (common format compatibility)
|
|
127
|
+
#
|
|
128
|
+
# @return [Array<Hash>, nil]
|
|
129
|
+
def message
|
|
130
|
+
messages
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# @param value [Array, String, Hash]
|
|
134
|
+
# @return [void]
|
|
135
|
+
def message=(value)
|
|
136
|
+
self.messages = value
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Sets instructions as developer messages
|
|
140
|
+
#
|
|
141
|
+
# Prepends developer messages to the messages array.
|
|
142
|
+
#
|
|
143
|
+
# @param values [Array<String>, String]
|
|
144
|
+
# @return [void]
|
|
145
|
+
def instructions=(*values)
|
|
146
|
+
instructions_messages = OpenAI::Chat::Transforms.normalize_instructions(values.flatten)
|
|
147
|
+
current_messages = messages || []
|
|
148
|
+
self.messages = instructions_messages + current_messages
|
|
69
149
|
end
|
|
70
150
|
|
|
71
|
-
#
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
+
|
|
171
|
+
# Accessor for OpenRouter-specific provider preferences
|
|
172
|
+
#
|
|
173
|
+
# @return [Hash, nil]
|
|
174
|
+
def provider
|
|
175
|
+
@openrouter_params[:provider]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Sets provider preferences
|
|
179
|
+
#
|
|
180
|
+
# @param value [Hash]
|
|
181
|
+
# @return [void]
|
|
182
|
+
def provider=(value)
|
|
183
|
+
@openrouter_params[:provider] = value
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Accessor for OpenRouter plugins
|
|
187
|
+
#
|
|
188
|
+
# @return [Array, nil]
|
|
189
|
+
def plugins
|
|
190
|
+
@openrouter_params[:plugins]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Sets plugins
|
|
194
|
+
#
|
|
195
|
+
# @param value [Array]
|
|
196
|
+
# @return [void]
|
|
197
|
+
def plugins=(value)
|
|
198
|
+
@openrouter_params[:plugins] = value
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Accessor for OpenRouter transforms
|
|
202
|
+
#
|
|
203
|
+
# @return [Array]
|
|
204
|
+
def transforms
|
|
205
|
+
@openrouter_params[:transforms] || []
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Sets transforms
|
|
209
|
+
#
|
|
210
|
+
# @param value [Array]
|
|
211
|
+
# @return [void]
|
|
212
|
+
def transforms=(value)
|
|
213
|
+
@openrouter_params[:transforms] = value
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Accessor for fallback models
|
|
217
|
+
#
|
|
218
|
+
# @return [Array]
|
|
219
|
+
def models
|
|
220
|
+
@openrouter_params[:models] || []
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Sets fallback models
|
|
224
|
+
#
|
|
225
|
+
# @param value [Array]
|
|
226
|
+
# @return [void]
|
|
227
|
+
def models=(value)
|
|
228
|
+
@openrouter_params[:models] = value
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Alias for backwards compatibility
|
|
232
|
+
alias_method :fallback_models, :models
|
|
233
|
+
alias_method :fallback_models=, :models=
|
|
234
|
+
|
|
235
|
+
# Accessor for routing strategy
|
|
236
|
+
#
|
|
237
|
+
# @return [String]
|
|
238
|
+
def route
|
|
239
|
+
@openrouter_params[:route] || DEFAULTS[:route]
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Sets routing strategy
|
|
243
|
+
#
|
|
244
|
+
# @param value [String]
|
|
245
|
+
# @return [void]
|
|
246
|
+
def route=(value)
|
|
247
|
+
@openrouter_params[:route] = value
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
private
|
|
251
|
+
|
|
252
|
+
# @api private
|
|
253
|
+
# @param params [Hash]
|
|
254
|
+
# @return [Hash]
|
|
255
|
+
def apply_defaults(params)
|
|
256
|
+
# Set default model if not provided
|
|
257
|
+
params[:model] ||= "openrouter/auto"
|
|
258
|
+
|
|
259
|
+
# Apply other defaults
|
|
260
|
+
DEFAULTS.each do |key, value|
|
|
261
|
+
params[key] = value unless params.key?(key)
|
|
76
262
|
end
|
|
77
263
|
|
|
78
|
-
|
|
264
|
+
params
|
|
79
265
|
end
|
|
80
266
|
end
|
|
81
267
|
end
|
|
@@ -1,55 +1,52 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require_relative "assistant"
|
|
6
|
-
require_relative "user"
|
|
3
|
+
require_relative "../../transforms"
|
|
7
4
|
|
|
8
5
|
module ActiveAgent
|
|
9
6
|
module Providers
|
|
10
7
|
module OpenRouter
|
|
11
8
|
module Requests
|
|
12
9
|
module Messages
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
# ActiveModel type for casting and normalizing messages
|
|
11
|
+
#
|
|
12
|
+
# Delegates to OpenRouter transforms which use OpenAI's message normalization.
|
|
13
|
+
class MessagesType < ActiveModel::Type::Value
|
|
14
|
+
# Casts value to normalized messages array
|
|
15
|
+
#
|
|
16
|
+
# @param value [Array, String, Hash, nil]
|
|
17
|
+
# @return [Array, nil]
|
|
18
|
+
def cast(value)
|
|
19
|
+
return nil if value.nil?
|
|
20
|
+
Transforms.normalize_messages(value)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Serializes messages to hash array
|
|
24
|
+
#
|
|
25
|
+
# @param value [Array, nil]
|
|
26
|
+
# @return [Array, nil]
|
|
27
|
+
def serialize(value)
|
|
28
|
+
return nil if value.nil?
|
|
29
|
+
|
|
30
|
+
# If already serialized as hashes, return as-is
|
|
31
|
+
return value if value.is_a?(Array) && value.all? { |m| m.is_a?(Hash) }
|
|
32
|
+
|
|
33
|
+
# Otherwise convert gem objects to hashes
|
|
34
|
+
value.map { |msg| Transforms.gem_to_hash(msg) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param value [Object]
|
|
38
|
+
# @return [Array, nil]
|
|
39
|
+
def deserialize(value)
|
|
40
|
+
cast(value)
|
|
18
41
|
end
|
|
19
42
|
end
|
|
20
43
|
|
|
21
|
-
|
|
44
|
+
# Kept for backwards compatibility but delegates to MessagesType
|
|
45
|
+
class MessageType < MessagesType
|
|
22
46
|
def cast(value)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
when String
|
|
27
|
-
User.new(content: value)
|
|
28
|
-
when Hash
|
|
29
|
-
hash = value.deep_symbolize_keys
|
|
30
|
-
role = hash[:role]&.to_sym
|
|
31
|
-
|
|
32
|
-
case role
|
|
33
|
-
when :developer
|
|
34
|
-
OpenAI::Chat::Requests::Messages::Developer.new(**hash)
|
|
35
|
-
when :system
|
|
36
|
-
OpenAI::Chat::Requests::Messages::System.new(**hash)
|
|
37
|
-
when :user, nil
|
|
38
|
-
User.new(**hash)
|
|
39
|
-
when :assistant
|
|
40
|
-
Assistant.new(**hash)
|
|
41
|
-
when :tool
|
|
42
|
-
OpenAI::Chat::Requests::Messages::Tool.new(**hash)
|
|
43
|
-
when :function
|
|
44
|
-
OpenAI::Chat::Requests::Messages::Function.new(**hash)
|
|
45
|
-
else
|
|
46
|
-
raise ArgumentError, "Unknown message role: #{role.inspect}"
|
|
47
|
-
end
|
|
48
|
-
when nil
|
|
49
|
-
nil
|
|
50
|
-
else
|
|
51
|
-
raise ArgumentError, "Cannot cast #{value.class} to Message (expected Base, String, Hash, or nil)"
|
|
52
|
-
end
|
|
47
|
+
# Single message - wrap in array then unwrap
|
|
48
|
+
result = super(value.is_a?(Array) ? value : [ value ])
|
|
49
|
+
result&.first
|
|
53
50
|
end
|
|
54
51
|
end
|
|
55
52
|
end
|
|
@@ -9,12 +9,28 @@ module ActiveAgent
|
|
|
9
9
|
module Requests
|
|
10
10
|
module Messages
|
|
11
11
|
module Content
|
|
12
|
-
# File content part for OpenRouter
|
|
12
|
+
# File content part for OpenRouter messages
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
14
|
+
# Represents a file attachment in a message. Unlike OpenAI which strips
|
|
15
|
+
# the data URI prefix, OpenRouter preserves it in the file_data field.
|
|
16
|
+
#
|
|
17
|
+
# @example PDF file attachment
|
|
18
|
+
# file = File.new(
|
|
19
|
+
# file: {
|
|
20
|
+
# file_data: 'data:application/pdf;base64,JVBERi0...',
|
|
21
|
+
# filename: 'document.pdf'
|
|
22
|
+
# }
|
|
23
|
+
# )
|
|
24
|
+
#
|
|
25
|
+
# @see Files::Details
|
|
26
|
+
# @see https://openrouter.ai/docs/file-uploads OpenRouter File Uploads
|
|
16
27
|
class File < OpenAI::Chat::Requests::Messages::Content::Base
|
|
28
|
+
# @!attribute type
|
|
29
|
+
# @return [String] always "file"
|
|
17
30
|
attribute :type, :string, as: "file"
|
|
31
|
+
|
|
32
|
+
# @!attribute file
|
|
33
|
+
# @return [Files::Details] file details with data URI
|
|
18
34
|
attribute :file, Files::DetailsType.new
|
|
19
35
|
|
|
20
36
|
validates :file, presence: true
|
|
@@ -9,12 +9,23 @@ module ActiveAgent
|
|
|
9
9
|
module Messages
|
|
10
10
|
module Content
|
|
11
11
|
module Files
|
|
12
|
-
#
|
|
12
|
+
# File details for OpenRouter file attachments
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
# (e.g., data:application/pdf;base64,)
|
|
14
|
+
# Represents the nested file object within a file content part.
|
|
15
|
+
# Unlike OpenAI which strips the data URI prefix (e.g., data:application/pdf;base64,),
|
|
16
|
+
# OpenRouter requires it to be present in the file_data field.
|
|
17
|
+
#
|
|
18
|
+
# @example With data URI
|
|
19
|
+
# details = Details.new(
|
|
20
|
+
# file_data: 'data:application/pdf;base64,JVBERi0xLjQK...',
|
|
21
|
+
# filename: 'report.pdf'
|
|
22
|
+
# )
|
|
23
|
+
#
|
|
24
|
+
# @see Content::File
|
|
16
25
|
class Details < OpenAI::Chat::Requests::Messages::Content::Files::Details
|
|
17
|
-
#
|
|
26
|
+
# @!attribute file_data
|
|
27
|
+
# @return [String] file data with data URI prefix intact
|
|
28
|
+
# Format: "data:<mime-type>;base64,<base64-data>"
|
|
18
29
|
attribute :file_data, :string
|
|
19
30
|
end
|
|
20
31
|
end
|
|
@@ -4,16 +4,32 @@ module ActiveAgent
|
|
|
4
4
|
module Providers
|
|
5
5
|
module OpenRouter
|
|
6
6
|
module Requests
|
|
7
|
-
#
|
|
8
|
-
# Currently supports the file-parser plugin for PDF processing.
|
|
7
|
+
# Plugin configuration for OpenRouter requests
|
|
9
8
|
#
|
|
10
|
-
#
|
|
9
|
+
# OpenRouter supports plugins that enhance model capabilities.
|
|
10
|
+
# Currently supports the file-parser plugin for processing PDF documents.
|
|
11
|
+
#
|
|
12
|
+
# @example File parser plugin with PDF text extraction
|
|
11
13
|
# plugin = Plugin.new(
|
|
12
14
|
# id: 'file-parser',
|
|
13
15
|
# pdf: { engine: 'pdf-text' }
|
|
14
16
|
# )
|
|
17
|
+
#
|
|
18
|
+
# @example File parser plugin with OCR
|
|
19
|
+
# plugin = Plugin.new(
|
|
20
|
+
# id: 'file-parser',
|
|
21
|
+
# pdf: { engine: 'mistral-ocr' }
|
|
22
|
+
# )
|
|
23
|
+
#
|
|
24
|
+
# @see https://openrouter.ai/docs/plugins OpenRouter Plugins
|
|
25
|
+
# @see Plugins::PdfConfig
|
|
15
26
|
class Plugin < Common::BaseModel
|
|
27
|
+
# @!attribute id
|
|
28
|
+
# @return [String] plugin identifier (currently only 'file-parser' is supported)
|
|
16
29
|
attribute :id, :string
|
|
30
|
+
|
|
31
|
+
# @!attribute pdf
|
|
32
|
+
# @return [Plugins::PdfConfig, nil] PDF processing configuration
|
|
17
33
|
attribute :pdf, Plugins::PdfConfigType.new
|
|
18
34
|
|
|
19
35
|
validates :id, presence: true
|
|
@@ -5,19 +5,41 @@ module ActiveAgent
|
|
|
5
5
|
module OpenRouter
|
|
6
6
|
module Requests
|
|
7
7
|
module Plugins
|
|
8
|
-
#
|
|
8
|
+
# PDF processing configuration for file-parser plugin
|
|
9
9
|
#
|
|
10
|
-
# OpenRouter provides
|
|
11
|
-
#
|
|
12
|
-
# - "pdf-text": Best for well-structured PDFs with clear text content (Free)
|
|
13
|
-
# - "native": Only available for models that support file input natively (charged as input tokens)
|
|
10
|
+
# OpenRouter provides multiple PDF processing engines with different
|
|
11
|
+
# capabilities and costs:
|
|
14
12
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
13
|
+
# - **mistral-ocr**: Best for scanned documents or PDFs with images
|
|
14
|
+
# - Cost: $2 per 1,000 pages
|
|
15
|
+
# - Use when: Document is image-heavy or has poor text extraction
|
|
17
16
|
#
|
|
18
|
-
#
|
|
17
|
+
# - **pdf-text**: Best for well-structured PDFs with clear text content
|
|
18
|
+
# - Cost: Free
|
|
19
|
+
# - Use when: Document has clean, extractable text
|
|
20
|
+
#
|
|
21
|
+
# - **native**: Use model's native file processing capabilities
|
|
22
|
+
# - Cost: Charged as input tokens
|
|
23
|
+
# - Use when: Model supports native file input
|
|
24
|
+
#
|
|
25
|
+
# If no engine is specified, OpenRouter defaults to the model's native
|
|
26
|
+
# file processing if available, otherwise uses mistral-ocr.
|
|
27
|
+
#
|
|
28
|
+
# @example Text extraction (free)
|
|
19
29
|
# pdf_config = PdfConfig.new(engine: 'pdf-text')
|
|
30
|
+
#
|
|
31
|
+
# @example OCR for scanned documents
|
|
32
|
+
# pdf_config = PdfConfig.new(engine: 'mistral-ocr')
|
|
33
|
+
#
|
|
34
|
+
# @example Use model's native processing
|
|
35
|
+
# pdf_config = PdfConfig.new(engine: 'native')
|
|
36
|
+
#
|
|
37
|
+
# @see https://openrouter.ai/docs/plugins#file-parser OpenRouter File Parser Plugin
|
|
38
|
+
# @see Plugin
|
|
20
39
|
class PdfConfig < Common::BaseModel
|
|
40
|
+
# @!attribute engine
|
|
41
|
+
# @return [String, nil] PDF processing engine
|
|
42
|
+
# Options: 'mistral-ocr', 'pdf-text', 'native'
|
|
21
43
|
attribute :engine, :string
|
|
22
44
|
|
|
23
45
|
validates :engine, inclusion: { in: %w[mistral-ocr pdf-text native] }, allow_nil: true
|