activeagent 1.0.0.rc1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -1
- data/lib/active_agent/providers/_base_provider.rb +92 -82
- data/lib/active_agent/providers/anthropic/_types.rb +2 -2
- data/lib/active_agent/providers/anthropic/request.rb +135 -81
- data/lib/active_agent/providers/anthropic/transforms.rb +353 -0
- data/lib/active_agent/providers/anthropic_provider.rb +96 -53
- data/lib/active_agent/providers/common/messages/_types.rb +37 -1
- data/lib/active_agent/providers/common/responses/base.rb +118 -70
- data/lib/active_agent/providers/common/usage.rb +385 -0
- data/lib/active_agent/providers/concerns/instrumentation.rb +263 -0
- data/lib/active_agent/providers/log_subscriber.rb +64 -246
- data/lib/active_agent/providers/mock_provider.rb +23 -23
- data/lib/active_agent/providers/ollama/chat/request.rb +214 -35
- data/lib/active_agent/providers/ollama/chat/transforms.rb +135 -0
- data/lib/active_agent/providers/ollama/embedding/request.rb +160 -47
- data/lib/active_agent/providers/ollama/embedding/transforms.rb +160 -0
- data/lib/active_agent/providers/ollama_provider.rb +0 -1
- data/lib/active_agent/providers/open_ai/_base.rb +3 -2
- data/lib/active_agent/providers/open_ai/chat/_types.rb +13 -1
- data/lib/active_agent/providers/open_ai/chat/request.rb +132 -186
- data/lib/active_agent/providers/open_ai/chat/transforms.rb +364 -0
- data/lib/active_agent/providers/open_ai/chat_provider.rb +57 -36
- data/lib/active_agent/providers/open_ai/embedding/_types.rb +13 -2
- data/lib/active_agent/providers/open_ai/embedding/request.rb +38 -70
- data/lib/active_agent/providers/open_ai/embedding/transforms.rb +88 -0
- data/lib/active_agent/providers/open_ai/responses/_types.rb +1 -7
- data/lib/active_agent/providers/open_ai/responses/request.rb +100 -134
- data/lib/active_agent/providers/open_ai/responses/transforms.rb +228 -0
- data/lib/active_agent/providers/open_ai/responses_provider.rb +77 -30
- data/lib/active_agent/providers/open_ai_provider.rb +0 -3
- data/lib/active_agent/providers/open_router/_types.rb +27 -1
- data/lib/active_agent/providers/open_router/options.rb +49 -1
- data/lib/active_agent/providers/open_router/request.rb +232 -66
- data/lib/active_agent/providers/open_router/requests/_types.rb +0 -1
- data/lib/active_agent/providers/open_router/requests/messages/_types.rb +37 -40
- data/lib/active_agent/providers/open_router/requests/messages/content/file.rb +19 -3
- data/lib/active_agent/providers/open_router/requests/messages/content/files/details.rb +15 -4
- data/lib/active_agent/providers/open_router/requests/plugin.rb +19 -3
- data/lib/active_agent/providers/open_router/requests/plugins/pdf_config.rb +30 -8
- data/lib/active_agent/providers/open_router/requests/prediction.rb +17 -0
- data/lib/active_agent/providers/open_router/requests/provider_preferences/max_price.rb +41 -7
- data/lib/active_agent/providers/open_router/requests/provider_preferences.rb +60 -19
- data/lib/active_agent/providers/open_router/requests/response_format.rb +30 -2
- data/lib/active_agent/providers/open_router/transforms.rb +134 -0
- data/lib/active_agent/providers/open_router_provider.rb +9 -0
- data/lib/active_agent/version.rb +1 -1
- metadata +15 -159
- data/lib/active_agent/generation_provider/open_router/types.rb +0 -505
- data/lib/active_agent/generation_provider/xai_provider.rb +0 -144
- data/lib/active_agent/providers/anthropic/requests/_types.rb +0 -190
- data/lib/active_agent/providers/anthropic/requests/container_params.rb +0 -19
- data/lib/active_agent/providers/anthropic/requests/content/base.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/content/sources/base.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/context_management_config.rb +0 -18
- data/lib/active_agent/providers/anthropic/requests/messages/_types.rb +0 -189
- data/lib/active_agent/providers/anthropic/requests/messages/assistant.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/base.rb +0 -63
- data/lib/active_agent/providers/anthropic/requests/messages/content/_types.rb +0 -143
- data/lib/active_agent/providers/anthropic/requests/messages/content/base.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/messages/content/document.rb +0 -26
- data/lib/active_agent/providers/anthropic/requests/messages/content/image.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/redacted_thinking.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/messages/content/search_result.rb +0 -27
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/_types.rb +0 -171
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/base.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_base64.rb +0 -25
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_file.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_text.rb +0 -25
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_url.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_base64.rb +0 -27
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_file.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_url.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/text.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/messages/content/thinking.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_result.rb +0 -24
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_use.rb +0 -28
- data/lib/active_agent/providers/anthropic/requests/messages/user.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/metadata.rb +0 -18
- data/lib/active_agent/providers/anthropic/requests/response_format.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/thinking_config/_types.rb +0 -60
- data/lib/active_agent/providers/anthropic/requests/thinking_config/base.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/thinking_config/disabled.rb +0 -16
- data/lib/active_agent/providers/anthropic/requests/thinking_config/enabled.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/tool_choice/_types.rb +0 -78
- data/lib/active_agent/providers/anthropic/requests/tool_choice/any.rb +0 -17
- data/lib/active_agent/providers/anthropic/requests/tool_choice/auto.rb +0 -17
- data/lib/active_agent/providers/anthropic/requests/tool_choice/base.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/tool_choice/none.rb +0 -16
- data/lib/active_agent/providers/anthropic/requests/tool_choice/tool.rb +0 -20
- data/lib/active_agent/providers/ollama/chat/requests/_types.rb +0 -3
- data/lib/active_agent/providers/ollama/chat/requests/messages/_types.rb +0 -116
- data/lib/active_agent/providers/ollama/chat/requests/messages/assistant.rb +0 -19
- data/lib/active_agent/providers/ollama/chat/requests/messages/user.rb +0 -19
- data/lib/active_agent/providers/ollama/embedding/requests/_types.rb +0 -83
- data/lib/active_agent/providers/ollama/embedding/requests/options.rb +0 -104
- data/lib/active_agent/providers/open_ai/chat/requests/_types.rb +0 -229
- data/lib/active_agent/providers/open_ai/chat/requests/audio.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/messages/_types.rb +0 -123
- data/lib/active_agent/providers/open_ai/chat/requests/messages/assistant.rb +0 -42
- data/lib/active_agent/providers/open_ai/chat/requests/messages/base.rb +0 -78
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/_types.rb +0 -133
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/audio.rb +0 -35
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/base.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/file.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/_types.rb +0 -60
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/details.rb +0 -41
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/image.rb +0 -37
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/refusal.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/text.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/developer.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/function.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/system.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/tool.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/messages/user.rb +0 -32
- data/lib/active_agent/providers/open_ai/chat/requests/prediction.rb +0 -46
- data/lib/active_agent/providers/open_ai/chat/requests/response_format.rb +0 -53
- data/lib/active_agent/providers/open_ai/chat/requests/stream_options.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/tool_choice.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/tools/_types.rb +0 -5
- data/lib/active_agent/providers/open_ai/chat/requests/tools/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/chat/requests/tools/custom_tool.rb +0 -41
- data/lib/active_agent/providers/open_ai/chat/requests/tools/function_tool.rb +0 -51
- data/lib/active_agent/providers/open_ai/chat/requests/web_search_options.rb +0 -45
- data/lib/active_agent/providers/open_ai/embedding/requests/_types.rb +0 -49
- data/lib/active_agent/providers/open_ai/responses/requests/_types.rb +0 -231
- data/lib/active_agent/providers/open_ai/responses/requests/conversation.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/_types.rb +0 -264
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/assistant_message.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/base.rb +0 -89
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/code_interpreter_tool_call.rb +0 -30
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call_output.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/_types.rb +0 -207
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_audio.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_file.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_image.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_text.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call_output.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/developer_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/file_search_tool_call.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_call_output.rb +0 -32
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/image_gen_tool_call.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/input_message.rb +0 -31
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/item_reference.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call_output.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_request.rb +0 -30
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_response.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_list_tools.rb +0 -29
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_tool_call.rb +0 -35
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/output_message.rb +0 -35
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/reasoning.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/system_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_call_base.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_message.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/user_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/web_search_tool_call.rb +0 -24
- data/lib/active_agent/providers/open_ai/responses/requests/prompt_reference.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/reasoning.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/stream_options.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text/_types.rb +0 -89
- data/lib/active_agent/providers/open_ai/responses/requests/text/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_object.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_schema.rb +0 -48
- data/lib/active_agent/providers/open_ai/responses/requests/text/plain.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text.rb +0 -41
- data/lib/active_agent/providers/open_ai/responses/requests/tool_choice.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/tools/_types.rb +0 -112
- data/lib/active_agent/providers/open_ai/responses/requests/tools/base.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/tools/code_interpreter_tool.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/tools/computer_tool.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/tools/custom_tool.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/tools/file_search_tool.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/tools/function_tool.rb +0 -29
- data/lib/active_agent/providers/open_ai/responses/requests/tools/image_generation_tool.rb +0 -37
- data/lib/active_agent/providers/open_ai/responses/requests/tools/local_shell_tool.rb +0 -21
- data/lib/active_agent/providers/open_ai/responses/requests/tools/mcp_tool.rb +0 -41
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_preview_tool.rb +0 -24
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_tool.rb +0 -25
- data/lib/active_agent/providers/open_ai/schema.yml +0 -65937
- data/lib/active_agent/providers/open_router/requests/message.rb +0 -1
- data/lib/active_agent/providers/open_router/requests/messages/assistant.rb +0 -20
- data/lib/active_agent/providers/open_router/requests/messages/user.rb +0 -30
|
@@ -5,6 +5,7 @@ require_relative "_base_provider"
|
|
|
5
5
|
require_gem!(:anthropic, __FILE__)
|
|
6
6
|
|
|
7
7
|
require_relative "anthropic/_types"
|
|
8
|
+
require_relative "anthropic/transforms"
|
|
8
9
|
|
|
9
10
|
module ActiveAgent
|
|
10
11
|
module Providers
|
|
@@ -24,10 +25,10 @@ module ActiveAgent
|
|
|
24
25
|
|
|
25
26
|
protected
|
|
26
27
|
|
|
27
|
-
#
|
|
28
|
+
# Removes forced tool choice after first use to prevent endless looping.
|
|
28
29
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
30
|
+
# Clears tool_choice when the specified tool has already been called in the
|
|
31
|
+
# conversation, preventing the model from being forced to call it repeatedly.
|
|
31
32
|
#
|
|
32
33
|
# @see BaseProvider#prepare_prompt_request
|
|
33
34
|
# @return [Request]
|
|
@@ -41,20 +42,24 @@ module ActiveAgent
|
|
|
41
42
|
# @api private
|
|
42
43
|
def prepare_prompt_request_tools
|
|
43
44
|
return unless request.tool_choice
|
|
45
|
+
return unless request.tool_choice.respond_to?(:type)
|
|
44
46
|
|
|
45
47
|
functions_used = message_stack.pluck(:content).flatten.select { _1[:type] == "tool_use" }.pluck(:name)
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
# tool_choice is always a gem model object (ToolChoiceAny, ToolChoiceTool, ToolChoiceAuto)
|
|
50
|
+
tool_choice_type = request.tool_choice.type
|
|
51
|
+
tool_choice_name = request.tool_choice.respond_to?(:name) ? request.tool_choice.name : nil
|
|
52
|
+
|
|
53
|
+
if (tool_choice_type == :any && functions_used.any?) ||
|
|
54
|
+
(tool_choice_type == :tool && tool_choice_name && functions_used.include?(tool_choice_name))
|
|
49
55
|
|
|
50
|
-
instrument("tool_choice_removed.provider.active_agent")
|
|
51
56
|
request.tool_choice = nil
|
|
52
57
|
end
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
# @api private
|
|
56
61
|
def prepare_prompt_request_response_format
|
|
57
|
-
return unless request.response_format&.type == "json_object"
|
|
62
|
+
return unless request.response_format&.dig(:type) == "json_object"
|
|
58
63
|
|
|
59
64
|
self.message_stack.push({
|
|
60
65
|
role: "assistant",
|
|
@@ -62,50 +67,61 @@ module ActiveAgent
|
|
|
62
67
|
})
|
|
63
68
|
end
|
|
64
69
|
|
|
70
|
+
# @see BaseProvider#api_prompt_executer
|
|
71
|
+
# @return [Anthropic::Messages]
|
|
65
72
|
def api_prompt_executer
|
|
66
73
|
client.messages
|
|
67
74
|
end
|
|
68
75
|
|
|
69
|
-
#
|
|
76
|
+
# @see BaseProvider#api_response_normalize
|
|
77
|
+
# @param api_response [Anthropic::Models::Message]
|
|
78
|
+
# @return [Hash] normalized response hash
|
|
79
|
+
def api_response_normalize(api_response)
|
|
80
|
+
return api_response unless api_response
|
|
81
|
+
|
|
82
|
+
Anthropic::Transforms.gem_to_hash(api_response)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Processes streaming chunks and builds message incrementally in message_stack.
|
|
70
86
|
#
|
|
71
87
|
# Handles chunk types: message_start, content_block_start, content_block_delta,
|
|
72
88
|
# content_block_stop, message_delta, message_stop. Manages text deltas,
|
|
73
89
|
# tool use inputs, and Claude's thinking/signature blocks.
|
|
74
90
|
#
|
|
75
|
-
# @
|
|
91
|
+
# @see BaseProvider#process_stream_chunk
|
|
92
|
+
# @param api_response_chunk [Anthropic::StreamEvent]
|
|
76
93
|
# @return [void]
|
|
77
94
|
def process_stream_chunk(api_response_chunk)
|
|
78
|
-
|
|
79
|
-
chunk_type = api_response_chunk[:type].to_sym
|
|
95
|
+
chunk_type = api_response_chunk[:type]&.to_sym
|
|
80
96
|
|
|
81
|
-
instrument("
|
|
97
|
+
instrument("stream_chunk.active_agent", chunk_type:)
|
|
82
98
|
|
|
83
99
|
broadcast_stream_open
|
|
84
100
|
|
|
85
101
|
case chunk_type
|
|
86
102
|
# Message Created
|
|
87
103
|
when :message_start
|
|
88
|
-
api_message = api_response_chunk.
|
|
104
|
+
api_message = Anthropic::Transforms.gem_to_hash(api_response_chunk.message)
|
|
89
105
|
self.message_stack.push(api_message)
|
|
90
106
|
broadcast_stream_update(message_stack.last)
|
|
91
107
|
|
|
92
108
|
# -> Content Block Create
|
|
93
109
|
when :content_block_start
|
|
94
|
-
api_content = api_response_chunk.
|
|
110
|
+
api_content = Anthropic::Transforms.gem_to_hash(api_response_chunk.content_block)
|
|
95
111
|
self.message_stack.last[:content].push(api_content)
|
|
96
112
|
broadcast_stream_update(message_stack.last, api_content[:text])
|
|
97
113
|
|
|
98
114
|
# -> -> Content Block Append
|
|
99
115
|
when :content_block_delta
|
|
100
|
-
index = api_response_chunk.
|
|
116
|
+
index = api_response_chunk.index
|
|
101
117
|
content = self.message_stack.last[:content][index]
|
|
102
|
-
api_delta = api_response_chunk.
|
|
118
|
+
api_delta = api_response_chunk.delta
|
|
103
119
|
|
|
104
|
-
case
|
|
120
|
+
case api_delta.type.to_sym
|
|
105
121
|
# -> -> -> Content Text Append
|
|
106
122
|
when :text_delta
|
|
107
|
-
content[:text] += api_delta
|
|
108
|
-
broadcast_stream_update(message_stack.last, api_delta
|
|
123
|
+
content[:text] += api_delta.text
|
|
124
|
+
broadcast_stream_update(message_stack.last, api_delta.text)
|
|
109
125
|
|
|
110
126
|
# -> -> -> Content Function Call Append
|
|
111
127
|
when :input_json_delta
|
|
@@ -113,21 +129,29 @@ module ActiveAgent
|
|
|
113
129
|
when :thinking_delta, :signature_delta
|
|
114
130
|
# TODO: Add with thinking rendering support
|
|
115
131
|
else
|
|
116
|
-
|
|
132
|
+
raise "Unexpected delta type: #{api_delta.type}"
|
|
117
133
|
end
|
|
118
134
|
# -> Content Block Completed [Full Block]
|
|
119
135
|
when :content_block_stop
|
|
120
|
-
index = api_response_chunk.
|
|
121
|
-
api_content = api_response_chunk.
|
|
136
|
+
index = api_response_chunk.index
|
|
137
|
+
api_content = Anthropic::Transforms.gem_to_hash(api_response_chunk.content_block)
|
|
122
138
|
self.message_stack.last[:content][index] = api_content
|
|
123
139
|
|
|
124
140
|
# Message Delta
|
|
125
141
|
when :message_delta
|
|
126
|
-
|
|
142
|
+
delta = Anthropic::Transforms.gem_to_hash(api_response_chunk.delta)
|
|
143
|
+
self.message_stack.last.merge!(delta)
|
|
127
144
|
|
|
128
145
|
# Message Completed [Full Message]
|
|
129
146
|
when :message_stop
|
|
130
|
-
|
|
147
|
+
api_message = Anthropic::Transforms.gem_to_hash(api_response_chunk.message)
|
|
148
|
+
|
|
149
|
+
# Handle _json_buf (gem >= 1.14.0)
|
|
150
|
+
api_message[:content]&.each do |content_block|
|
|
151
|
+
content_block.delete(:_json_buf) if content_block[:type] == "tool_use"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
self.message_stack[-1] = api_message
|
|
131
155
|
|
|
132
156
|
# Once we are finished, close out and run tooling callbacks (Recursive)
|
|
133
157
|
process_prompt_finished if message_stack.last[:stop_reason]
|
|
@@ -137,12 +161,12 @@ module ActiveAgent
|
|
|
137
161
|
# TODO: https://docs.claude.com/en/docs/build-with-claude/streaming#error-events
|
|
138
162
|
else
|
|
139
163
|
# No-Op: Looks like internal tracking from gem wrapper
|
|
140
|
-
return if api_response_chunk.
|
|
141
|
-
|
|
164
|
+
return if api_response_chunk.respond_to?(:snapshot)
|
|
165
|
+
raise "Unexpected chunk type: #{api_response_chunk.type}"
|
|
142
166
|
end
|
|
143
167
|
end
|
|
144
168
|
|
|
145
|
-
# Executes tool calls and
|
|
169
|
+
# Executes tool calls and appends user message with results to message_stack.
|
|
146
170
|
#
|
|
147
171
|
# @param api_function_calls [Array<Hash>] with :name, :input, and :id keys
|
|
148
172
|
# @return [void]
|
|
@@ -151,58 +175,77 @@ module ActiveAgent
|
|
|
151
175
|
process_tool_call_function(api_function_call)
|
|
152
176
|
end
|
|
153
177
|
|
|
154
|
-
|
|
178
|
+
api_message = ::Anthropic::Models::MessageParam.new(role: "user", content:)
|
|
179
|
+
message = Anthropic::Transforms.gem_to_hash(api_message)
|
|
155
180
|
|
|
156
|
-
message_stack.push(message
|
|
181
|
+
message_stack.push(message)
|
|
157
182
|
end
|
|
158
183
|
|
|
159
|
-
# Executes a single tool call
|
|
184
|
+
# Executes a single tool call via callback.
|
|
160
185
|
#
|
|
161
186
|
# @param api_function_call [Hash] with :name, :input, and :id keys
|
|
162
|
-
# @return [Anthropic::
|
|
187
|
+
# @return [Anthropic::Models::ToolResultBlockParam]
|
|
163
188
|
def process_tool_call_function(api_function_call)
|
|
164
|
-
instrument("
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
189
|
+
instrument("tool_call.active_agent", tool_name: api_function_call[:name]) do
|
|
190
|
+
results = tools_function.call(
|
|
191
|
+
api_function_call[:name], **api_function_call[:input]
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
::Anthropic::Models::ToolResultBlockParam.new(
|
|
195
|
+
type: "tool_result",
|
|
196
|
+
tool_use_id: api_function_call[:id],
|
|
197
|
+
content: results.to_json,
|
|
198
|
+
is_error: false
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
169
202
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
203
|
+
# Converts API response message to hash for message_stack.
|
|
204
|
+
# Converts Anthropic gem response object to hash for storage.
|
|
205
|
+
#
|
|
206
|
+
# @param api_response [Anthropic::Models::Message]
|
|
207
|
+
# @return [Common::PromptResponse, nil]
|
|
208
|
+
def process_prompt_finished(api_response = nil)
|
|
209
|
+
# Convert gem object to hash so that raw_response[:usage] works
|
|
210
|
+
api_response_hash = api_response ? Anthropic::Transforms.gem_to_hash(api_response) : nil
|
|
211
|
+
super(api_response_hash)
|
|
174
212
|
end
|
|
175
213
|
|
|
176
|
-
# Extracts messages from completed API response.
|
|
177
214
|
#
|
|
178
|
-
# Handles JSON response format by
|
|
179
|
-
#
|
|
215
|
+
# Handles JSON response format simulation by prepending `{` to the response
|
|
216
|
+
# content after removing the assistant lead-in message.
|
|
180
217
|
#
|
|
181
|
-
# @
|
|
218
|
+
# @see BaseProvider#process_prompt_finished_extract_messages
|
|
219
|
+
# @param api_response [Hash] converted response hash
|
|
182
220
|
# @return [Array<Hash>, nil]
|
|
183
221
|
def process_prompt_finished_extract_messages(api_response)
|
|
184
222
|
return unless api_response
|
|
185
223
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
message[:content][0][:text] = "{#{message[:content][0][:text]}" # Merge in `{` prefix
|
|
224
|
+
# Handle JSON response format simulation
|
|
225
|
+
if request.response_format&.dig(:type) == "json_object"
|
|
226
|
+
request.pop_message!
|
|
227
|
+
api_response[:content][0][:text] = "{#{api_response[:content][0][:text]}"
|
|
191
228
|
end
|
|
192
229
|
|
|
193
|
-
[
|
|
230
|
+
[ api_response ]
|
|
194
231
|
end
|
|
195
232
|
|
|
196
|
-
# Extracts
|
|
233
|
+
# Extracts tool_use blocks from message_stack and parses JSON inputs.
|
|
197
234
|
#
|
|
198
|
-
#
|
|
199
|
-
# input parameters for function execution.
|
|
235
|
+
# Handles JSON buffer parsing for gem versions and string inputs for gem >= 1.14.0.
|
|
200
236
|
#
|
|
237
|
+
# @see BaseProvider#process_prompt_finished_extract_function_calls
|
|
201
238
|
# @return [Array<Hash>] with :name, :input, and :id keys
|
|
202
239
|
def process_prompt_finished_extract_function_calls
|
|
203
240
|
message_stack.pluck(:content).flatten.select { _1 in { type: "tool_use" } }.map do |api_function_call|
|
|
204
241
|
json_buf = api_function_call.delete(:json_buf)
|
|
205
242
|
api_function_call[:input] = JSON.parse(json_buf, symbolize_names: true) if json_buf
|
|
243
|
+
|
|
244
|
+
# Handle case where :input is still a JSON string (gem >= 1.14.0)
|
|
245
|
+
if api_function_call[:input].is_a?(String)
|
|
246
|
+
api_function_call[:input] = JSON.parse(api_function_call[:input], symbolize_names: true)
|
|
247
|
+
end
|
|
248
|
+
|
|
206
249
|
api_function_call
|
|
207
250
|
end
|
|
208
251
|
end
|
|
@@ -51,6 +51,12 @@ module ActiveAgent
|
|
|
51
51
|
when "assistant"
|
|
52
52
|
# Filter to only known attributes for Assistant
|
|
53
53
|
filtered_hash = hash.slice(:role, :content, :name)
|
|
54
|
+
|
|
55
|
+
# Compress content array to string if needed (Anthropic format)
|
|
56
|
+
if filtered_hash[:content].is_a?(Array)
|
|
57
|
+
filtered_hash[:content] = compress_content_array(filtered_hash[:content])
|
|
58
|
+
end
|
|
59
|
+
|
|
54
60
|
Common::Messages::Assistant.new(**filtered_hash)
|
|
55
61
|
when "tool"
|
|
56
62
|
# Filter to only known attributes for Tool
|
|
@@ -63,6 +69,13 @@ module ActiveAgent
|
|
|
63
69
|
# Check if the value responds to to_common (provider-specific message)
|
|
64
70
|
if value.respond_to?(:to_common)
|
|
65
71
|
cast_message(value.to_common)
|
|
72
|
+
# Check if it's a gem model object that can be converted to hash
|
|
73
|
+
# Use JSON round-trip to ensure proper nested serialization
|
|
74
|
+
elsif value.respond_to?(:to_json)
|
|
75
|
+
hash = JSON.parse(value.to_json, symbolize_names: true)
|
|
76
|
+
cast_message(hash)
|
|
77
|
+
elsif value.respond_to?(:to_h)
|
|
78
|
+
cast_message(value.to_h)
|
|
66
79
|
else
|
|
67
80
|
raise ArgumentError, "Cannot cast #{value.class} to Message"
|
|
68
81
|
end
|
|
@@ -78,9 +91,32 @@ module ActiveAgent
|
|
|
78
91
|
when Hash
|
|
79
92
|
value
|
|
80
93
|
else
|
|
81
|
-
|
|
94
|
+
raise ArgumentError, "Cannot serialize #{value.class}"
|
|
82
95
|
end
|
|
83
96
|
end
|
|
97
|
+
|
|
98
|
+
# Compresses Anthropic-style content array into a string.
|
|
99
|
+
#
|
|
100
|
+
# Anthropic messages can have content as an array of blocks like:
|
|
101
|
+
# [{type: "text", text: "..."}, {type: "tool_use", ...}]
|
|
102
|
+
# This extracts and joins text blocks into a single string.
|
|
103
|
+
#
|
|
104
|
+
# @param content_array [Array<Hash>]
|
|
105
|
+
# @return [String]
|
|
106
|
+
def compress_content_array(content_array)
|
|
107
|
+
content_array.map do |block|
|
|
108
|
+
case block[:type]&.to_s
|
|
109
|
+
when "text"
|
|
110
|
+
block[:text]
|
|
111
|
+
when "tool_use"
|
|
112
|
+
# Tool use blocks don't have readable text content
|
|
113
|
+
nil
|
|
114
|
+
else
|
|
115
|
+
# Unknown block type, try to extract text if present
|
|
116
|
+
block[:text]
|
|
117
|
+
end
|
|
118
|
+
end.compact.join("\n")
|
|
119
|
+
end
|
|
84
120
|
end
|
|
85
121
|
|
|
86
122
|
# Type for Messages array
|
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_agent/providers/common/model"
|
|
4
|
+
require "active_agent/providers/common/usage"
|
|
4
5
|
|
|
5
6
|
module ActiveAgent
|
|
6
7
|
module Providers
|
|
7
8
|
module Common
|
|
8
9
|
module Responses
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# This class represents the standard response structure from AI providers
|
|
12
|
-
# across different services (OpenAI, Anthropic, etc.). It provides a unified
|
|
13
|
-
# interface for accessing response data, usage statistics, and request context.
|
|
10
|
+
# Provides unified interface for AI provider responses across OpenAI, Anthropic, etc.
|
|
14
11
|
#
|
|
15
12
|
# @abstract Subclass and override {#usage} if provider uses non-standard format
|
|
16
13
|
#
|
|
17
|
-
# @note
|
|
18
|
-
# - {Prompt} for conversational/completion responses
|
|
19
|
-
# - {Embed} for embedding responses
|
|
14
|
+
# @note Use specialized subclasses for specific response types:
|
|
15
|
+
# - {Prompt} for conversational/completion responses
|
|
16
|
+
# - {Embed} for embedding responses
|
|
20
17
|
#
|
|
21
18
|
# @example Accessing response data
|
|
22
19
|
# response = agent.prompt.generate_now
|
|
23
20
|
# response.success? #=> true
|
|
24
|
-
# response.usage #=>
|
|
21
|
+
# response.usage #=> Usage object with normalized fields
|
|
25
22
|
# response.total_tokens #=> 30
|
|
26
23
|
#
|
|
27
24
|
# @example Inspecting raw provider data
|
|
@@ -33,117 +30,168 @@ module ActiveAgent
|
|
|
33
30
|
# @see BaseModel
|
|
34
31
|
class Base < BaseModel
|
|
35
32
|
# @!attribute [r] context
|
|
36
|
-
#
|
|
33
|
+
# Original request context sent to the provider.
|
|
37
34
|
#
|
|
38
|
-
#
|
|
39
|
-
# messages, tools, and other configuration passed to the LLM.
|
|
35
|
+
# Includes instructions, messages, tools, and configuration.
|
|
40
36
|
#
|
|
41
|
-
# @return [Hash]
|
|
37
|
+
# @return [Hash]
|
|
42
38
|
attribute :context, writable: false
|
|
43
39
|
|
|
44
40
|
# @!attribute [r] raw_request
|
|
45
|
-
#
|
|
41
|
+
# Most recent request in provider-specific format.
|
|
46
42
|
#
|
|
47
|
-
#
|
|
48
|
-
# useful for debugging and logging.
|
|
43
|
+
# Useful for debugging and logging.
|
|
49
44
|
#
|
|
50
|
-
# @return [Hash]
|
|
45
|
+
# @return [Hash]
|
|
51
46
|
attribute :raw_request, writable: false
|
|
52
47
|
|
|
53
48
|
# @!attribute [r] raw_response
|
|
54
|
-
#
|
|
49
|
+
# Most recent response in provider-specific format.
|
|
55
50
|
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
51
|
+
# Includes metadata, usage stats, and provider-specific fields.
|
|
52
|
+
# Hash keys are deep symbolized for consistent access.
|
|
58
53
|
#
|
|
59
|
-
# @return [Hash]
|
|
54
|
+
# @return [Hash]
|
|
60
55
|
attribute :raw_response, writable: false
|
|
61
56
|
|
|
62
|
-
#
|
|
57
|
+
# @!attribute [r] usages
|
|
58
|
+
# Usage objects from each API call in multi-turn conversations.
|
|
59
|
+
#
|
|
60
|
+
# Each call (e.g., for tool calling) tracks usage separately. These are
|
|
61
|
+
# summed to provide cumulative statistics via {#usage}.
|
|
63
62
|
#
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
# @return [Array<Usage>]
|
|
64
|
+
attribute :usages, default: -> { [] }, writable: false
|
|
65
|
+
|
|
66
|
+
# Initializes response with deep-duplicated attributes.
|
|
67
67
|
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
# @option kwargs [Hash] :raw_request the provider-formatted request
|
|
71
|
-
# @option kwargs [Hash] :raw_response the provider-formatted response
|
|
68
|
+
# Deep duplication prevents external modifications from affecting internal state.
|
|
69
|
+
# The raw_response is deep symbolized for consistent key access across providers.
|
|
72
70
|
#
|
|
73
|
-
# @
|
|
71
|
+
# @param kwargs [Hash]
|
|
72
|
+
# @option kwargs [Hash] :context
|
|
73
|
+
# @option kwargs [Hash] :raw_request
|
|
74
|
+
# @option kwargs [Hash] :raw_response
|
|
74
75
|
def initialize(kwargs = {})
|
|
75
|
-
|
|
76
|
+
kwargs = kwargs.deep_dup # Ensure that userland can't fuck with our memory space
|
|
77
|
+
|
|
78
|
+
# Deep symbolize raw_response for consistent access across all extraction methods
|
|
79
|
+
if kwargs[:raw_response].is_a?(Hash)
|
|
80
|
+
kwargs[:raw_response] = kwargs[:raw_response].deep_symbolize_keys
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
super(kwargs)
|
|
76
84
|
end
|
|
77
85
|
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
# @return [String, Array<Hash>, nil] the instructions that were sent to the provider
|
|
86
|
+
# @return [String, Array<Hash>, nil]
|
|
81
87
|
def instructions
|
|
82
88
|
context[:instructions]
|
|
83
89
|
end
|
|
84
90
|
|
|
85
|
-
# Indicates whether the generation request was successful.
|
|
86
|
-
#
|
|
87
91
|
# @todo Better handling of failure flows
|
|
88
|
-
#
|
|
89
|
-
# @return [Boolean] true if successful, false otherwise
|
|
92
|
+
# @return [Boolean]
|
|
90
93
|
def success?
|
|
91
94
|
true
|
|
92
95
|
end
|
|
93
96
|
|
|
94
|
-
#
|
|
97
|
+
# Normalized usage statistics across all providers.
|
|
98
|
+
#
|
|
99
|
+
# For multi-turn conversations with tool calling, returns cumulative
|
|
100
|
+
# usage across all API calls (sum of {#usages}).
|
|
95
101
|
#
|
|
96
|
-
#
|
|
97
|
-
# standardized format within the response. This method extracts that
|
|
98
|
-
# information for token counting and billing purposes.
|
|
102
|
+
# @return [Usage, nil]
|
|
99
103
|
#
|
|
100
|
-
# @
|
|
101
|
-
#
|
|
104
|
+
# @example Single-turn usage
|
|
105
|
+
# response.usage.input_tokens #=> 100
|
|
106
|
+
# response.usage.output_tokens #=> 25
|
|
107
|
+
# response.usage.total_tokens #=> 125
|
|
102
108
|
#
|
|
103
|
-
# @example
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
+
# @example Multi-turn usage (cumulative)
|
|
110
|
+
# # After 3 API calls due to tool usage:
|
|
111
|
+
# response.usage.input_tokens #=> 350 (sum of all calls)
|
|
112
|
+
# response.usage.output_tokens #=> 120 (sum of all calls)
|
|
113
|
+
#
|
|
114
|
+
# @see Usage
|
|
109
115
|
def usage
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
@usage ||= begin
|
|
117
|
+
if usages.any?
|
|
118
|
+
usages.reduce(:+)
|
|
119
|
+
elsif raw_response
|
|
120
|
+
Usage.from_provider_usage(
|
|
121
|
+
raw_response.is_a?(Hash) ? raw_response[:usage] : raw_response.usage
|
|
122
|
+
)
|
|
123
|
+
end
|
|
115
124
|
end
|
|
116
125
|
end
|
|
117
126
|
|
|
118
|
-
#
|
|
127
|
+
# Response ID from provider, useful for tracking and debugging.
|
|
119
128
|
#
|
|
120
|
-
# @return [
|
|
129
|
+
# @return [String, nil]
|
|
121
130
|
#
|
|
122
131
|
# @example
|
|
123
|
-
# response.
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
# response.id #=> "chatcmpl-CbDx1nXoNSBrNIMhiuy5fk7jXQjmT" (OpenAI)
|
|
133
|
+
# response.id #=> "msg_01RotDmSnYpKQjrTpaHUaEBz" (Anthropic)
|
|
134
|
+
# response.id #=> "gen-1761505659-yxgaVsqVABMQqw6oA7QF" (OpenRouter)
|
|
135
|
+
def id
|
|
136
|
+
@id ||= begin
|
|
137
|
+
return nil unless raw_response
|
|
138
|
+
|
|
139
|
+
if raw_response.is_a?(Hash)
|
|
140
|
+
raw_response[:id]
|
|
141
|
+
elsif raw_response.respond_to?(:id)
|
|
142
|
+
raw_response.id
|
|
143
|
+
end
|
|
144
|
+
end
|
|
126
145
|
end
|
|
127
146
|
|
|
128
|
-
#
|
|
147
|
+
# Model name from provider response.
|
|
148
|
+
#
|
|
149
|
+
# Useful for confirming which model was actually used, as providers may
|
|
150
|
+
# use different versions than requested.
|
|
129
151
|
#
|
|
130
|
-
# @return [
|
|
152
|
+
# @return [String, nil]
|
|
131
153
|
#
|
|
132
154
|
# @example
|
|
133
|
-
# response.
|
|
134
|
-
|
|
135
|
-
|
|
155
|
+
# response.model #=> "gpt-4o-mini-2024-07-18"
|
|
156
|
+
# response.model #=> "claude-3-5-haiku-20241022"
|
|
157
|
+
def model
|
|
158
|
+
@model ||= begin
|
|
159
|
+
return nil unless raw_response
|
|
160
|
+
|
|
161
|
+
if raw_response.is_a?(Hash)
|
|
162
|
+
raw_response[:model]
|
|
163
|
+
elsif raw_response.respond_to?(:model)
|
|
164
|
+
raw_response.model
|
|
165
|
+
end
|
|
166
|
+
end
|
|
136
167
|
end
|
|
137
168
|
|
|
138
|
-
#
|
|
169
|
+
# Finish reason from provider response.
|
|
170
|
+
#
|
|
171
|
+
# Indicates why generation stopped (e.g., "stop", "length", "tool_calls").
|
|
172
|
+
# Normalizes access across providers that use different field names.
|
|
139
173
|
#
|
|
140
|
-
# @return [
|
|
174
|
+
# @return [String, nil]
|
|
141
175
|
#
|
|
142
176
|
# @example
|
|
143
|
-
# response.
|
|
144
|
-
|
|
145
|
-
|
|
177
|
+
# response.finish_reason #=> "stop"
|
|
178
|
+
# response.finish_reason #=> "length"
|
|
179
|
+
# response.finish_reason #=> "tool_calls"
|
|
180
|
+
# response.stop_reason #=> "stop" (alias)
|
|
181
|
+
def finish_reason
|
|
182
|
+
@finish_reason ||= begin
|
|
183
|
+
return nil unless raw_response
|
|
184
|
+
|
|
185
|
+
if raw_response.is_a?(Hash)
|
|
186
|
+
# OpenAI format: choices[0].finish_reason or choices[0].message.finish_reason
|
|
187
|
+
raw_response.dig(:choices, 0, :finish_reason) ||
|
|
188
|
+
raw_response.dig(:choices, 0, :message, :finish_reason) ||
|
|
189
|
+
# Anthropic format: stop_reason
|
|
190
|
+
raw_response[:stop_reason]
|
|
191
|
+
end
|
|
192
|
+
end
|
|
146
193
|
end
|
|
194
|
+
alias_method :stop_reason, :finish_reason
|
|
147
195
|
end
|
|
148
196
|
end
|
|
149
197
|
end
|