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,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "enabled"
|
|
5
|
+
require_relative "disabled"
|
|
6
|
+
|
|
7
|
+
module ActiveAgent
|
|
8
|
+
module Providers
|
|
9
|
+
module Anthropic
|
|
10
|
+
module Requests
|
|
11
|
+
module ThinkingConfig
|
|
12
|
+
# Type for ThinkingConfig
|
|
13
|
+
class ThinkingConfigType < ActiveModel::Type::Value
|
|
14
|
+
def cast(value)
|
|
15
|
+
case value
|
|
16
|
+
when Base
|
|
17
|
+
value
|
|
18
|
+
when Hash
|
|
19
|
+
hash = value.deep_symbolize_keys
|
|
20
|
+
type = hash[:type]&.to_sym
|
|
21
|
+
|
|
22
|
+
case type
|
|
23
|
+
when :enabled
|
|
24
|
+
Enabled.new(**hash)
|
|
25
|
+
when :disabled
|
|
26
|
+
Disabled.new(**hash)
|
|
27
|
+
when nil
|
|
28
|
+
nil
|
|
29
|
+
else
|
|
30
|
+
raise ArgumentError, "Unknown thinking config type: #{type}"
|
|
31
|
+
end
|
|
32
|
+
when nil
|
|
33
|
+
nil
|
|
34
|
+
else
|
|
35
|
+
raise ArgumentError, "Cannot cast #{value.class} to ThinkingConfig"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def serialize(value)
|
|
40
|
+
case value
|
|
41
|
+
when Base
|
|
42
|
+
value.serialize
|
|
43
|
+
when Hash
|
|
44
|
+
value
|
|
45
|
+
when nil
|
|
46
|
+
nil
|
|
47
|
+
else
|
|
48
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def deserialize(value)
|
|
53
|
+
cast(value)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module Anthropic
|
|
8
|
+
module Requests
|
|
9
|
+
module ThinkingConfig
|
|
10
|
+
# Base class for thinking configuration
|
|
11
|
+
class Base < Common::BaseModel
|
|
12
|
+
attribute :type, :string
|
|
13
|
+
|
|
14
|
+
validates :type, presence: true, inclusion: { in: %w[enabled disabled] }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module Anthropic
|
|
6
|
+
module Requests
|
|
7
|
+
module ThinkingConfig
|
|
8
|
+
# Disabled thinking configuration
|
|
9
|
+
class Disabled < Base
|
|
10
|
+
attribute :type, :string, as: "disabled"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module Anthropic
|
|
6
|
+
module Requests
|
|
7
|
+
module ThinkingConfig
|
|
8
|
+
# Enabled thinking configuration with budget
|
|
9
|
+
class Enabled < Base
|
|
10
|
+
attribute :type, :string, as: "enabled"
|
|
11
|
+
attribute :budget_tokens, :integer
|
|
12
|
+
|
|
13
|
+
validates :budget_tokens, presence: true
|
|
14
|
+
validates :budget_tokens, numericality: { greater_than_or_equal_to: 1024 }, allow_nil: false
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "auto"
|
|
5
|
+
require_relative "any"
|
|
6
|
+
require_relative "tool"
|
|
7
|
+
require_relative "none"
|
|
8
|
+
|
|
9
|
+
module ActiveAgent
|
|
10
|
+
module Providers
|
|
11
|
+
module Anthropic
|
|
12
|
+
module Requests
|
|
13
|
+
module ToolChoice
|
|
14
|
+
# Type for ToolChoice
|
|
15
|
+
class ToolChoiceType < ActiveModel::Type::Value
|
|
16
|
+
def cast(value)
|
|
17
|
+
case value
|
|
18
|
+
when Base
|
|
19
|
+
value
|
|
20
|
+
when Hash
|
|
21
|
+
hash = value.deep_symbolize_keys
|
|
22
|
+
type = hash[:type]&.to_sym
|
|
23
|
+
|
|
24
|
+
case type
|
|
25
|
+
when :auto
|
|
26
|
+
Auto.new(**hash)
|
|
27
|
+
when :any
|
|
28
|
+
Any.new(**hash)
|
|
29
|
+
when :tool
|
|
30
|
+
Tool.new(**hash)
|
|
31
|
+
when :none
|
|
32
|
+
None.new(**hash)
|
|
33
|
+
when nil
|
|
34
|
+
nil
|
|
35
|
+
else
|
|
36
|
+
raise ArgumentError, "Unknown tool choice type: #{type}"
|
|
37
|
+
end
|
|
38
|
+
when String, Symbol
|
|
39
|
+
# Allow string shortcuts like "auto", "any", "none"
|
|
40
|
+
case value.to_sym
|
|
41
|
+
when :auto
|
|
42
|
+
Auto.new
|
|
43
|
+
when :any
|
|
44
|
+
Any.new
|
|
45
|
+
when :none
|
|
46
|
+
None.new
|
|
47
|
+
else
|
|
48
|
+
raise ArgumentError, "Unknown tool choice: #{value}"
|
|
49
|
+
end
|
|
50
|
+
when nil
|
|
51
|
+
nil
|
|
52
|
+
else
|
|
53
|
+
raise ArgumentError, "Cannot cast #{value.class} to ToolChoice"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def serialize(value)
|
|
58
|
+
case value
|
|
59
|
+
when Base
|
|
60
|
+
value.serialize
|
|
61
|
+
when Hash
|
|
62
|
+
value
|
|
63
|
+
when nil
|
|
64
|
+
nil
|
|
65
|
+
else
|
|
66
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def deserialize(value)
|
|
71
|
+
cast(value)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module Anthropic
|
|
6
|
+
module Requests
|
|
7
|
+
module ToolChoice
|
|
8
|
+
# Any tool choice - model will use any available tools
|
|
9
|
+
class Any < Base
|
|
10
|
+
attribute :type, :string, as: "any"
|
|
11
|
+
attribute :disable_parallel_tool_use, :boolean
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module Anthropic
|
|
6
|
+
module Requests
|
|
7
|
+
module ToolChoice
|
|
8
|
+
# Auto tool choice - model decides whether to use tools
|
|
9
|
+
class Auto < Base
|
|
10
|
+
attribute :type, :string, as: "auto"
|
|
11
|
+
attribute :disable_parallel_tool_use, :boolean
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_agent/providers/common/model"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module Anthropic
|
|
8
|
+
module Requests
|
|
9
|
+
module ToolChoice
|
|
10
|
+
# Base class for tool choice configurations
|
|
11
|
+
class Base < Common::BaseModel
|
|
12
|
+
attribute :type, :string
|
|
13
|
+
|
|
14
|
+
validates :type, presence: true, inclusion: { in: %w[auto any tool none] }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module Anthropic
|
|
6
|
+
module Requests
|
|
7
|
+
module ToolChoice
|
|
8
|
+
# None tool choice - model will not use tools
|
|
9
|
+
class None < Base
|
|
10
|
+
attribute :type, :string, as: "none"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAgent
|
|
4
|
+
module Providers
|
|
5
|
+
module Anthropic
|
|
6
|
+
module Requests
|
|
7
|
+
module ToolChoice
|
|
8
|
+
# Tool choice - model will use the specified tool
|
|
9
|
+
class Tool < Base
|
|
10
|
+
attribute :type, :string, as: "tool"
|
|
11
|
+
attribute :name, :string
|
|
12
|
+
attribute :disable_parallel_tool_use, :boolean
|
|
13
|
+
|
|
14
|
+
validates :name, presence: true
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# lib/active_agent/providers/anthropic_provider.rb
|
|
2
|
+
|
|
3
|
+
require_relative "_base_provider"
|
|
4
|
+
|
|
5
|
+
require_gem!(:anthropic, __FILE__)
|
|
6
|
+
|
|
7
|
+
require_relative "anthropic/_types"
|
|
8
|
+
|
|
9
|
+
module ActiveAgent
|
|
10
|
+
module Providers
|
|
11
|
+
# Handles communication with Anthropic's Claude models.
|
|
12
|
+
#
|
|
13
|
+
# Supports message creation, streaming responses, tool calling, and Claude-specific
|
|
14
|
+
# features like thinking mode and content blocks. Manages tool choice cleanup to
|
|
15
|
+
# prevent endless looping when tools have been used in conversation history.
|
|
16
|
+
#
|
|
17
|
+
# @see BaseProvider
|
|
18
|
+
class AnthropicProvider < BaseProvider
|
|
19
|
+
# @todo Add support for Anthropic::BedrockClient and Anthropic::VertexClient
|
|
20
|
+
# @return [Anthropic::Client]
|
|
21
|
+
def client
|
|
22
|
+
::Anthropic::Client.new(**options.serialize)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
|
|
27
|
+
# Prepares the request and handles tool choice cleanup.
|
|
28
|
+
#
|
|
29
|
+
# Removes forced tool choice from subsequent requests to prevent endless looping
|
|
30
|
+
# when the tool has already been used in the conversation.
|
|
31
|
+
#
|
|
32
|
+
# @see BaseProvider#prepare_prompt_request
|
|
33
|
+
# @return [Request]
|
|
34
|
+
def prepare_prompt_request
|
|
35
|
+
prepare_prompt_request_tools
|
|
36
|
+
prepare_prompt_request_response_format
|
|
37
|
+
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @api private
|
|
42
|
+
def prepare_prompt_request_tools
|
|
43
|
+
return unless request.tool_choice
|
|
44
|
+
|
|
45
|
+
functions_used = message_stack.pluck(:content).flatten.select { _1[:type] == "tool_use" }.pluck(:name)
|
|
46
|
+
|
|
47
|
+
if (request.tool_choice.type == "any" && functions_used.any?) ||
|
|
48
|
+
(request.tool_choice.type == "tool" && functions_used.include?(request.tool_choice.name))
|
|
49
|
+
|
|
50
|
+
instrument("tool_choice_removed.provider.active_agent")
|
|
51
|
+
request.tool_choice = nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @api private
|
|
56
|
+
def prepare_prompt_request_response_format
|
|
57
|
+
return unless request.response_format&.type == "json_object"
|
|
58
|
+
|
|
59
|
+
self.message_stack.push({
|
|
60
|
+
role: "assistant",
|
|
61
|
+
content: "Here is the JSON requested:\n{"
|
|
62
|
+
})
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def api_prompt_executer
|
|
66
|
+
client.messages
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Processes streaming response chunks from Anthropic's API.
|
|
70
|
+
#
|
|
71
|
+
# Handles chunk types: message_start, content_block_start, content_block_delta,
|
|
72
|
+
# content_block_stop, message_delta, message_stop. Manages text deltas,
|
|
73
|
+
# tool use inputs, and Claude's thinking/signature blocks.
|
|
74
|
+
#
|
|
75
|
+
# @param api_response_chunk [Object]
|
|
76
|
+
# @return [void]
|
|
77
|
+
def process_stream_chunk(api_response_chunk)
|
|
78
|
+
api_response_chunk = api_response_chunk.as_json.deep_symbolize_keys
|
|
79
|
+
chunk_type = api_response_chunk[:type].to_sym
|
|
80
|
+
|
|
81
|
+
instrument("stream_chunk_processing.provider.active_agent", chunk_type:)
|
|
82
|
+
|
|
83
|
+
broadcast_stream_open
|
|
84
|
+
|
|
85
|
+
case chunk_type
|
|
86
|
+
# Message Created
|
|
87
|
+
when :message_start
|
|
88
|
+
api_message = api_response_chunk.fetch(:message)
|
|
89
|
+
self.message_stack.push(api_message)
|
|
90
|
+
broadcast_stream_update(message_stack.last)
|
|
91
|
+
|
|
92
|
+
# -> Content Block Create
|
|
93
|
+
when :content_block_start
|
|
94
|
+
api_content = api_response_chunk.fetch(:content_block)
|
|
95
|
+
self.message_stack.last[:content].push(api_content)
|
|
96
|
+
broadcast_stream_update(message_stack.last, api_content[:text])
|
|
97
|
+
|
|
98
|
+
# -> -> Content Block Append
|
|
99
|
+
when :content_block_delta
|
|
100
|
+
index = api_response_chunk.fetch(:index)
|
|
101
|
+
content = self.message_stack.last[:content][index]
|
|
102
|
+
api_delta = api_response_chunk.fetch(:delta)
|
|
103
|
+
|
|
104
|
+
case (delta_type = api_delta[:type].to_sym)
|
|
105
|
+
# -> -> -> Content Text Append
|
|
106
|
+
when :text_delta
|
|
107
|
+
content[:text] += api_delta[:text]
|
|
108
|
+
broadcast_stream_update(message_stack.last, api_delta[:text])
|
|
109
|
+
|
|
110
|
+
# -> -> -> Content Function Call Append
|
|
111
|
+
when :input_json_delta
|
|
112
|
+
# No-Op; Wait for Function call to be complete
|
|
113
|
+
when :thinking_delta, :signature_delta
|
|
114
|
+
# TODO: Add with thinking rendering support
|
|
115
|
+
else
|
|
116
|
+
fail "Unexpected Delta Type #{delta_type}"
|
|
117
|
+
end
|
|
118
|
+
# -> Content Block Completed [Full Block]
|
|
119
|
+
when :content_block_stop
|
|
120
|
+
index = api_response_chunk.fetch(:index)
|
|
121
|
+
api_content = api_response_chunk.fetch(:content_block)
|
|
122
|
+
self.message_stack.last[:content][index] = api_content
|
|
123
|
+
|
|
124
|
+
# Message Delta
|
|
125
|
+
when :message_delta
|
|
126
|
+
self.message_stack.last.merge!(api_response_chunk.fetch(:delta))
|
|
127
|
+
|
|
128
|
+
# Message Completed [Full Message]
|
|
129
|
+
when :message_stop
|
|
130
|
+
self.message_stack[-1] = api_response_chunk.fetch(:message)
|
|
131
|
+
|
|
132
|
+
# Once we are finished, close out and run tooling callbacks (Recursive)
|
|
133
|
+
process_prompt_finished if message_stack.last[:stop_reason]
|
|
134
|
+
when :ping
|
|
135
|
+
# No-Op Keep Awake
|
|
136
|
+
when :overloaded_error
|
|
137
|
+
# TODO: https://docs.claude.com/en/docs/build-with-claude/streaming#error-events
|
|
138
|
+
else
|
|
139
|
+
# No-Op: Looks like internal tracking from gem wrapper
|
|
140
|
+
return if api_response_chunk.key?(:snapshot)
|
|
141
|
+
fail "Unexpected Chunk Type: #{api_response_chunk[:type]}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Executes tool calls and creates user message with results.
|
|
146
|
+
#
|
|
147
|
+
# @param api_function_calls [Array<Hash>] with :name, :input, and :id keys
|
|
148
|
+
# @return [void]
|
|
149
|
+
def process_function_calls(api_function_calls)
|
|
150
|
+
content = api_function_calls.map do |api_function_call|
|
|
151
|
+
process_tool_call_function(api_function_call)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
message = Anthropic::Requests::Messages::User.new(content:)
|
|
155
|
+
|
|
156
|
+
message_stack.push(message.serialize)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Executes a single tool call and returns the result.
|
|
160
|
+
#
|
|
161
|
+
# @param api_function_call [Hash] with :name, :input, and :id keys
|
|
162
|
+
# @return [Anthropic::Requests::Content::ToolResult]
|
|
163
|
+
def process_tool_call_function(api_function_call)
|
|
164
|
+
instrument("tool_execution.provider.active_agent", tool_name: api_function_call[:name])
|
|
165
|
+
|
|
166
|
+
results = tools_function.call(
|
|
167
|
+
api_function_call[:name], **api_function_call[:input]
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
Anthropic::Requests::Content::ToolResult.new(
|
|
171
|
+
tool_use_id: api_function_call[:id],
|
|
172
|
+
content: results.to_json,
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Extracts messages from completed API response.
|
|
177
|
+
#
|
|
178
|
+
# Handles JSON response format by merging the leading `{` prefix back into
|
|
179
|
+
# the message content after removing the assistant lead-in message.
|
|
180
|
+
#
|
|
181
|
+
# @param api_response [Object]
|
|
182
|
+
# @return [Array<Hash>, nil]
|
|
183
|
+
def process_prompt_finished_extract_messages(api_response)
|
|
184
|
+
return unless api_response
|
|
185
|
+
|
|
186
|
+
message = api_response.as_json.deep_symbolize_keys
|
|
187
|
+
|
|
188
|
+
if request.response_format&.type == "json_object"
|
|
189
|
+
request.messages.pop # Remove the `Here is the JSON requested:\n{` lead in message
|
|
190
|
+
message[:content][0][:text] = "{#{message[:content][0][:text]}" # Merge in `{` prefix
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
[ message ]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Extracts function calls from message stack.
|
|
197
|
+
#
|
|
198
|
+
# Processes tool_use content blocks and converts JSON buffers into proper
|
|
199
|
+
# input parameters for function execution.
|
|
200
|
+
#
|
|
201
|
+
# @return [Array<Hash>] with :name, :input, and :id keys
|
|
202
|
+
def process_prompt_finished_extract_function_calls
|
|
203
|
+
message_stack.pluck(:content).flatten.select { _1 in { type: "tool_use" } }.map do |api_function_call|
|
|
204
|
+
json_buf = api_function_call.delete(:json_buf)
|
|
205
|
+
api_function_call[:input] = JSON.parse(json_buf, symbolize_names: true) if json_buf
|
|
206
|
+
api_function_call
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "user"
|
|
4
|
+
require_relative "assistant"
|
|
5
|
+
require_relative "tool"
|
|
6
|
+
|
|
7
|
+
module ActiveAgent
|
|
8
|
+
module Providers
|
|
9
|
+
module Common
|
|
10
|
+
module Messages
|
|
11
|
+
module Types
|
|
12
|
+
# Type for a single Message
|
|
13
|
+
class MessageType < ActiveModel::Type::Value
|
|
14
|
+
def cast(value)
|
|
15
|
+
cast_message(value)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def serialize(value)
|
|
19
|
+
serialize_message(value)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def deserialize(value)
|
|
23
|
+
cast(value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def cast_message(value)
|
|
29
|
+
case value
|
|
30
|
+
when Common::Messages::Base
|
|
31
|
+
value
|
|
32
|
+
when String
|
|
33
|
+
# Convert bare strings to user messages
|
|
34
|
+
Common::Messages::User.new(content: value)
|
|
35
|
+
when Hash
|
|
36
|
+
hash = value.deep_symbolize_keys
|
|
37
|
+
role = hash[:role]&.to_s
|
|
38
|
+
|
|
39
|
+
case role
|
|
40
|
+
when "system"
|
|
41
|
+
nil # System messages are dropped in common format, replaced by Instructions
|
|
42
|
+
when "user", nil
|
|
43
|
+
# Handle both standard format and format with `text` key
|
|
44
|
+
if hash[:text] && !hash[:content]
|
|
45
|
+
Common::Messages::User.new(content: hash[:text])
|
|
46
|
+
else
|
|
47
|
+
# Filter to only known attributes for User
|
|
48
|
+
filtered_hash = hash.slice(:role, :content, :name)
|
|
49
|
+
Common::Messages::User.new(**filtered_hash.merge(role: "user"))
|
|
50
|
+
end
|
|
51
|
+
when "assistant"
|
|
52
|
+
# Filter to only known attributes for Assistant
|
|
53
|
+
filtered_hash = hash.slice(:role, :content, :name)
|
|
54
|
+
Common::Messages::Assistant.new(**filtered_hash)
|
|
55
|
+
when "tool"
|
|
56
|
+
# Filter to only known attributes for Tool
|
|
57
|
+
filtered_hash = hash.slice(:role, :content, :tool_call_id)
|
|
58
|
+
Common::Messages::Tool.new(**filtered_hash)
|
|
59
|
+
else
|
|
60
|
+
raise ArgumentError, "Unknown message role: #{role}"
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
# Check if the value responds to to_common (provider-specific message)
|
|
64
|
+
if value.respond_to?(:to_common)
|
|
65
|
+
cast_message(value.to_common)
|
|
66
|
+
else
|
|
67
|
+
raise ArgumentError, "Cannot cast #{value.class} to Message"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def serialize_message(value)
|
|
73
|
+
case value
|
|
74
|
+
when nil
|
|
75
|
+
nil
|
|
76
|
+
when Common::Messages::Base
|
|
77
|
+
value.to_h
|
|
78
|
+
when Hash
|
|
79
|
+
value
|
|
80
|
+
else
|
|
81
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Type for Messages array
|
|
87
|
+
class MessagesType < ActiveModel::Type::Value
|
|
88
|
+
def cast(value)
|
|
89
|
+
case value
|
|
90
|
+
when Array
|
|
91
|
+
value.map { |v| message_type.cast(v) }.compact
|
|
92
|
+
when nil
|
|
93
|
+
[]
|
|
94
|
+
else
|
|
95
|
+
raise ArgumentError, "Cannot cast #{value.class} to Messages array"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def serialize(value)
|
|
100
|
+
case value
|
|
101
|
+
when Array
|
|
102
|
+
value.map { |v| message_type.serialize(v) }.compact
|
|
103
|
+
when nil
|
|
104
|
+
[]
|
|
105
|
+
else
|
|
106
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def deserialize(value)
|
|
111
|
+
cast(value)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def message_type
|
|
117
|
+
@message_type ||= MessageType.new
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module Common
|
|
8
|
+
module Messages
|
|
9
|
+
# Represents messages sent by the AI assistant in a conversation.
|
|
10
|
+
class Assistant < Base
|
|
11
|
+
attribute :role, :string, as: "assistant"
|
|
12
|
+
attribute :content, :string
|
|
13
|
+
attribute :name, :string
|
|
14
|
+
|
|
15
|
+
validates :content, presence: true
|
|
16
|
+
|
|
17
|
+
# Extracts and parses JSON object or array from message content.
|
|
18
|
+
#
|
|
19
|
+
# Searches for the first occurrence of `{` or `[` and last occurrence of `}` or `]`,
|
|
20
|
+
# then parses the content between them. Useful for extracting structured data from
|
|
21
|
+
# assistant messages that may contain additional text around the JSON.
|
|
22
|
+
#
|
|
23
|
+
# @param symbolize_names [Boolean] whether to symbolize hash keys
|
|
24
|
+
# @param normalize_names [Symbol, nil] key normalization method (e.g., :underscore)
|
|
25
|
+
# @return [Hash, Array, nil] parsed JSON structure or nil if parsing fails
|
|
26
|
+
def parsed_json(symbolize_names: true, normalize_names: :underscore)
|
|
27
|
+
start_char = [ content.index("{"), content.index("[") ].compact.min
|
|
28
|
+
end_char = [ content.rindex("}"), content.rindex("]") ].compact.max
|
|
29
|
+
content_stripped = content[start_char..end_char] if start_char && end_char
|
|
30
|
+
return unless content_stripped
|
|
31
|
+
|
|
32
|
+
content_parsed = JSON.parse(content_stripped)
|
|
33
|
+
|
|
34
|
+
transform_hash = ->(hash) do
|
|
35
|
+
next if hash.nil?
|
|
36
|
+
|
|
37
|
+
hash = hash.deep_transform_keys(&normalize_names) if normalize_names
|
|
38
|
+
hash = hash.deep_symbolize_keys if symbolize_names
|
|
39
|
+
hash
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
case content_parsed
|
|
43
|
+
when Hash then transform_hash.call(content_parsed)
|
|
44
|
+
when Array then content_parsed.map { |item| item.is_a?(Hash) ? transform_hash.call(item) : item }
|
|
45
|
+
else content_parsed
|
|
46
|
+
end
|
|
47
|
+
rescue JSON::ParserError
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
alias_method :json_object, :parsed_json
|
|
52
|
+
alias_method :parse_json, :parsed_json
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|