activeagent 0.6.3 → 1.0.0.rc1
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 +210 -2
- data/README.md +15 -24
- data/lib/active_agent/base.rb +389 -39
- data/lib/active_agent/concerns/callbacks.rb +251 -0
- data/lib/active_agent/concerns/observers.rb +147 -0
- data/lib/active_agent/concerns/parameterized.rb +292 -0
- data/lib/active_agent/concerns/provider.rb +120 -0
- data/lib/active_agent/concerns/queueing.rb +36 -0
- data/lib/active_agent/concerns/rescue.rb +64 -0
- data/lib/active_agent/concerns/streaming.rb +282 -0
- data/lib/active_agent/concerns/tooling.rb +23 -0
- data/lib/active_agent/concerns/view.rb +150 -0
- data/lib/active_agent/configuration.rb +442 -20
- data/lib/active_agent/generation.rb +141 -47
- data/lib/active_agent/generation_provider/open_router/types.rb +505 -0
- data/lib/active_agent/generation_provider/xai_provider.rb +144 -0
- data/lib/active_agent/providers/_base_provider.rb +410 -0
- data/lib/active_agent/providers/anthropic/_types.rb +63 -0
- data/lib/active_agent/providers/anthropic/options.rb +53 -0
- data/lib/active_agent/providers/anthropic/request.rb +109 -0
- data/lib/active_agent/providers/anthropic/requests/_types.rb +190 -0
- data/lib/active_agent/providers/anthropic/requests/container_params.rb +19 -0
- data/lib/active_agent/providers/anthropic/requests/content/base.rb +21 -0
- data/lib/active_agent/providers/anthropic/requests/content/sources/base.rb +22 -0
- data/lib/active_agent/providers/anthropic/requests/context_management_config.rb +18 -0
- data/lib/active_agent/providers/anthropic/requests/messages/_types.rb +189 -0
- data/lib/active_agent/providers/anthropic/requests/messages/assistant.rb +23 -0
- data/lib/active_agent/providers/anthropic/requests/messages/base.rb +63 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/_types.rb +143 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/base.rb +21 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/document.rb +26 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/image.rb +23 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/redacted_thinking.rb +21 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/search_result.rb +27 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/_types.rb +171 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/base.rb +22 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_base64.rb +25 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_file.rb +23 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_text.rb +25 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_url.rb +23 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_base64.rb +27 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_file.rb +23 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_url.rb +23 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/text.rb +22 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/thinking.rb +23 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_result.rb +24 -0
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_use.rb +28 -0
- data/lib/active_agent/providers/anthropic/requests/messages/user.rb +21 -0
- data/lib/active_agent/providers/anthropic/requests/metadata.rb +18 -0
- data/lib/active_agent/providers/anthropic/requests/response_format.rb +22 -0
- data/lib/active_agent/providers/anthropic/requests/thinking_config/_types.rb +60 -0
- data/lib/active_agent/providers/anthropic/requests/thinking_config/base.rb +20 -0
- data/lib/active_agent/providers/anthropic/requests/thinking_config/disabled.rb +16 -0
- data/lib/active_agent/providers/anthropic/requests/thinking_config/enabled.rb +20 -0
- data/lib/active_agent/providers/anthropic/requests/tool_choice/_types.rb +78 -0
- data/lib/active_agent/providers/anthropic/requests/tool_choice/any.rb +17 -0
- data/lib/active_agent/providers/anthropic/requests/tool_choice/auto.rb +17 -0
- data/lib/active_agent/providers/anthropic/requests/tool_choice/base.rb +20 -0
- data/lib/active_agent/providers/anthropic/requests/tool_choice/none.rb +16 -0
- data/lib/active_agent/providers/anthropic/requests/tool_choice/tool.rb +20 -0
- data/lib/active_agent/providers/anthropic_provider.rb +211 -0
- data/lib/active_agent/providers/common/messages/_types.rb +124 -0
- data/lib/active_agent/providers/common/messages/assistant.rb +57 -0
- data/lib/active_agent/providers/common/messages/base.rb +17 -0
- data/lib/active_agent/providers/common/messages/system.rb +20 -0
- data/lib/active_agent/providers/common/messages/tool.rb +21 -0
- data/lib/active_agent/providers/common/messages/user.rb +20 -0
- data/lib/active_agent/providers/common/model.rb +361 -0
- data/lib/active_agent/providers/common/response.rb +13 -0
- data/lib/active_agent/providers/common/responses/_types.rb +51 -0
- data/lib/active_agent/providers/common/responses/base.rb +151 -0
- data/lib/active_agent/providers/common/responses/embed.rb +33 -0
- data/lib/active_agent/providers/common/responses/format.rb +31 -0
- data/lib/active_agent/providers/common/responses/message.rb +3 -0
- data/lib/active_agent/providers/common/responses/prompt.rb +42 -0
- data/lib/active_agent/providers/concerns/exception_handler.rb +72 -0
- data/lib/active_agent/providers/concerns/previewable.rb +150 -0
- data/lib/active_agent/providers/log_subscriber.rb +360 -0
- data/lib/active_agent/providers/mock/_types.rb +77 -0
- data/lib/active_agent/providers/mock/embedding_request.rb +17 -0
- data/lib/active_agent/providers/mock/messages/_types.rb +103 -0
- data/lib/active_agent/providers/mock/messages/assistant.rb +26 -0
- data/lib/active_agent/providers/mock/messages/base.rb +63 -0
- data/lib/active_agent/providers/mock/messages/user.rb +18 -0
- data/lib/active_agent/providers/mock/options.rb +30 -0
- data/lib/active_agent/providers/mock/request.rb +38 -0
- data/lib/active_agent/providers/mock_provider.rb +311 -0
- data/lib/active_agent/providers/ollama/_types.rb +5 -0
- data/lib/active_agent/providers/ollama/chat/_types.rb +44 -0
- data/lib/active_agent/providers/ollama/chat/request.rb +70 -0
- data/lib/active_agent/providers/ollama/chat/requests/_types.rb +3 -0
- data/lib/active_agent/providers/ollama/chat/requests/messages/_types.rb +116 -0
- data/lib/active_agent/providers/ollama/chat/requests/messages/assistant.rb +19 -0
- data/lib/active_agent/providers/ollama/chat/requests/messages/user.rb +19 -0
- data/lib/active_agent/providers/ollama/embedding/_types.rb +44 -0
- data/lib/active_agent/providers/ollama/embedding/request.rb +77 -0
- data/lib/active_agent/providers/ollama/embedding/requests/_types.rb +83 -0
- data/lib/active_agent/providers/ollama/embedding/requests/options.rb +104 -0
- data/lib/active_agent/providers/ollama/options.rb +27 -0
- data/lib/active_agent/providers/ollama_provider.rb +95 -0
- data/lib/active_agent/providers/open_ai/_base.rb +58 -0
- data/lib/active_agent/providers/open_ai/_types.rb +5 -0
- data/lib/active_agent/providers/open_ai/chat/_types.rb +44 -0
- data/lib/active_agent/providers/open_ai/chat/request.rb +215 -0
- data/lib/active_agent/providers/open_ai/chat/requests/_types.rb +229 -0
- data/lib/active_agent/providers/open_ai/chat/requests/audio.rb +24 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/_types.rb +123 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/assistant.rb +42 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/base.rb +78 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/_types.rb +133 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/audio.rb +35 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/base.rb +24 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/file.rb +26 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/_types.rb +60 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/details.rb +41 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/image.rb +37 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/refusal.rb +25 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/text.rb +25 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/developer.rb +25 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/function.rb +25 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/system.rb +25 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/tool.rb +26 -0
- data/lib/active_agent/providers/open_ai/chat/requests/messages/user.rb +32 -0
- data/lib/active_agent/providers/open_ai/chat/requests/prediction.rb +46 -0
- data/lib/active_agent/providers/open_ai/chat/requests/response_format.rb +53 -0
- data/lib/active_agent/providers/open_ai/chat/requests/stream_options.rb +24 -0
- data/lib/active_agent/providers/open_ai/chat/requests/tool_choice.rb +26 -0
- data/lib/active_agent/providers/open_ai/chat/requests/tools/_types.rb +5 -0
- data/lib/active_agent/providers/open_ai/chat/requests/tools/base.rb +22 -0
- data/lib/active_agent/providers/open_ai/chat/requests/tools/custom_tool.rb +41 -0
- data/lib/active_agent/providers/open_ai/chat/requests/tools/function_tool.rb +51 -0
- data/lib/active_agent/providers/open_ai/chat/requests/web_search_options.rb +45 -0
- data/lib/active_agent/providers/open_ai/chat_provider.rb +198 -0
- data/lib/active_agent/providers/open_ai/embedding/_types.rb +45 -0
- data/lib/active_agent/providers/open_ai/embedding/request.rb +85 -0
- data/lib/active_agent/providers/open_ai/embedding/requests/_types.rb +49 -0
- data/lib/active_agent/providers/open_ai/options.rb +74 -0
- data/lib/active_agent/providers/open_ai/responses/_types.rb +50 -0
- data/lib/active_agent/providers/open_ai/responses/request.rb +163 -0
- data/lib/active_agent/providers/open_ai/responses/requests/_types.rb +231 -0
- data/lib/active_agent/providers/open_ai/responses/requests/conversation.rb +23 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/_types.rb +264 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/assistant_message.rb +22 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/base.rb +89 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/code_interpreter_tool_call.rb +30 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call.rb +28 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call_output.rb +33 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/_types.rb +207 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/base.rb +22 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_audio.rb +26 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_file.rb +28 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_image.rb +28 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_text.rb +25 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call.rb +28 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call_output.rb +27 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/developer_message.rb +20 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/file_search_tool_call.rb +25 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_call_output.rb +32 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_tool_call.rb +28 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/image_gen_tool_call.rb +27 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/input_message.rb +31 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/item_reference.rb +23 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call.rb +26 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call_output.rb +33 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_request.rb +30 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_response.rb +28 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_list_tools.rb +29 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_tool_call.rb +35 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/output_message.rb +35 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/reasoning.rb +33 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/system_message.rb +20 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_call_base.rb +27 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_message.rb +23 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/user_message.rb +20 -0
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/web_search_tool_call.rb +24 -0
- data/lib/active_agent/providers/open_ai/responses/requests/prompt_reference.rb +23 -0
- data/lib/active_agent/providers/open_ai/responses/requests/reasoning.rb +23 -0
- data/lib/active_agent/providers/open_ai/responses/requests/stream_options.rb +20 -0
- data/lib/active_agent/providers/open_ai/responses/requests/text/_types.rb +89 -0
- data/lib/active_agent/providers/open_ai/responses/requests/text/base.rb +22 -0
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_object.rb +20 -0
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_schema.rb +48 -0
- data/lib/active_agent/providers/open_ai/responses/requests/text/plain.rb +20 -0
- data/lib/active_agent/providers/open_ai/responses/requests/text.rb +41 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tool_choice.rb +26 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/_types.rb +112 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/base.rb +25 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/code_interpreter_tool.rb +23 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/computer_tool.rb +27 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/custom_tool.rb +28 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/file_search_tool.rb +27 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/function_tool.rb +29 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/image_generation_tool.rb +37 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/local_shell_tool.rb +21 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/mcp_tool.rb +41 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_preview_tool.rb +24 -0
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_tool.rb +25 -0
- data/lib/active_agent/providers/open_ai/responses_provider.rb +153 -0
- data/lib/active_agent/providers/open_ai/schema.yml +65937 -0
- data/lib/active_agent/providers/open_ai_provider.rb +97 -0
- data/lib/active_agent/providers/open_router/_types.rb +45 -0
- data/lib/active_agent/providers/open_router/options.rb +93 -0
- data/lib/active_agent/providers/open_router/request.rb +83 -0
- data/lib/active_agent/providers/open_router/requests/_types.rb +198 -0
- data/lib/active_agent/providers/open_router/requests/message.rb +1 -0
- data/lib/active_agent/providers/open_router/requests/messages/_types.rb +59 -0
- data/lib/active_agent/providers/open_router/requests/messages/assistant.rb +20 -0
- data/lib/active_agent/providers/open_router/requests/messages/content/_types.rb +97 -0
- data/lib/active_agent/providers/open_router/requests/messages/content/file.rb +27 -0
- data/lib/active_agent/providers/open_router/requests/messages/content/files/_types.rb +61 -0
- data/lib/active_agent/providers/open_router/requests/messages/content/files/details.rb +26 -0
- data/lib/active_agent/providers/open_router/requests/messages/user.rb +30 -0
- data/lib/active_agent/providers/open_router/requests/plugin.rb +25 -0
- data/lib/active_agent/providers/open_router/requests/plugins/_types.rb +46 -0
- data/lib/active_agent/providers/open_router/requests/plugins/pdf_config.rb +29 -0
- data/lib/active_agent/providers/open_router/requests/prediction.rb +17 -0
- data/lib/active_agent/providers/open_router/requests/provider_preferences/_types.rb +44 -0
- data/lib/active_agent/providers/open_router/requests/provider_preferences/max_price.rb +30 -0
- data/lib/active_agent/providers/open_router/requests/provider_preferences.rb +64 -0
- data/lib/active_agent/providers/open_router/requests/response_format.rb +49 -0
- data/lib/active_agent/providers/open_router_provider.rb +53 -0
- data/lib/active_agent/providers/openai_provider.rb +2 -0
- data/lib/active_agent/providers/openrouter_provider.rb +2 -0
- data/lib/active_agent/railtie.rb +8 -6
- data/lib/active_agent/schema_generator.rb +333 -166
- data/lib/active_agent/version.rb +1 -1
- data/lib/active_agent.rb +112 -36
- data/lib/generators/active_agent/agent/USAGE +78 -0
- data/lib/generators/active_agent/{agent_generator.rb → agent/agent_generator.rb} +14 -4
- data/lib/generators/active_agent/install/USAGE +25 -0
- data/lib/generators/active_agent/{install_generator.rb → install/install_generator.rb} +1 -19
- data/lib/generators/active_agent/templates/agent.rb.tt +7 -3
- data/lib/generators/active_agent/templates/application_agent.rb.tt +0 -2
- data/lib/generators/erb/agent_generator.rb +31 -16
- data/lib/generators/erb/templates/instructions.md.erb.tt +3 -0
- data/lib/generators/erb/templates/instructions.md.tt +3 -0
- data/lib/generators/erb/templates/instructions.text.tt +1 -0
- data/lib/generators/erb/templates/message.md.erb.tt +5 -0
- data/lib/generators/erb/templates/schema.json.tt +10 -0
- data/lib/generators/test_unit/agent_generator.rb +1 -1
- data/lib/generators/test_unit/templates/functional_test.rb.tt +4 -2
- metadata +320 -65
- data/lib/active_agent/action_prompt/action.rb +0 -13
- data/lib/active_agent/action_prompt/base.rb +0 -623
- data/lib/active_agent/action_prompt/message.rb +0 -126
- data/lib/active_agent/action_prompt/prompt.rb +0 -136
- data/lib/active_agent/action_prompt.rb +0 -19
- data/lib/active_agent/callbacks.rb +0 -33
- data/lib/active_agent/generation_provider/anthropic_provider.rb +0 -163
- data/lib/active_agent/generation_provider/base.rb +0 -55
- data/lib/active_agent/generation_provider/base_adapter.rb +0 -19
- data/lib/active_agent/generation_provider/error_handling.rb +0 -167
- data/lib/active_agent/generation_provider/log_subscriber.rb +0 -92
- data/lib/active_agent/generation_provider/message_formatting.rb +0 -107
- data/lib/active_agent/generation_provider/ollama_provider.rb +0 -66
- data/lib/active_agent/generation_provider/open_ai_provider.rb +0 -279
- data/lib/active_agent/generation_provider/open_router_provider.rb +0 -385
- data/lib/active_agent/generation_provider/parameter_builder.rb +0 -119
- data/lib/active_agent/generation_provider/response.rb +0 -75
- data/lib/active_agent/generation_provider/responses_adapter.rb +0 -44
- data/lib/active_agent/generation_provider/stream_processing.rb +0 -58
- data/lib/active_agent/generation_provider/tool_management.rb +0 -142
- data/lib/active_agent/generation_provider.rb +0 -67
- data/lib/active_agent/log_subscriber.rb +0 -44
- data/lib/active_agent/parameterized.rb +0 -75
- data/lib/active_agent/prompt_helper.rb +0 -19
- data/lib/active_agent/queued_generation.rb +0 -12
- data/lib/active_agent/rescuable.rb +0 -34
- data/lib/active_agent/sanitizers.rb +0 -40
- data/lib/active_agent/streaming.rb +0 -34
- data/lib/active_agent/test_case.rb +0 -125
- data/lib/generators/USAGE +0 -47
- data/lib/generators/active_agent/USAGE +0 -56
- data/lib/generators/erb/install_generator.rb +0 -44
- data/lib/generators/erb/templates/layout.html.erb.tt +0 -1
- data/lib/generators/erb/templates/layout.json.erb.tt +0 -1
- data/lib/generators/erb/templates/layout.text.erb.tt +0 -1
- data/lib/generators/erb/templates/view.html.erb.tt +0 -5
- data/lib/generators/erb/templates/view.json.erb.tt +0 -16
- /data/lib/active_agent/{preview.rb → concerns/preview.rb} +0 -0
- /data/lib/generators/erb/templates/{view.text.erb.tt → message.text.erb.tt} +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require_relative "_base_provider"
|
|
2
|
+
|
|
3
|
+
require_gem!(:openai, __FILE__)
|
|
4
|
+
|
|
5
|
+
require_relative "open_ai/_base"
|
|
6
|
+
require_relative "open_ai/chat_provider"
|
|
7
|
+
require_relative "open_ai/responses_provider"
|
|
8
|
+
require_relative "open_ai/embedding/_types"
|
|
9
|
+
|
|
10
|
+
module ActiveAgent
|
|
11
|
+
module Providers
|
|
12
|
+
# Router for OpenAI's API versions based on supported features.
|
|
13
|
+
#
|
|
14
|
+
# This provider acts as a thin wrapper that routes requests between different versions
|
|
15
|
+
# of OpenAI's API (Chat API and Responses API) depending on the features used in the prompt.
|
|
16
|
+
# It automatically selects the appropriate API version based on:
|
|
17
|
+
# - Explicit API version specification (+:api_version+ option)
|
|
18
|
+
# - Presence of audio content in the request
|
|
19
|
+
#
|
|
20
|
+
# @example Basic usage
|
|
21
|
+
# provider = ActiveAgent::Providers::OpenAIProvider.new(...)
|
|
22
|
+
# result = provider.generate
|
|
23
|
+
#
|
|
24
|
+
# @see https://platform.openai.com/docs/guides/migrate-to-responses
|
|
25
|
+
class OpenAIProvider < OpenAI::Base
|
|
26
|
+
# Returns the embedding request type for OpenAI.
|
|
27
|
+
#
|
|
28
|
+
# @return [ActiveModel::Type::Value] The OpenAI embedding request type
|
|
29
|
+
def self.embed_request_type
|
|
30
|
+
OpenAI::Embedding::RequestType.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_internal :api_version
|
|
34
|
+
attr_internal :raw_options
|
|
35
|
+
|
|
36
|
+
# Initializes the OpenAI provider router.
|
|
37
|
+
#
|
|
38
|
+
# Since this layer is just routing based on API version, we want to wait
|
|
39
|
+
# to cast values into their types.
|
|
40
|
+
#
|
|
41
|
+
# @param kwargs [Hash] Configuration options for the provider
|
|
42
|
+
# @option kwargs [Symbol] :service The service name to validate
|
|
43
|
+
# @option kwargs [Symbol] :api_version The OpenAI API version to use (:chat or :responses)
|
|
44
|
+
def initialize(kwargs = {})
|
|
45
|
+
# For Routing Prompt APIs
|
|
46
|
+
self.api_version = kwargs.delete(:api_version)
|
|
47
|
+
self.raw_options = kwargs.deep_dup
|
|
48
|
+
|
|
49
|
+
super
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Generates a response by routing to the appropriate OpenAI API version.
|
|
53
|
+
#
|
|
54
|
+
# This method determines which API version to use based on the prompt context:
|
|
55
|
+
# - Uses Chat API if +api_version: :chat+ is specified or audio is present
|
|
56
|
+
# - Uses Responses API otherwise (default)
|
|
57
|
+
#
|
|
58
|
+
# @return [Object] The generation result from the selected API provider
|
|
59
|
+
#
|
|
60
|
+
# @see https://platform.openai.com/docs/guides/migrate-to-responses
|
|
61
|
+
def prompt
|
|
62
|
+
if api_version == :chat || context[:audio].present?
|
|
63
|
+
instrument("api_routing.provider.active_agent", api_type: :chat, api_version: api_version, has_audio: context[:audio].present?)
|
|
64
|
+
OpenAI::ChatProvider.new(raw_options).prompt
|
|
65
|
+
else # api_version == :responses || true
|
|
66
|
+
instrument("api_routing.provider.active_agent", api_type: :responses, api_version: api_version)
|
|
67
|
+
OpenAI::ResponsesProvider.new(raw_options).prompt
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Generates a preview by routing to the appropriate OpenAI API version.
|
|
72
|
+
#
|
|
73
|
+
# Routes to Chat API or Responses API using the same logic as {#prompt}.
|
|
74
|
+
#
|
|
75
|
+
# @return [String] markdown-formatted preview
|
|
76
|
+
# @see #prompt
|
|
77
|
+
def preview
|
|
78
|
+
if api_version == :chat || context[:audio].present?
|
|
79
|
+
OpenAI::ChatProvider.new(raw_options).preview
|
|
80
|
+
else # api_version == :responses || true
|
|
81
|
+
OpenAI::ResponsesProvider.new(raw_options).preview
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
protected
|
|
86
|
+
|
|
87
|
+
# Executes an embedding request via OpenAI's API.
|
|
88
|
+
#
|
|
89
|
+
# @param parameters [Hash] The embedding request parameters
|
|
90
|
+
# @return [Object] The embedding response from OpenAI
|
|
91
|
+
def api_embed_execute(parameters)
|
|
92
|
+
instrument("embeddings_request.provider.active_agent")
|
|
93
|
+
client.embeddings.create(**parameters).as_json.deep_symbolize_keys
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "requests/_types"
|
|
4
|
+
|
|
5
|
+
require_relative "request"
|
|
6
|
+
require_relative "options"
|
|
7
|
+
|
|
8
|
+
module ActiveAgent
|
|
9
|
+
module Providers
|
|
10
|
+
module OpenRouter
|
|
11
|
+
# Type for Request model
|
|
12
|
+
class RequestType < ActiveModel::Type::Value
|
|
13
|
+
def cast(value)
|
|
14
|
+
case value
|
|
15
|
+
when Request
|
|
16
|
+
value
|
|
17
|
+
when Hash
|
|
18
|
+
Request.new(**value.deep_symbolize_keys)
|
|
19
|
+
when nil
|
|
20
|
+
nil
|
|
21
|
+
else
|
|
22
|
+
raise ArgumentError, "Cannot cast #{value.class} to Request"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def serialize(value)
|
|
27
|
+
case value
|
|
28
|
+
when Request
|
|
29
|
+
value.serialize
|
|
30
|
+
when Hash
|
|
31
|
+
value
|
|
32
|
+
when nil
|
|
33
|
+
nil
|
|
34
|
+
else
|
|
35
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def deserialize(value)
|
|
40
|
+
cast(value)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../open_ai/options"
|
|
4
|
+
require_relative "requests/response_format"
|
|
5
|
+
require_relative "requests/prediction"
|
|
6
|
+
require_relative "requests/provider_preferences"
|
|
7
|
+
|
|
8
|
+
module ActiveAgent
|
|
9
|
+
module Providers
|
|
10
|
+
module OpenRouter
|
|
11
|
+
class Options < ActiveAgent::Providers::OpenAI::Options
|
|
12
|
+
attribute :base_url, :string, as: "https://openrouter.ai/api/v1"
|
|
13
|
+
attribute :app_name, :string, fallback: "ActiveAgent"
|
|
14
|
+
attribute :site_url, :string, fallback: "https://activeagents.ai/"
|
|
15
|
+
|
|
16
|
+
def initialize(kwargs = {})
|
|
17
|
+
kwargs = kwargs.deep_symbolize_keys if kwargs.respond_to?(:deep_symbolize_keys)
|
|
18
|
+
|
|
19
|
+
super(**deep_compact(kwargs.merge(
|
|
20
|
+
app_name: kwargs[:app_name] || resolve_app_name(kwargs),
|
|
21
|
+
site_url: kwargs[:site_url] || resolve_site_url(kwargs),
|
|
22
|
+
)))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def serialize
|
|
26
|
+
super.except(:app_name, :site_url)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# We fallback to ActiveAgent but allow empty strings to unset
|
|
30
|
+
def extra_headers
|
|
31
|
+
deep_compact(
|
|
32
|
+
"http-referer" => site_url.presence,
|
|
33
|
+
"x-title" => app_name.presence
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def resolve_api_key(kwargs)
|
|
40
|
+
kwargs["api_key"] ||
|
|
41
|
+
ENV["OPENROUTER_API_KEY"] ||
|
|
42
|
+
ENV["OPEN_ROUTER_API_KEY"] ||
|
|
43
|
+
ENV["OPENROUTER_ACCESS_TOKEN"] ||
|
|
44
|
+
ENV["OPEN_ROUTER_ACCESS_TOKEN"]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Not Used as Part of Open Router
|
|
48
|
+
def resolve_organization_id(kwargs) = nil
|
|
49
|
+
def resolve_project_id(kwargs) = nil
|
|
50
|
+
|
|
51
|
+
def resolve_app_name(kwargs)
|
|
52
|
+
if defined?(Rails) && Rails.application
|
|
53
|
+
Rails.application.class.name.split("::").first
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resolve_site_url(kwargs)
|
|
58
|
+
# First check ActiveAgent kwargs
|
|
59
|
+
return kwargs[:default_url_options][:host] if kwargs.dig(:default_url_options, :host)
|
|
60
|
+
|
|
61
|
+
# Then check Rails routes default_url_options
|
|
62
|
+
if defined?(Rails) && Rails.application&.routes&.default_url_options&.any?
|
|
63
|
+
host = Rails.application.routes.default_url_options[:host]
|
|
64
|
+
port = Rails.application.routes.default_url_options[:port]
|
|
65
|
+
protocol = Rails.application.routes.default_url_options[:protocol] || "https"
|
|
66
|
+
|
|
67
|
+
if host
|
|
68
|
+
url = "#{protocol}://#{host}"
|
|
69
|
+
url += ":#{port}" if port && port != 80 && port != 443
|
|
70
|
+
return url
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Finally check ActionMailer options as fallback
|
|
75
|
+
if defined?(Rails) && Rails.application&.config&.action_mailer&.default_url_options
|
|
76
|
+
options = Rails.application.config.action_mailer.default_url_options
|
|
77
|
+
host = options[:host]
|
|
78
|
+
port = options[:port]
|
|
79
|
+
protocol = options[:protocol] || "https"
|
|
80
|
+
|
|
81
|
+
if host
|
|
82
|
+
url = "#{protocol}://#{host}"
|
|
83
|
+
url += ":#{port}" if port && port != 80 && port != 443
|
|
84
|
+
return url
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
nil
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../open_ai/_types"
|
|
4
|
+
require_relative "requests/_types"
|
|
5
|
+
|
|
6
|
+
module ActiveAgent
|
|
7
|
+
module Providers
|
|
8
|
+
module OpenRouter
|
|
9
|
+
class Request < OpenAI::Chat::Request
|
|
10
|
+
# Prompting Options
|
|
11
|
+
attribute :model, :string, fallback: "openrouter/auto"
|
|
12
|
+
attribute :response_format, Requests::ResponseFormatType.new
|
|
13
|
+
attribute :max_tokens, :integer
|
|
14
|
+
attribute :stop # Can be string or array
|
|
15
|
+
|
|
16
|
+
# Messages array (required)
|
|
17
|
+
attribute :messages, Requests::Messages::MessagesType.new
|
|
18
|
+
|
|
19
|
+
# LLM Parameters
|
|
20
|
+
attribute :seed, :integer
|
|
21
|
+
attribute :top_p, :float
|
|
22
|
+
attribute :top_k, :integer
|
|
23
|
+
attribute :frequency_penalty, :float
|
|
24
|
+
attribute :presence_penalty, :float
|
|
25
|
+
attribute :repetition_penalty, :float
|
|
26
|
+
attribute :top_logprobs, :integer
|
|
27
|
+
attribute :min_p, :float
|
|
28
|
+
attribute :top_a, :float
|
|
29
|
+
attribute :logit_bias # Hash of token_id => bias value
|
|
30
|
+
|
|
31
|
+
# Tool calling (inherited from OpenAI but explicitly documented)
|
|
32
|
+
# attribute :tools, :json
|
|
33
|
+
# attribute :tool_choice, :json
|
|
34
|
+
|
|
35
|
+
# Predicted outputs
|
|
36
|
+
attribute :prediction, Requests::PredictionType.new
|
|
37
|
+
|
|
38
|
+
# OpenRouter-specific parameters
|
|
39
|
+
attribute :transforms, default: -> { [] } # Array of strings
|
|
40
|
+
attribute :models, default: -> { [] } # Array of model strings for fallback
|
|
41
|
+
attribute :route, :string, default: "fallback"
|
|
42
|
+
attribute :provider, Requests::ProviderPreferencesType.new, default: {}
|
|
43
|
+
attribute :user, :string # Stable identifier for end-users
|
|
44
|
+
attribute :plugins, Requests::PluginsType.new # Array of plugin configurations (e.g., file-parser for PDFs)
|
|
45
|
+
|
|
46
|
+
# Validations for parameters with specific ranges
|
|
47
|
+
validates :max_tokens, numericality: { greater_than_or_equal_to: 1 }, allow_nil: true
|
|
48
|
+
validates :top_p, numericality: { greater_than: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
49
|
+
validates :top_k, numericality: { greater_than_or_equal_to: 1 }, allow_nil: true
|
|
50
|
+
validates :frequency_penalty, numericality: { greater_than_or_equal_to: -2, less_than_or_equal_to: 2 }, allow_nil: true
|
|
51
|
+
validates :presence_penalty, numericality: { greater_than_or_equal_to: -2, less_than_or_equal_to: 2 }, allow_nil: true
|
|
52
|
+
validates :repetition_penalty, numericality: { greater_than: 0, less_than_or_equal_to: 2 }, allow_nil: true
|
|
53
|
+
validates :min_p, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
54
|
+
validates :top_a, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
55
|
+
validates :route, inclusion: { in: [ "fallback" ] }, allow_nil: true
|
|
56
|
+
|
|
57
|
+
# Backwards Compatibility
|
|
58
|
+
delegate_attributes :data_collection, :enable_fallbacks, :sort, :ignore, :only, :quantizations, :max_price, to: :provider
|
|
59
|
+
alias_attribute :fallback_models, :models
|
|
60
|
+
|
|
61
|
+
# Common Format Compatability
|
|
62
|
+
def messages=(value)
|
|
63
|
+
case value
|
|
64
|
+
when Array
|
|
65
|
+
super((messages || []) | value)
|
|
66
|
+
else
|
|
67
|
+
super((messages || []) | [ value ])
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Common Format Compatability
|
|
72
|
+
def response_format=(value)
|
|
73
|
+
# If we are doing structured output, we need to ensure that we route to models that support it.
|
|
74
|
+
if %i[json_object json_schema].include?(value[:type].to_sym)
|
|
75
|
+
self.provider.require_parameters = true
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
super(value)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "messages/_types"
|
|
4
|
+
require_relative "provider_preferences/_types"
|
|
5
|
+
require_relative "plugins/_types"
|
|
6
|
+
|
|
7
|
+
require_relative "message"
|
|
8
|
+
require_relative "prediction"
|
|
9
|
+
require_relative "provider_preferences"
|
|
10
|
+
require_relative "response_format"
|
|
11
|
+
require_relative "plugin"
|
|
12
|
+
|
|
13
|
+
module ActiveAgent
|
|
14
|
+
module Providers
|
|
15
|
+
module OpenRouter
|
|
16
|
+
module Requests
|
|
17
|
+
# Type for MaxPrice
|
|
18
|
+
class MaxPriceType < ActiveModel::Type::Value
|
|
19
|
+
def cast(value)
|
|
20
|
+
case value
|
|
21
|
+
when MaxPrice
|
|
22
|
+
value
|
|
23
|
+
when Hash
|
|
24
|
+
MaxPrice.new(**value.deep_symbolize_keys)
|
|
25
|
+
when nil
|
|
26
|
+
nil
|
|
27
|
+
else
|
|
28
|
+
raise ArgumentError, "Cannot cast #{value.class} to MaxPrice"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def serialize(value)
|
|
33
|
+
case value
|
|
34
|
+
when MaxPrice
|
|
35
|
+
value.serialize
|
|
36
|
+
when Hash
|
|
37
|
+
value
|
|
38
|
+
when nil
|
|
39
|
+
nil
|
|
40
|
+
else
|
|
41
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def deserialize(value)
|
|
46
|
+
cast(value)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Type for Prediction
|
|
51
|
+
class PredictionType < ActiveModel::Type::Value
|
|
52
|
+
def cast(value)
|
|
53
|
+
case value
|
|
54
|
+
when Prediction
|
|
55
|
+
value
|
|
56
|
+
when Hash
|
|
57
|
+
Prediction.new(**value.deep_symbolize_keys)
|
|
58
|
+
when nil
|
|
59
|
+
nil
|
|
60
|
+
else
|
|
61
|
+
raise ArgumentError, "Cannot cast #{value.class} to Prediction"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def serialize(value)
|
|
66
|
+
case value
|
|
67
|
+
when Prediction
|
|
68
|
+
value.serialize
|
|
69
|
+
when Hash
|
|
70
|
+
value
|
|
71
|
+
when nil
|
|
72
|
+
nil
|
|
73
|
+
else
|
|
74
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def deserialize(value)
|
|
79
|
+
cast(value)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Type for ProviderPreferences
|
|
84
|
+
class ProviderPreferencesType < ActiveModel::Type::Value
|
|
85
|
+
def cast(value)
|
|
86
|
+
case value
|
|
87
|
+
when ProviderPreferences
|
|
88
|
+
value
|
|
89
|
+
when Hash
|
|
90
|
+
ProviderPreferences.new(**value.deep_symbolize_keys)
|
|
91
|
+
when nil
|
|
92
|
+
nil
|
|
93
|
+
else
|
|
94
|
+
raise ArgumentError, "Cannot cast #{value.class} to ProviderPreferences"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def serialize(value)
|
|
99
|
+
case value
|
|
100
|
+
when ProviderPreferences
|
|
101
|
+
value.serialize
|
|
102
|
+
when Hash
|
|
103
|
+
value
|
|
104
|
+
when nil
|
|
105
|
+
nil
|
|
106
|
+
else
|
|
107
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def deserialize(value)
|
|
112
|
+
cast(value)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Type for ResponseFormat
|
|
117
|
+
class ResponseFormatType < ActiveModel::Type::Value
|
|
118
|
+
def cast(value)
|
|
119
|
+
case value
|
|
120
|
+
when ResponseFormat
|
|
121
|
+
value
|
|
122
|
+
when Hash
|
|
123
|
+
ResponseFormat.new(**value.deep_symbolize_keys)
|
|
124
|
+
when nil
|
|
125
|
+
nil
|
|
126
|
+
else
|
|
127
|
+
raise ArgumentError, "Cannot cast #{value.class} to ResponseFormat"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def serialize(value)
|
|
132
|
+
case value
|
|
133
|
+
when ResponseFormat
|
|
134
|
+
value.serialize
|
|
135
|
+
when Hash
|
|
136
|
+
value
|
|
137
|
+
when nil
|
|
138
|
+
nil
|
|
139
|
+
else
|
|
140
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def deserialize(value)
|
|
145
|
+
cast(value)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Type for Plugins (array of Plugin objects)
|
|
150
|
+
class PluginsType < ActiveModel::Type::Value
|
|
151
|
+
def cast(value)
|
|
152
|
+
case value
|
|
153
|
+
when Array
|
|
154
|
+
value.map do |item|
|
|
155
|
+
case item
|
|
156
|
+
when Plugin
|
|
157
|
+
item
|
|
158
|
+
when Hash
|
|
159
|
+
Plugin.new(**item.deep_symbolize_keys)
|
|
160
|
+
else
|
|
161
|
+
raise ArgumentError, "Cannot cast #{item.class} to Plugin"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
when nil
|
|
165
|
+
nil
|
|
166
|
+
else
|
|
167
|
+
raise ArgumentError, "Cannot cast #{value.class} to Array of Plugins"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def serialize(value)
|
|
172
|
+
case value
|
|
173
|
+
when Array
|
|
174
|
+
value.map do |item|
|
|
175
|
+
case item
|
|
176
|
+
when Plugin
|
|
177
|
+
item.serialize
|
|
178
|
+
when Hash
|
|
179
|
+
item
|
|
180
|
+
else
|
|
181
|
+
raise ArgumentError, "Cannot serialize #{item.class}"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
when nil
|
|
185
|
+
nil
|
|
186
|
+
else
|
|
187
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def deserialize(value)
|
|
192
|
+
cast(value)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative "messages/assistant"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/open_ai/chat/requests/messages/_types"
|
|
4
|
+
|
|
5
|
+
require_relative "assistant"
|
|
6
|
+
require_relative "user"
|
|
7
|
+
|
|
8
|
+
module ActiveAgent
|
|
9
|
+
module Providers
|
|
10
|
+
module OpenRouter
|
|
11
|
+
module Requests
|
|
12
|
+
module Messages
|
|
13
|
+
# Type for Messages array - uses OpenRouter's MessageType
|
|
14
|
+
class MessagesType < OpenAI::Chat::Requests::Messages::MessagesType
|
|
15
|
+
def initialize
|
|
16
|
+
super
|
|
17
|
+
@message_type = MessageType.new
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class MessageType < OpenAI::Chat::Requests::Messages::MessageType
|
|
22
|
+
def cast(value)
|
|
23
|
+
case value
|
|
24
|
+
when OpenAI::Chat::Requests::Messages::Base
|
|
25
|
+
value
|
|
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
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module OpenRouter
|
|
6
|
+
module Requests
|
|
7
|
+
module Messages
|
|
8
|
+
# Assistant message for OpenRouter API.
|
|
9
|
+
#
|
|
10
|
+
# Extends OpenAI's assistant message with OpenRouter-specific behavior.
|
|
11
|
+
# Drops reasoning fields during message reconstruction since they're not
|
|
12
|
+
# part of the standard request format.
|
|
13
|
+
class Assistant < OpenAI::Chat::Requests::Messages::Assistant
|
|
14
|
+
drop_attributes :reasoning, :reasoning_details
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/open_ai/chat/requests/messages/content/_types"
|
|
4
|
+
require_relative "file"
|
|
5
|
+
|
|
6
|
+
module ActiveAgent
|
|
7
|
+
module Providers
|
|
8
|
+
module OpenRouter
|
|
9
|
+
module Requests
|
|
10
|
+
module Messages
|
|
11
|
+
module Content
|
|
12
|
+
# Type for handling content (string or array of content parts) in OpenRouter.
|
|
13
|
+
#
|
|
14
|
+
# Extends OpenAI's ContentsType to use OpenRouter's ContentType which
|
|
15
|
+
# handles files differently (preserves data URI prefix).
|
|
16
|
+
class ContentsType < OpenAI::Chat::Requests::Messages::Content::ContentsType
|
|
17
|
+
def initialize
|
|
18
|
+
super
|
|
19
|
+
@content_type = ContentType.new
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Type for individual content items in OpenRouter.
|
|
24
|
+
#
|
|
25
|
+
# Uses OpenRouter's File class for file content which preserves
|
|
26
|
+
# the data URI prefix that OpenAI strips.
|
|
27
|
+
class ContentType < ActiveModel::Type::Value
|
|
28
|
+
def cast(value)
|
|
29
|
+
case value
|
|
30
|
+
when OpenAI::Chat::Requests::Messages::Content::Base
|
|
31
|
+
value
|
|
32
|
+
when Hash
|
|
33
|
+
hash = value.deep_symbolize_keys
|
|
34
|
+
type = hash[:type]&.to_sym
|
|
35
|
+
|
|
36
|
+
case type
|
|
37
|
+
when :text
|
|
38
|
+
OpenAI::Chat::Requests::Messages::Content::Text.new(**hash)
|
|
39
|
+
when :image_url
|
|
40
|
+
OpenAI::Chat::Requests::Messages::Content::Image.new(**hash)
|
|
41
|
+
when :input_audio
|
|
42
|
+
OpenAI::Chat::Requests::Messages::Content::Audio.new(**hash)
|
|
43
|
+
when :file
|
|
44
|
+
# Use OpenRouter's File class instead of OpenAI's
|
|
45
|
+
File.new(**hash)
|
|
46
|
+
when :refusal
|
|
47
|
+
OpenAI::Chat::Requests::Messages::Content::Refusal.new(**hash)
|
|
48
|
+
when nil
|
|
49
|
+
# When type is nil, check for specific content keys to infer type
|
|
50
|
+
if hash.key?(:text)
|
|
51
|
+
OpenAI::Chat::Requests::Messages::Content::Text.new(**hash)
|
|
52
|
+
elsif hash.key?(:image)
|
|
53
|
+
OpenAI::Chat::Requests::Messages::Content::Image.new(**hash.merge(image_url: hash.delete(:image)))
|
|
54
|
+
elsif hash.key?(:document)
|
|
55
|
+
# Use OpenRouter's File class for document content
|
|
56
|
+
File.new(**hash.merge(file: hash.delete(:document)))
|
|
57
|
+
else
|
|
58
|
+
raise ArgumentError, "Cannot determine content type from hash keys: #{hash.keys.inspect}"
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
raise ArgumentError, "Unknown content type: #{type.inspect}"
|
|
62
|
+
end
|
|
63
|
+
when String
|
|
64
|
+
# Plain text string becomes text content
|
|
65
|
+
OpenAI::Chat::Requests::Messages::Content::Text.new(text: value)
|
|
66
|
+
when nil
|
|
67
|
+
nil
|
|
68
|
+
else
|
|
69
|
+
raise ArgumentError, "Cannot cast #{value.class} to Content (expected Base, Hash, String, or nil)"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def serialize(value)
|
|
74
|
+
case value
|
|
75
|
+
when OpenAI::Chat::Requests::Messages::Content::Base
|
|
76
|
+
value.serialize
|
|
77
|
+
when Hash
|
|
78
|
+
value
|
|
79
|
+
when String
|
|
80
|
+
{ type: "text", text: value }
|
|
81
|
+
when nil
|
|
82
|
+
nil
|
|
83
|
+
else
|
|
84
|
+
raise ArgumentError, "Cannot serialize #{value.class} as Content"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def deserialize(value)
|
|
89
|
+
cast(value)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|