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,410 @@
|
|
|
1
|
+
require "active_support/delegation"
|
|
2
|
+
|
|
3
|
+
require_relative "common/response"
|
|
4
|
+
require_relative "concerns/exception_handler"
|
|
5
|
+
require_relative "concerns/previewable"
|
|
6
|
+
|
|
7
|
+
# Maps provider types to their gem dependencies.
|
|
8
|
+
# @private
|
|
9
|
+
GEM_LOADERS = {
|
|
10
|
+
anthropic: [ "anthropic", "~> 1.12", "anthropic" ],
|
|
11
|
+
openai: [ "openai", "~> 0.34", "openai" ]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# Loads and requires a provider's gem dependency.
|
|
15
|
+
#
|
|
16
|
+
# @param type [Symbol] provider type (:anthropic, :openai)
|
|
17
|
+
# @param file_name [String] provider file path for error context
|
|
18
|
+
# @return [void]
|
|
19
|
+
# @raise [LoadError] when the required gem is not available
|
|
20
|
+
def require_gem!(type, file_name)
|
|
21
|
+
gem_name, requirement, package_name = GEM_LOADERS.fetch(type)
|
|
22
|
+
provider_name = file_name.split("/").last.delete_suffix(".rb").camelize
|
|
23
|
+
|
|
24
|
+
begin
|
|
25
|
+
gem(gem_name, requirement)
|
|
26
|
+
require(package_name)
|
|
27
|
+
rescue LoadError
|
|
28
|
+
raise LoadError, "The '#{gem_name}' gem is required for #{provider_name}. Please add it to your Gemfile and run `bundle install`."
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module ActiveAgent
|
|
33
|
+
module Providers
|
|
34
|
+
# Base class for LLM provider integrations.
|
|
35
|
+
#
|
|
36
|
+
# Orchestrates API requests, streaming responses, and multi-turn tool calling.
|
|
37
|
+
# Each provider (OpenAI, Anthropic, etc.) subclasses this to implement
|
|
38
|
+
# provider-specific API interactions.
|
|
39
|
+
#
|
|
40
|
+
# @abstract Subclasses must implement {#api_prompt_execute},
|
|
41
|
+
# {#process_stream_chunk}, {#process_prompt_finished_extract_messages},
|
|
42
|
+
# and {#process_prompt_finished_extract_function_calls}
|
|
43
|
+
class BaseProvider
|
|
44
|
+
extend ActiveSupport::Delegation
|
|
45
|
+
|
|
46
|
+
include ExceptionHandler
|
|
47
|
+
include Previewable
|
|
48
|
+
|
|
49
|
+
class ProvidersError < StandardError; end
|
|
50
|
+
|
|
51
|
+
attr_internal :options, :context, :trace_id, # Setup
|
|
52
|
+
:request, :message_stack, # Runtime
|
|
53
|
+
:stream_broadcaster, :streaming, # Callback (Streams)
|
|
54
|
+
:tools_function # Callback (Tools)
|
|
55
|
+
|
|
56
|
+
# @return [String] provider name extracted from class name (e.g., "Anthropic", "OpenAI")
|
|
57
|
+
def self.service_name
|
|
58
|
+
name.split("::").last.delete_suffix("Provider")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [String] module-qualified provider name (e.g., "Anthropic", "OpenAI::Chat")
|
|
62
|
+
def self.tag_name
|
|
63
|
+
name.delete_prefix("ActiveAgent::Providers::").delete_suffix("Provider")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [Module] provider's namespace module (e.g., ActiveAgent::Providers::OpenAI)
|
|
67
|
+
def self.namespace
|
|
68
|
+
"#{name.deconstantize}::#{service_name}".safe_constantize
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [Class] provider's options class
|
|
72
|
+
def self.options_klass
|
|
73
|
+
namespace::Options
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [ActiveModel::Type::Value] provider-specific request type for prompt casting/serialization
|
|
77
|
+
def self.prompt_request_type
|
|
78
|
+
namespace::RequestType.new
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [ActiveModel::Type::Value] provider-specific request type for embedding casting/serialization
|
|
82
|
+
# @raise [NotImplementedError] when provider doesn't support embeddings
|
|
83
|
+
def self.embed_request_type
|
|
84
|
+
fail(NotImplementedError)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
delegate :service_name, :tag_name, :namespace, :options_klass, :prompt_request_type, :embed_request_type, to: :class
|
|
88
|
+
|
|
89
|
+
# Initializes a provider instance.
|
|
90
|
+
#
|
|
91
|
+
# @param kwargs [Hash] configuration and callbacks
|
|
92
|
+
# @option kwargs [Symbol] :service validates against provider's service name
|
|
93
|
+
# @option kwargs [Proc] :stream_broadcaster callback for streaming events (:open, :update, :close)
|
|
94
|
+
# @option kwargs [Proc] :tools_function callback to execute tool/function calls
|
|
95
|
+
# @raise [RuntimeError] when service name doesn't match provider
|
|
96
|
+
def initialize(kwargs = {})
|
|
97
|
+
assert_service!(kwargs.delete(:service))
|
|
98
|
+
|
|
99
|
+
configure_exception_handler(
|
|
100
|
+
exception_handler: kwargs.delete(:exception_handler)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
self.trace_id = kwargs[:trace_id]
|
|
104
|
+
self.stream_broadcaster = kwargs.delete(:stream_broadcaster)
|
|
105
|
+
self.streaming = false
|
|
106
|
+
self.tools_function = kwargs.delete(:tools_function)
|
|
107
|
+
self.options = options_klass.new(kwargs.extract!(*options_klass.keys))
|
|
108
|
+
self.context = kwargs
|
|
109
|
+
self.message_stack = []
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Executes a prompt request with error handling and instrumentation.
|
|
113
|
+
#
|
|
114
|
+
# @return [ActiveAgent::Providers::Common::PromptResponse]
|
|
115
|
+
def prompt
|
|
116
|
+
instrument("prompt_start.provider.active_agent") do
|
|
117
|
+
self.request = prompt_request_type.cast(context.except(:trace_id))
|
|
118
|
+
resolve_prompt
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Generates a preview of the prompt without executing the API call.
|
|
123
|
+
#
|
|
124
|
+
# Casts context into a request object and renders it as markdown for inspection.
|
|
125
|
+
#
|
|
126
|
+
# @return [String] markdown-formatted preview
|
|
127
|
+
def preview
|
|
128
|
+
self.request = prompt_request_type.cast(context.except(:trace_id))
|
|
129
|
+
preview_prompt
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Executes an embedding request with error handling and instrumentation.
|
|
133
|
+
#
|
|
134
|
+
# Converts text into vector representations for semantic search and similarity operations.
|
|
135
|
+
#
|
|
136
|
+
# @return [ActiveAgent::Providers::Common::EmbedResponse]
|
|
137
|
+
def embed
|
|
138
|
+
instrument("embed_start.provider.active_agent") do
|
|
139
|
+
self.request = embed_request_type.cast(context.except(:trace_id))
|
|
140
|
+
resolve_embed
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
protected
|
|
145
|
+
|
|
146
|
+
# @param name [String, nil]
|
|
147
|
+
# @raise [RuntimeError] when service name doesn't match provider
|
|
148
|
+
def assert_service!(name)
|
|
149
|
+
fail "Unexpected Service Name: #{name} != #{service_name}" if name && name != service_name
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Instruments an event for logging and metrics.
|
|
153
|
+
#
|
|
154
|
+
# @param name [String]
|
|
155
|
+
# @param payload [Hash]
|
|
156
|
+
# @yield block to instrument
|
|
157
|
+
# @return [Object] block result
|
|
158
|
+
def instrument(name, payload = {}, &block)
|
|
159
|
+
full_payload = { provider: service_name, provider_module: tag_name, trace_id: }.merge(payload)
|
|
160
|
+
ActiveSupport::Notifications.instrument(name, full_payload, &block)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Orchestrates the complete prompt request lifecycle.
|
|
164
|
+
#
|
|
165
|
+
# Prepares request, executes API call, processes response, and handles
|
|
166
|
+
# recursive tool/function calling until completion.
|
|
167
|
+
#
|
|
168
|
+
# @return [ActiveAgent::Providers::Common::PromptResponse]
|
|
169
|
+
def resolve_prompt
|
|
170
|
+
request = prepare_prompt_request
|
|
171
|
+
|
|
172
|
+
instrument("request_prepared.provider.active_agent", message_count: request.messages.size)
|
|
173
|
+
|
|
174
|
+
# @todo Validate Request
|
|
175
|
+
api_parameters = api_request_build(request, prompt_request_type)
|
|
176
|
+
api_response = instrument("api_call.provider.active_agent", streaming: api_parameters[:stream].present?) do
|
|
177
|
+
with_exception_handling { api_prompt_execute(api_parameters) }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
process_prompt_finished(api_response.as_json&.deep_symbolize_keys)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Orchestrates the complete embedding request lifecycle.
|
|
184
|
+
#
|
|
185
|
+
# @return [ActiveAgent::Providers::Common::EmbedResponse]
|
|
186
|
+
def resolve_embed
|
|
187
|
+
# @todo Validate Request
|
|
188
|
+
api_parameters = api_request_build(request, embed_request_type)
|
|
189
|
+
api_response = instrument("embed_call.provider.active_agent") do
|
|
190
|
+
with_exception_handling { api_embed_execute(api_parameters) }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
process_embed_finished(api_response)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Prepares request for next iteration in multi-turn conversation.
|
|
197
|
+
#
|
|
198
|
+
# Appends accumulated messages from message stack and resets buffer for next cycle.
|
|
199
|
+
#
|
|
200
|
+
# @return [Request]
|
|
201
|
+
def prepare_prompt_request
|
|
202
|
+
self.request.messages = [ *request.messages, *message_stack ]
|
|
203
|
+
self.message_stack = []
|
|
204
|
+
|
|
205
|
+
self.request
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Builds API request parameters from request object.
|
|
209
|
+
#
|
|
210
|
+
# @param request [Request]
|
|
211
|
+
# @param request_type [ActiveModel::Type::Value] type for serialization
|
|
212
|
+
# @return [Hash]
|
|
213
|
+
def api_request_build(request, request_type)
|
|
214
|
+
parameters = request_type.serialize(request)
|
|
215
|
+
parameters[:stream] = process_stream if request.try(:stream)
|
|
216
|
+
|
|
217
|
+
if options.extra_headers.present?
|
|
218
|
+
parameters[:request_options] = { extra_headers: options.extra_headers }.deep_merge(parameters[:request_options] || {})
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
parameters
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# @return [Proc] invoked for each response chunk
|
|
225
|
+
def process_stream
|
|
226
|
+
proc do |api_response_chunk|
|
|
227
|
+
process_stream_chunk(api_response_chunk)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Executes prompt request against provider's API.
|
|
232
|
+
#
|
|
233
|
+
# @abstract
|
|
234
|
+
# @param request_parameters [Hash]
|
|
235
|
+
# @return [Object] provider-specific API response
|
|
236
|
+
# @raise [NotImplementedError]
|
|
237
|
+
def api_prompt_execute(parameters)
|
|
238
|
+
instrument("api_request.provider.active_agent", model: parameters[:model], streaming: !!parameters[:stream])
|
|
239
|
+
|
|
240
|
+
unless parameters[:stream]
|
|
241
|
+
api_prompt_executer.create(**parameters)
|
|
242
|
+
else
|
|
243
|
+
api_prompt_executer.stream(**parameters.except(:stream)).each(¶meters[:stream])
|
|
244
|
+
nil
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Returns provider-specific API executer for prompt requests.
|
|
249
|
+
#
|
|
250
|
+
# Since all currently implemented providers use stainless gems, subclasses
|
|
251
|
+
# only need to override endpoint selection.
|
|
252
|
+
#
|
|
253
|
+
# @abstract
|
|
254
|
+
# @return [Object] provider-specific API client
|
|
255
|
+
# @raise [NotImplementedError]
|
|
256
|
+
def api_prompt_executer
|
|
257
|
+
fail NotImplementedError, "Subclass expected to implement"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Executes embedding request against provider's API.
|
|
261
|
+
#
|
|
262
|
+
# @abstract
|
|
263
|
+
# @param request_parameters [Hash]
|
|
264
|
+
# @return [Object] provider-specific embedding response
|
|
265
|
+
# @raise [NotImplementedError]
|
|
266
|
+
def api_embed_execute(request_parameters)
|
|
267
|
+
fail NotImplementedError, "Subclass expected to implement"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Processes a single streaming response chunk.
|
|
271
|
+
#
|
|
272
|
+
# @abstract
|
|
273
|
+
# @param api_response_chunk [Object] provider-specific chunk format
|
|
274
|
+
# @raise [NotImplementedError]
|
|
275
|
+
def process_stream_chunk(api_response_chunk)
|
|
276
|
+
fail NotImplementedError, "Subclass expected to implement"
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Broadcasts stream open event.
|
|
280
|
+
#
|
|
281
|
+
# Fires once per request cycle even during multi-turn tool calling.
|
|
282
|
+
#
|
|
283
|
+
# @return [void]
|
|
284
|
+
def broadcast_stream_open
|
|
285
|
+
return if streaming
|
|
286
|
+
self.streaming = true
|
|
287
|
+
|
|
288
|
+
instrument("stream_open.provider.active_agent")
|
|
289
|
+
stream_broadcaster.call(nil, nil, :open)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Broadcasts stream update with message content delta.
|
|
293
|
+
#
|
|
294
|
+
# @param message [Hash, Object]
|
|
295
|
+
# @param delta [String, nil] incremental content chunk
|
|
296
|
+
# @return [void]
|
|
297
|
+
def broadcast_stream_update(message, delta = nil)
|
|
298
|
+
stream_broadcaster.call(message, delta, :update)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Broadcasts stream close event.
|
|
302
|
+
#
|
|
303
|
+
# Fires once per request cycle even during multi-turn tool calling.
|
|
304
|
+
#
|
|
305
|
+
# @return [void]
|
|
306
|
+
def broadcast_stream_close
|
|
307
|
+
return unless streaming
|
|
308
|
+
self.streaming = false
|
|
309
|
+
|
|
310
|
+
instrument("stream_close.provider.active_agent")
|
|
311
|
+
stream_broadcaster.call(message_stack.last, nil, :close)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Processes completed API response and handles tool calling recursion.
|
|
315
|
+
#
|
|
316
|
+
# Extracts messages and function calls from the response. If tools were invoked,
|
|
317
|
+
# executes them and recursively continues the prompt until completion.
|
|
318
|
+
#
|
|
319
|
+
# @param api_response [Object, nil] provider-specific response
|
|
320
|
+
# @return [Common::PromptResponse, nil]
|
|
321
|
+
def process_prompt_finished(api_response = nil)
|
|
322
|
+
if (api_messages = process_prompt_finished_extract_messages(api_response))
|
|
323
|
+
instrument("messages_extracted.provider.active_agent", message_count: api_messages.size)
|
|
324
|
+
message_stack.push(*api_messages)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
if (tool_calls = process_prompt_finished_extract_function_calls)&.any?
|
|
328
|
+
instrument("tool_calls_processing.provider.active_agent", tool_count: tool_calls.size)
|
|
329
|
+
process_function_calls(tool_calls)
|
|
330
|
+
|
|
331
|
+
instrument("multi_turn_continue.provider.active_agent")
|
|
332
|
+
resolve_prompt
|
|
333
|
+
else
|
|
334
|
+
|
|
335
|
+
# During a multi iteration process, we will internally open/close the stream
|
|
336
|
+
# with the provider, but this should all look like one big stream to the agents
|
|
337
|
+
# as they continue to work.
|
|
338
|
+
broadcast_stream_close
|
|
339
|
+
|
|
340
|
+
instrument("prompt_complete.provider.active_agent", message_count: message_stack.size)
|
|
341
|
+
|
|
342
|
+
# To convert the messages into common format we first need to merge the current
|
|
343
|
+
# stack and then cast them to the provider type, so we can cast them out to common.
|
|
344
|
+
messages = prompt_request_type.cast(
|
|
345
|
+
messages: [ *request.messages, *message_stack ]
|
|
346
|
+
).messages
|
|
347
|
+
|
|
348
|
+
# This will returned as it closes up the recursive stack
|
|
349
|
+
Common::PromptResponse.new(
|
|
350
|
+
context:,
|
|
351
|
+
raw_request: prompt_request_type.serialize(request),
|
|
352
|
+
raw_response: api_response,
|
|
353
|
+
messages:,
|
|
354
|
+
format: request.response_format
|
|
355
|
+
)
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Extracts messages from API response.
|
|
360
|
+
#
|
|
361
|
+
# @abstract
|
|
362
|
+
# @param api_response [Object]
|
|
363
|
+
# @return [Array<Message>, nil]
|
|
364
|
+
# @raise [NotImplementedError]
|
|
365
|
+
def process_prompt_finished_extract_messages(api_response)
|
|
366
|
+
fail NotImplementedError, "Subclass expected to implement"
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# Extracts tool/function calls from API response.
|
|
370
|
+
#
|
|
371
|
+
# @abstract
|
|
372
|
+
# @return [Array<Hash>, nil]
|
|
373
|
+
# @raise [NotImplementedError]
|
|
374
|
+
def process_prompt_finished_extract_function_calls
|
|
375
|
+
fail NotImplementedError, "Subclass expected to implement"
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# @param api_response [Hash]
|
|
379
|
+
# @return [Common::EmbedResponse]
|
|
380
|
+
def process_embed_finished(api_response)
|
|
381
|
+
Common::EmbedResponse.new(
|
|
382
|
+
context:,
|
|
383
|
+
raw_request: embed_request_type.serialize(request),
|
|
384
|
+
raw_response: api_response,
|
|
385
|
+
data: process_embed_finished_data(api_response)
|
|
386
|
+
)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Extracts embedding vectors from API response.
|
|
390
|
+
#
|
|
391
|
+
# Handles both list and single embedding response formats:
|
|
392
|
+
# - List: `{ "data": [{ "embedding": [...] }] }`
|
|
393
|
+
# - Single: `{ "embedding": [...] }`
|
|
394
|
+
#
|
|
395
|
+
# @param api_response [Hash]
|
|
396
|
+
# @return [Array<Hash>] embedding objects with :index, :object, :embedding keys
|
|
397
|
+
# @raise [RuntimeError] when response format is unexpected
|
|
398
|
+
def process_embed_finished_data(api_response)
|
|
399
|
+
case (type = api_response[:object].to_sym)
|
|
400
|
+
when :list
|
|
401
|
+
api_response[:data]
|
|
402
|
+
when :embedding
|
|
403
|
+
[ { index: 0 }.merge(api_response.slice(:index, :object, :embedding)) ]
|
|
404
|
+
else
|
|
405
|
+
fail "Unexpected Embed Object Type: #{type}"
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "requests/_types"
|
|
4
|
+
|
|
5
|
+
require_relative "options"
|
|
6
|
+
require_relative "request"
|
|
7
|
+
|
|
8
|
+
module ActiveAgent
|
|
9
|
+
module Providers
|
|
10
|
+
module Anthropic
|
|
11
|
+
# ActiveModel type for casting and serializing Anthropic Request objects.
|
|
12
|
+
#
|
|
13
|
+
# Handles conversion between Hash, Request, and serialized formats for API calls.
|
|
14
|
+
class RequestType < ActiveModel::Type::Value
|
|
15
|
+
# Casts input to Request object.
|
|
16
|
+
#
|
|
17
|
+
# @param value [Request, Hash, nil]
|
|
18
|
+
# @return [Request, nil]
|
|
19
|
+
# @raise [ArgumentError] when value cannot be cast to Request
|
|
20
|
+
def cast(value)
|
|
21
|
+
case value
|
|
22
|
+
when Request
|
|
23
|
+
value
|
|
24
|
+
when Hash
|
|
25
|
+
Request.new(**value.deep_symbolize_keys)
|
|
26
|
+
when nil
|
|
27
|
+
nil
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError, "Cannot cast #{value.class} to Request"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Serializes Request to Hash for API submission.
|
|
34
|
+
#
|
|
35
|
+
# Removes `:response_format` key as it's a simulated feature not directly
|
|
36
|
+
# supported by Anthropic's API.
|
|
37
|
+
#
|
|
38
|
+
# @param value [Request, Hash, nil]
|
|
39
|
+
# @return [Hash, nil]
|
|
40
|
+
# @raise [ArgumentError] when value cannot be serialized
|
|
41
|
+
def serialize(value)
|
|
42
|
+
case value
|
|
43
|
+
when Request
|
|
44
|
+
# Response Format is a simulated feature, not directly supported by API
|
|
45
|
+
value.serialize.except(:response_format)
|
|
46
|
+
when Hash
|
|
47
|
+
value
|
|
48
|
+
when nil
|
|
49
|
+
nil
|
|
50
|
+
else
|
|
51
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @param value [Object]
|
|
56
|
+
# @return [Request, nil]
|
|
57
|
+
def deserialize(value)
|
|
58
|
+
cast(value)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module Anthropic
|
|
8
|
+
class Options < Common::BaseModel
|
|
9
|
+
attribute :api_key, :string
|
|
10
|
+
attribute :base_url, :string, default: "https://api.anthropic.com"
|
|
11
|
+
|
|
12
|
+
attribute :anthropic_beta, :string
|
|
13
|
+
|
|
14
|
+
attribute :max_retries, :integer, default: ::Anthropic::Client::DEFAULT_MAX_RETRIES
|
|
15
|
+
attribute :timeout, :float, default: ::Anthropic::Client::DEFAULT_TIMEOUT_IN_SECONDS
|
|
16
|
+
attribute :initial_retry_delay, :float, default: ::Anthropic::Client::DEFAULT_INITIAL_RETRY_DELAY
|
|
17
|
+
attribute :max_retry_delay, :float, default: ::Anthropic::Client::DEFAULT_MAX_RETRY_DELAY
|
|
18
|
+
|
|
19
|
+
# Common Interface Compatibility
|
|
20
|
+
alias_attribute :access_token, :api_key
|
|
21
|
+
|
|
22
|
+
def initialize(kwargs = {})
|
|
23
|
+
kwargs = kwargs.deep_symbolize_keys if kwargs.respond_to?(:deep_symbolize_keys)
|
|
24
|
+
|
|
25
|
+
super(**deep_compact(kwargs.except(:default_url_options).merge(
|
|
26
|
+
api_key: kwargs[:api_key] || resolve_access_token(kwargs),
|
|
27
|
+
)))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def serialize
|
|
31
|
+
super.except(:anthropic_beta).tap do |hash|
|
|
32
|
+
hash[:extra_headers] = extra_headers unless extra_headers.blank?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def extra_headers
|
|
37
|
+
deep_compact(
|
|
38
|
+
"anthropic-beta" => anthropic_beta.presence,
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def resolve_access_token(kwargs)
|
|
45
|
+
kwargs[:api_key] ||
|
|
46
|
+
kwargs[:access_token] ||
|
|
47
|
+
ENV["ANTHROPIC_ACCESS_TOKEN"] ||
|
|
48
|
+
ENV["ANTHROPIC_API_KEY"]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
require_relative "_types"
|
|
5
|
+
|
|
6
|
+
module ActiveAgent
|
|
7
|
+
module Providers
|
|
8
|
+
module Anthropic
|
|
9
|
+
class Request < Common::BaseModel
|
|
10
|
+
# Required parameters
|
|
11
|
+
attribute :model, :string
|
|
12
|
+
attribute :messages, Requests::Messages::MessagesType.new
|
|
13
|
+
attribute :max_tokens, :integer, fallback: 4096
|
|
14
|
+
|
|
15
|
+
# Optional parameters - Prompting
|
|
16
|
+
attribute :system, Requests::Messages::SystemType.new
|
|
17
|
+
attribute :temperature, :float
|
|
18
|
+
attribute :top_k, :integer
|
|
19
|
+
attribute :top_p, :float
|
|
20
|
+
attribute :stop_sequences, default: -> { [] } # Array of strings
|
|
21
|
+
|
|
22
|
+
# Optional parameters - Tools
|
|
23
|
+
attribute :tools # Array of tool definitions
|
|
24
|
+
attribute :tool_choice, Requests::ToolChoice::ToolChoiceType.new
|
|
25
|
+
|
|
26
|
+
# Optional parameters - Thinking
|
|
27
|
+
attribute :thinking, Requests::ThinkingConfig::ThinkingConfigType.new
|
|
28
|
+
|
|
29
|
+
# Optional parameters - Streaming
|
|
30
|
+
attribute :stream, :boolean, default: false
|
|
31
|
+
|
|
32
|
+
# Optional parameters - Metadata
|
|
33
|
+
attribute :metadata, Requests::MetadataType.new
|
|
34
|
+
|
|
35
|
+
# Optional parameters - Context Management
|
|
36
|
+
attribute :context_management, Requests::ContextManagementConfigType.new
|
|
37
|
+
|
|
38
|
+
# Optional parameters - Container
|
|
39
|
+
attribute :container, Requests::ContainerParamsType.new
|
|
40
|
+
|
|
41
|
+
# Optional parameters - Service tier
|
|
42
|
+
attribute :service_tier, :string
|
|
43
|
+
|
|
44
|
+
# Optional parameters - MCP Servers
|
|
45
|
+
attribute :mcp_servers, default: -> { [] } # Array of MCP server definitions
|
|
46
|
+
|
|
47
|
+
# Common Format Compatibility
|
|
48
|
+
attribute :response_format, Requests::ResponseFormatType.new
|
|
49
|
+
|
|
50
|
+
# Validations for required fields
|
|
51
|
+
validates :model, :messages, :max_tokens, presence: true
|
|
52
|
+
|
|
53
|
+
# Validations for numeric parameters
|
|
54
|
+
validates :max_tokens, numericality: { greater_than_or_equal_to: 1 }, allow_nil: true
|
|
55
|
+
validates :temperature, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
56
|
+
validates :top_k, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
|
57
|
+
validates :top_p, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
|
|
58
|
+
|
|
59
|
+
# Validations for specific values
|
|
60
|
+
validates :service_tier, inclusion: { in: %w[auto standard_only] }, allow_nil: true
|
|
61
|
+
|
|
62
|
+
# Custom validations
|
|
63
|
+
validate :validate_stop_sequences
|
|
64
|
+
validate :validate_tools_format
|
|
65
|
+
validate :validate_mcp_servers_format
|
|
66
|
+
|
|
67
|
+
# Common Format Compatibility
|
|
68
|
+
alias_attribute :instructions, :system
|
|
69
|
+
|
|
70
|
+
# Handle merging in the common format
|
|
71
|
+
def message=(value)
|
|
72
|
+
self.messages ||= []
|
|
73
|
+
self.messages << Requests::Messages::MessageType.new.cast(value)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def validate_stop_sequences
|
|
79
|
+
return if stop_sequences.nil? || stop_sequences.empty?
|
|
80
|
+
|
|
81
|
+
unless stop_sequences.is_a?(Array)
|
|
82
|
+
errors.add(:stop_sequences, "must be an array")
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def validate_tools_format
|
|
87
|
+
return if tools.nil?
|
|
88
|
+
|
|
89
|
+
unless tools.is_a?(Array)
|
|
90
|
+
errors.add(:tools, "must be an array")
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def validate_mcp_servers_format
|
|
95
|
+
return if mcp_servers.nil? || mcp_servers.empty?
|
|
96
|
+
|
|
97
|
+
unless mcp_servers.is_a?(Array)
|
|
98
|
+
errors.add(:mcp_servers, "must be an array")
|
|
99
|
+
return
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if mcp_servers.length > 20
|
|
103
|
+
errors.add(:mcp_servers, "can have at most 20 servers")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|