activeagent 1.0.0.rc1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +102 -1
- data/lib/active_agent/providers/_base_provider.rb +94 -82
- data/lib/active_agent/providers/anthropic/_types.rb +2 -2
- data/lib/active_agent/providers/anthropic/options.rb +4 -6
- data/lib/active_agent/providers/anthropic/request.rb +157 -78
- data/lib/active_agent/providers/anthropic/transforms.rb +482 -0
- data/lib/active_agent/providers/anthropic_provider.rb +159 -59
- data/lib/active_agent/providers/common/messages/_types.rb +46 -3
- data/lib/active_agent/providers/common/messages/assistant.rb +20 -4
- data/lib/active_agent/providers/common/responses/base.rb +118 -70
- data/lib/active_agent/providers/common/usage.rb +385 -0
- data/lib/active_agent/providers/concerns/instrumentation.rb +263 -0
- data/lib/active_agent/providers/concerns/previewable.rb +39 -5
- data/lib/active_agent/providers/concerns/tool_choice_clearing.rb +62 -0
- data/lib/active_agent/providers/log_subscriber.rb +64 -246
- data/lib/active_agent/providers/mock_provider.rb +23 -23
- data/lib/active_agent/providers/ollama/chat/request.rb +214 -35
- data/lib/active_agent/providers/ollama/chat/transforms.rb +135 -0
- data/lib/active_agent/providers/ollama/embedding/request.rb +160 -47
- data/lib/active_agent/providers/ollama/embedding/transforms.rb +160 -0
- data/lib/active_agent/providers/ollama_provider.rb +0 -1
- data/lib/active_agent/providers/open_ai/_base.rb +3 -2
- data/lib/active_agent/providers/open_ai/chat/_types.rb +13 -1
- data/lib/active_agent/providers/open_ai/chat/request.rb +132 -186
- data/lib/active_agent/providers/open_ai/chat/transforms.rb +444 -0
- data/lib/active_agent/providers/open_ai/chat_provider.rb +95 -36
- data/lib/active_agent/providers/open_ai/embedding/_types.rb +13 -2
- data/lib/active_agent/providers/open_ai/embedding/request.rb +38 -70
- data/lib/active_agent/providers/open_ai/embedding/transforms.rb +88 -0
- data/lib/active_agent/providers/open_ai/responses/_types.rb +1 -7
- data/lib/active_agent/providers/open_ai/responses/request.rb +116 -135
- data/lib/active_agent/providers/open_ai/responses/transforms.rb +363 -0
- data/lib/active_agent/providers/open_ai/responses_provider.rb +115 -30
- data/lib/active_agent/providers/open_ai_provider.rb +0 -3
- data/lib/active_agent/providers/open_router/_types.rb +27 -1
- data/lib/active_agent/providers/open_router/options.rb +49 -1
- data/lib/active_agent/providers/open_router/request.rb +252 -66
- data/lib/active_agent/providers/open_router/requests/_types.rb +0 -1
- data/lib/active_agent/providers/open_router/requests/messages/_types.rb +37 -40
- data/lib/active_agent/providers/open_router/requests/messages/content/file.rb +19 -3
- data/lib/active_agent/providers/open_router/requests/messages/content/files/details.rb +15 -4
- data/lib/active_agent/providers/open_router/requests/plugin.rb +19 -3
- data/lib/active_agent/providers/open_router/requests/plugins/pdf_config.rb +30 -8
- data/lib/active_agent/providers/open_router/requests/prediction.rb +17 -0
- data/lib/active_agent/providers/open_router/requests/provider_preferences/max_price.rb +41 -7
- data/lib/active_agent/providers/open_router/requests/provider_preferences.rb +60 -19
- data/lib/active_agent/providers/open_router/requests/response_format.rb +30 -2
- data/lib/active_agent/providers/open_router/transforms.rb +164 -0
- data/lib/active_agent/providers/open_router_provider.rb +23 -0
- data/lib/active_agent/version.rb +1 -1
- metadata +17 -160
- data/lib/active_agent/generation_provider/open_router/types.rb +0 -505
- data/lib/active_agent/generation_provider/xai_provider.rb +0 -144
- data/lib/active_agent/providers/anthropic/requests/_types.rb +0 -190
- data/lib/active_agent/providers/anthropic/requests/container_params.rb +0 -19
- data/lib/active_agent/providers/anthropic/requests/content/base.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/content/sources/base.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/context_management_config.rb +0 -18
- data/lib/active_agent/providers/anthropic/requests/messages/_types.rb +0 -189
- data/lib/active_agent/providers/anthropic/requests/messages/assistant.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/base.rb +0 -63
- data/lib/active_agent/providers/anthropic/requests/messages/content/_types.rb +0 -143
- data/lib/active_agent/providers/anthropic/requests/messages/content/base.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/messages/content/document.rb +0 -26
- data/lib/active_agent/providers/anthropic/requests/messages/content/image.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/redacted_thinking.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/messages/content/search_result.rb +0 -27
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/_types.rb +0 -171
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/base.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_base64.rb +0 -25
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_file.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_text.rb +0 -25
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_url.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_base64.rb +0 -27
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_file.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_url.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/text.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/messages/content/thinking.rb +0 -23
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_result.rb +0 -24
- data/lib/active_agent/providers/anthropic/requests/messages/content/tool_use.rb +0 -28
- data/lib/active_agent/providers/anthropic/requests/messages/user.rb +0 -21
- data/lib/active_agent/providers/anthropic/requests/metadata.rb +0 -18
- data/lib/active_agent/providers/anthropic/requests/response_format.rb +0 -22
- data/lib/active_agent/providers/anthropic/requests/thinking_config/_types.rb +0 -60
- data/lib/active_agent/providers/anthropic/requests/thinking_config/base.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/thinking_config/disabled.rb +0 -16
- data/lib/active_agent/providers/anthropic/requests/thinking_config/enabled.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/tool_choice/_types.rb +0 -78
- data/lib/active_agent/providers/anthropic/requests/tool_choice/any.rb +0 -17
- data/lib/active_agent/providers/anthropic/requests/tool_choice/auto.rb +0 -17
- data/lib/active_agent/providers/anthropic/requests/tool_choice/base.rb +0 -20
- data/lib/active_agent/providers/anthropic/requests/tool_choice/none.rb +0 -16
- data/lib/active_agent/providers/anthropic/requests/tool_choice/tool.rb +0 -20
- data/lib/active_agent/providers/ollama/chat/requests/_types.rb +0 -3
- data/lib/active_agent/providers/ollama/chat/requests/messages/_types.rb +0 -116
- data/lib/active_agent/providers/ollama/chat/requests/messages/assistant.rb +0 -19
- data/lib/active_agent/providers/ollama/chat/requests/messages/user.rb +0 -19
- data/lib/active_agent/providers/ollama/embedding/requests/_types.rb +0 -83
- data/lib/active_agent/providers/ollama/embedding/requests/options.rb +0 -104
- data/lib/active_agent/providers/open_ai/chat/requests/_types.rb +0 -229
- data/lib/active_agent/providers/open_ai/chat/requests/audio.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/messages/_types.rb +0 -123
- data/lib/active_agent/providers/open_ai/chat/requests/messages/assistant.rb +0 -42
- data/lib/active_agent/providers/open_ai/chat/requests/messages/base.rb +0 -78
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/_types.rb +0 -133
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/audio.rb +0 -35
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/base.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/file.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/_types.rb +0 -60
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/details.rb +0 -41
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/image.rb +0 -37
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/refusal.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/content/text.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/developer.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/function.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/system.rb +0 -25
- data/lib/active_agent/providers/open_ai/chat/requests/messages/tool.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/messages/user.rb +0 -32
- data/lib/active_agent/providers/open_ai/chat/requests/prediction.rb +0 -46
- data/lib/active_agent/providers/open_ai/chat/requests/response_format.rb +0 -53
- data/lib/active_agent/providers/open_ai/chat/requests/stream_options.rb +0 -24
- data/lib/active_agent/providers/open_ai/chat/requests/tool_choice.rb +0 -26
- data/lib/active_agent/providers/open_ai/chat/requests/tools/_types.rb +0 -5
- data/lib/active_agent/providers/open_ai/chat/requests/tools/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/chat/requests/tools/custom_tool.rb +0 -41
- data/lib/active_agent/providers/open_ai/chat/requests/tools/function_tool.rb +0 -51
- data/lib/active_agent/providers/open_ai/chat/requests/web_search_options.rb +0 -45
- data/lib/active_agent/providers/open_ai/embedding/requests/_types.rb +0 -49
- data/lib/active_agent/providers/open_ai/responses/requests/_types.rb +0 -231
- data/lib/active_agent/providers/open_ai/responses/requests/conversation.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/_types.rb +0 -264
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/assistant_message.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/base.rb +0 -89
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/code_interpreter_tool_call.rb +0 -30
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call_output.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/_types.rb +0 -207
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_audio.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_file.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_image.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_text.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call_output.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/developer_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/file_search_tool_call.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_call_output.rb +0 -32
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_tool_call.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/image_gen_tool_call.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/input_message.rb +0 -31
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/item_reference.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call_output.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_request.rb +0 -30
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_response.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_list_tools.rb +0 -29
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_tool_call.rb +0 -35
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/output_message.rb +0 -35
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/reasoning.rb +0 -33
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/system_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_call_base.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_message.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/user_message.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/inputs/web_search_tool_call.rb +0 -24
- data/lib/active_agent/providers/open_ai/responses/requests/prompt_reference.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/reasoning.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/stream_options.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text/_types.rb +0 -89
- data/lib/active_agent/providers/open_ai/responses/requests/text/base.rb +0 -22
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_object.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text/json_schema.rb +0 -48
- data/lib/active_agent/providers/open_ai/responses/requests/text/plain.rb +0 -20
- data/lib/active_agent/providers/open_ai/responses/requests/text.rb +0 -41
- data/lib/active_agent/providers/open_ai/responses/requests/tool_choice.rb +0 -26
- data/lib/active_agent/providers/open_ai/responses/requests/tools/_types.rb +0 -112
- data/lib/active_agent/providers/open_ai/responses/requests/tools/base.rb +0 -25
- data/lib/active_agent/providers/open_ai/responses/requests/tools/code_interpreter_tool.rb +0 -23
- data/lib/active_agent/providers/open_ai/responses/requests/tools/computer_tool.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/tools/custom_tool.rb +0 -28
- data/lib/active_agent/providers/open_ai/responses/requests/tools/file_search_tool.rb +0 -27
- data/lib/active_agent/providers/open_ai/responses/requests/tools/function_tool.rb +0 -29
- data/lib/active_agent/providers/open_ai/responses/requests/tools/image_generation_tool.rb +0 -37
- data/lib/active_agent/providers/open_ai/responses/requests/tools/local_shell_tool.rb +0 -21
- data/lib/active_agent/providers/open_ai/responses/requests/tools/mcp_tool.rb +0 -41
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_preview_tool.rb +0 -24
- data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_tool.rb +0 -25
- data/lib/active_agent/providers/open_ai/schema.yml +0 -65937
- data/lib/active_agent/providers/open_router/requests/message.rb +0 -1
- data/lib/active_agent/providers/open_router/requests/messages/assistant.rb +0 -20
- data/lib/active_agent/providers/open_router/requests/messages/user.rb +0 -30
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/hash/keys"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
module Providers
|
|
7
|
+
module OpenAI
|
|
8
|
+
module Responses
|
|
9
|
+
# Provides transformation methods for normalizing response parameters
|
|
10
|
+
# to OpenAI gem's native format
|
|
11
|
+
#
|
|
12
|
+
# Handles input normalization, message conversion, and response format
|
|
13
|
+
# transformation for the Responses API.
|
|
14
|
+
module Transforms
|
|
15
|
+
class << self
|
|
16
|
+
# Converts gem model object to hash via JSON round-trip
|
|
17
|
+
#
|
|
18
|
+
# @param gem_object [Object]
|
|
19
|
+
# @return [Hash] with symbolized keys
|
|
20
|
+
def gem_to_hash(gem_object)
|
|
21
|
+
JSON.parse(gem_object.to_json, symbolize_names: true)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Normalizes tools from common format to OpenAI Responses API format.
|
|
25
|
+
#
|
|
26
|
+
# Accepts tools in multiple formats:
|
|
27
|
+
# - Common format: `{name: "...", description: "...", parameters: {...}}`
|
|
28
|
+
# - Nested format: `{type: "function", function: {name: "...", ...}}`
|
|
29
|
+
# - Responses format: `{type: "function", name: "...", parameters: {...}}`
|
|
30
|
+
#
|
|
31
|
+
# Always outputs flat Responses API format.
|
|
32
|
+
#
|
|
33
|
+
# @param tools [Array<Hash>]
|
|
34
|
+
# @return [Array<Hash>]
|
|
35
|
+
def normalize_tools(tools)
|
|
36
|
+
return tools unless tools.is_a?(Array)
|
|
37
|
+
|
|
38
|
+
tools.map do |tool|
|
|
39
|
+
tool_hash = tool.is_a?(Hash) ? tool.deep_symbolize_keys : tool
|
|
40
|
+
|
|
41
|
+
# If already in Responses format (flat with type, name, parameters), return as-is
|
|
42
|
+
if tool_hash[:type] == "function" && tool_hash[:name]
|
|
43
|
+
next tool_hash
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# If in nested Chat API format, flatten it
|
|
47
|
+
if tool_hash[:type] == "function" && tool_hash[:function]
|
|
48
|
+
func = tool_hash[:function]
|
|
49
|
+
next {
|
|
50
|
+
type: "function",
|
|
51
|
+
name: func[:name],
|
|
52
|
+
description: func[:description],
|
|
53
|
+
parameters: func[:parameters] || func[:input_schema]
|
|
54
|
+
}.compact
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# If in common format (no type field), convert to Responses format
|
|
58
|
+
if tool_hash[:name] && !tool_hash[:type]
|
|
59
|
+
next {
|
|
60
|
+
type: "function",
|
|
61
|
+
name: tool_hash[:name],
|
|
62
|
+
description: tool_hash[:description],
|
|
63
|
+
parameters: tool_hash[:parameters] || tool_hash[:input_schema]
|
|
64
|
+
}.compact
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Pass through other formats
|
|
68
|
+
tool_hash
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Normalizes MCP servers from common format to OpenAI Responses API format.
|
|
73
|
+
#
|
|
74
|
+
# Common format:
|
|
75
|
+
# {name: "stripe", url: "https://...", authorization: "token"}
|
|
76
|
+
# OpenAI format:
|
|
77
|
+
# {type: "mcp", server_label: "stripe", server_url: "https://...", authorization: "token"}
|
|
78
|
+
#
|
|
79
|
+
# @param mcp_servers [Array<Hash>]
|
|
80
|
+
# @return [Array<Hash>]
|
|
81
|
+
def normalize_mcp_servers(mcp_servers)
|
|
82
|
+
return mcp_servers unless mcp_servers.is_a?(Array)
|
|
83
|
+
|
|
84
|
+
mcp_servers.map do |server|
|
|
85
|
+
server_hash = server.is_a?(Hash) ? server.deep_symbolize_keys : server
|
|
86
|
+
|
|
87
|
+
# If already in OpenAI format (has type: "mcp" and server_label), return as-is
|
|
88
|
+
if server_hash[:type] == "mcp" && server_hash[:server_label]
|
|
89
|
+
next server_hash
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Convert common format to OpenAI format
|
|
93
|
+
result = {
|
|
94
|
+
type: "mcp",
|
|
95
|
+
server_label: server_hash[:name] || server_hash[:server_label],
|
|
96
|
+
server_url: server_hash[:url] || server_hash[:server_url]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Keep authorization field (OpenAI uses 'authorization', not 'authorization_token')
|
|
100
|
+
if server_hash[:authorization]
|
|
101
|
+
result[:authorization] = server_hash[:authorization]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
result.compact
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Normalizes tool_choice from common format to OpenAI Responses API format.
|
|
109
|
+
#
|
|
110
|
+
# Responses API uses flat format for specific tool choice, unlike Chat API's nested format.
|
|
111
|
+
# Must return gem model objects for proper serialization.
|
|
112
|
+
#
|
|
113
|
+
# Maps:
|
|
114
|
+
# - "required" → :required symbol (force tool use)
|
|
115
|
+
# - "auto" → :auto symbol (let model decide)
|
|
116
|
+
# - { name: "..." } → ToolChoiceFunction model object
|
|
117
|
+
#
|
|
118
|
+
# @param tool_choice [String, Hash, Object]
|
|
119
|
+
# @return [Symbol, Object] Symbol or gem model object
|
|
120
|
+
def normalize_tool_choice(tool_choice)
|
|
121
|
+
# If already a gem model object, return as-is
|
|
122
|
+
return tool_choice if tool_choice.is_a?(::OpenAI::Models::Responses::ToolChoiceFunction) ||
|
|
123
|
+
tool_choice.is_a?(::OpenAI::Models::Responses::ToolChoiceAllowed) ||
|
|
124
|
+
tool_choice.is_a?(::OpenAI::Models::Responses::ToolChoiceTypes) ||
|
|
125
|
+
tool_choice.is_a?(::OpenAI::Models::Responses::ToolChoiceMcp) ||
|
|
126
|
+
tool_choice.is_a?(::OpenAI::Models::Responses::ToolChoiceCustom)
|
|
127
|
+
|
|
128
|
+
case tool_choice
|
|
129
|
+
when "required"
|
|
130
|
+
:required # Return as symbol
|
|
131
|
+
when "auto"
|
|
132
|
+
:auto # Return as symbol
|
|
133
|
+
when "none"
|
|
134
|
+
:none # Return as symbol
|
|
135
|
+
when Hash
|
|
136
|
+
choice_hash = tool_choice.deep_symbolize_keys
|
|
137
|
+
|
|
138
|
+
# If already in proper format with type, try to create gem model
|
|
139
|
+
if choice_hash[:type] == "function" && choice_hash[:name]
|
|
140
|
+
# Create ToolChoiceFunction gem model object
|
|
141
|
+
::OpenAI::Models::Responses::ToolChoiceFunction.new(
|
|
142
|
+
type: :function,
|
|
143
|
+
name: choice_hash[:name]
|
|
144
|
+
)
|
|
145
|
+
# Convert { name: "..." } to ToolChoiceFunction model
|
|
146
|
+
elsif choice_hash[:name] && !choice_hash[:type]
|
|
147
|
+
::OpenAI::Models::Responses::ToolChoiceFunction.new(
|
|
148
|
+
type: :function,
|
|
149
|
+
name: choice_hash[:name]
|
|
150
|
+
)
|
|
151
|
+
else
|
|
152
|
+
choice_hash
|
|
153
|
+
end
|
|
154
|
+
else
|
|
155
|
+
tool_choice
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Simplifies input for cleaner API requests
|
|
160
|
+
#
|
|
161
|
+
# Unwraps single-element arrays:
|
|
162
|
+
# - `["text"]` → `"text"`
|
|
163
|
+
# - `[{type: "input_text", text: "..."}]` → `"..."`
|
|
164
|
+
# - `[{role: "user", content: "..."}]` → `"..."`
|
|
165
|
+
#
|
|
166
|
+
# @param input [Array, String, Hash]
|
|
167
|
+
# @return [String, Array, Hash]
|
|
168
|
+
def simplify_input(input)
|
|
169
|
+
return input unless input.is_a?(Array)
|
|
170
|
+
|
|
171
|
+
# Single string element - unwrap it
|
|
172
|
+
if input.size == 1 && input[0].is_a?(String)
|
|
173
|
+
return input[0]
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Single content object {type: "input_text", text: "..."} - unwrap to string
|
|
177
|
+
if input.size == 1 &&
|
|
178
|
+
input[0].is_a?(Hash) &&
|
|
179
|
+
input[0][:type] == "input_text" &&
|
|
180
|
+
input[0][:text].is_a?(String) &&
|
|
181
|
+
input[0].keys.sort == [ :text, :type ]
|
|
182
|
+
return input[0][:text]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Single message with string content - simplify to string
|
|
186
|
+
if input.size == 1 &&
|
|
187
|
+
input[0].is_a?(Hash) &&
|
|
188
|
+
input[0][:role] == "user" &&
|
|
189
|
+
input[0][:content].is_a?(String)
|
|
190
|
+
return input[0][:content]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
input
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Normalizes response_format to OpenAI Responses API text parameter
|
|
197
|
+
#
|
|
198
|
+
# Maps common response_format structures to Responses API format.
|
|
199
|
+
# Returns ResponseTextConfig object to preserve proper nesting.
|
|
200
|
+
#
|
|
201
|
+
# @param format [Hash, Symbol, String]
|
|
202
|
+
# @return [OpenAI::Models::Responses::ResponseTextConfig]
|
|
203
|
+
def normalize_response_format(format)
|
|
204
|
+
text_hash = case format
|
|
205
|
+
when Hash
|
|
206
|
+
if format[:type] == "json_schema" || format[:type] == :json_schema
|
|
207
|
+
# json_schema format: map to Responses API structure
|
|
208
|
+
{
|
|
209
|
+
format: {
|
|
210
|
+
type: "json_schema",
|
|
211
|
+
name: format[:name] || format[:json_schema]&.dig(:name),
|
|
212
|
+
schema: format[:schema] || format[:json_schema]&.dig(:schema),
|
|
213
|
+
strict: format[:strict] || format[:json_schema]&.dig(:strict)
|
|
214
|
+
}.compact
|
|
215
|
+
}
|
|
216
|
+
elsif format[:type] == "json_object" || format[:type] == :json_object
|
|
217
|
+
# json_object format
|
|
218
|
+
{ format: { type: "json_object" } }
|
|
219
|
+
elsif format[:type]
|
|
220
|
+
# Other simple type formats (text, etc.) - wrap in format key
|
|
221
|
+
{ format: { type: format[:type].to_s } }
|
|
222
|
+
else
|
|
223
|
+
# Pass through other hash formats (already has format key or complex structure)
|
|
224
|
+
format
|
|
225
|
+
end
|
|
226
|
+
when Symbol, String
|
|
227
|
+
# Simple format types
|
|
228
|
+
{ format: { type: format.to_s } }
|
|
229
|
+
else
|
|
230
|
+
format
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Convert hash to ResponseTextConfig object to preserve nesting
|
|
234
|
+
::OpenAI::Models::Responses::ResponseTextConfig.new(**text_hash)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Normalizes input/messages to gem-compatible format
|
|
238
|
+
#
|
|
239
|
+
# Handles various input formats:
|
|
240
|
+
# - `"text"` → string (passthrough)
|
|
241
|
+
# - `{role: "user", content: "..."}` → wrapped in array
|
|
242
|
+
# - `[{text: "..."}, {image: "url"}]` → wrapped as user message with content array
|
|
243
|
+
# - `["msg1", "msg2"]` → array of user messages
|
|
244
|
+
#
|
|
245
|
+
# @param input [String, Hash, Array, Object]
|
|
246
|
+
# @return [String, Array<Hash>]
|
|
247
|
+
def normalize_input(input)
|
|
248
|
+
# String inputs pass through unchanged
|
|
249
|
+
return input if input.is_a?(String)
|
|
250
|
+
|
|
251
|
+
# Single hash should be wrapped in an array
|
|
252
|
+
if input.is_a?(Hash)
|
|
253
|
+
return [ normalize_message(input) ]
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Handle arrays
|
|
257
|
+
return input unless input.respond_to?(:map)
|
|
258
|
+
|
|
259
|
+
# Check if this is an array of content items (strings or text/image/document hashes)
|
|
260
|
+
# Content items don't have a :role key (messages do)
|
|
261
|
+
# BUT NOT a single string (which should have been caught above)
|
|
262
|
+
all_content_items = input.size > 1 && input.all? do |item|
|
|
263
|
+
if item.is_a?(String)
|
|
264
|
+
true
|
|
265
|
+
elsif item.is_a?(Hash)
|
|
266
|
+
# If it has a role, it's a message, not a content item
|
|
267
|
+
!item.key?(:role) && (item.key?(:text) || item.key?(:image) || item.key?(:document))
|
|
268
|
+
else
|
|
269
|
+
false
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
if all_content_items
|
|
274
|
+
# These are multiple content items, wrap in a user message
|
|
275
|
+
content = input.map { |item| normalize_message(item) }
|
|
276
|
+
return [ { role: "user", content: content } ]
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Otherwise treat as array of messages
|
|
280
|
+
input.map { |item| normalize_message(item, context: :input) }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Normalizes a single message to hash format
|
|
284
|
+
#
|
|
285
|
+
# Handles shorthand formats:
|
|
286
|
+
# - `{text: "..."}` → user message
|
|
287
|
+
# - `{image: "url"}` → input_image content part
|
|
288
|
+
# - `{document: "url"}` → input_file content part
|
|
289
|
+
#
|
|
290
|
+
# @param message [Hash, String, Object]
|
|
291
|
+
# @param context [Symbol] :input for messages, :content for content parts
|
|
292
|
+
# @return [Hash, String]
|
|
293
|
+
def normalize_message(message, context: :content)
|
|
294
|
+
# If it's our custom model object, serialize it
|
|
295
|
+
if message.respond_to?(:serialize)
|
|
296
|
+
message.serialize
|
|
297
|
+
elsif message.is_a?(Hash)
|
|
298
|
+
# If it has a role, it's a message - convert :text to :content
|
|
299
|
+
if message.key?(:role)
|
|
300
|
+
normalized = message.dup
|
|
301
|
+
if normalized.key?(:text) && !normalized.key?(:content)
|
|
302
|
+
normalized[:content] = normalized.delete(:text)
|
|
303
|
+
end
|
|
304
|
+
return normalized
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Expand shorthand formats to full structures for content items
|
|
308
|
+
if message.key?(:image)
|
|
309
|
+
{ type: "input_image", image_url: message[:image] }
|
|
310
|
+
elsif message.key?(:document)
|
|
311
|
+
document_value = message[:document]
|
|
312
|
+
if document_value.start_with?("data:")
|
|
313
|
+
{ type: "input_file", filename: "document.pdf", file_data: document_value }
|
|
314
|
+
else
|
|
315
|
+
{ type: "input_file", file_url: document_value }
|
|
316
|
+
end
|
|
317
|
+
elsif message.key?(:text) && message.size == 1
|
|
318
|
+
# Single :text key without :role - treat as user message
|
|
319
|
+
{ role: "user", content: message[:text] }
|
|
320
|
+
elsif message.key?(:text)
|
|
321
|
+
# Bare text content item with other keys
|
|
322
|
+
{ type: "input_text", text: message[:text] }
|
|
323
|
+
else
|
|
324
|
+
message
|
|
325
|
+
end
|
|
326
|
+
elsif message.is_a?(String)
|
|
327
|
+
# Context matters: in input array, strings become messages; in content array, they become input_text
|
|
328
|
+
if context == :input
|
|
329
|
+
{ role: "user", content: message }
|
|
330
|
+
else
|
|
331
|
+
{ type: "input_text", text: message }
|
|
332
|
+
end
|
|
333
|
+
else
|
|
334
|
+
# Pass through anything else
|
|
335
|
+
message
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Cleans up serialized request for API submission
|
|
340
|
+
#
|
|
341
|
+
# Removes default values and simplifies input where possible.
|
|
342
|
+
#
|
|
343
|
+
# @param hash [Hash] serialized request
|
|
344
|
+
# @param defaults [Hash] default values to remove
|
|
345
|
+
# @param gem_object [Object] original gem object
|
|
346
|
+
# @return [Hash] cleaned request hash
|
|
347
|
+
def cleanup_serialized_request(hash, defaults, gem_object)
|
|
348
|
+
# Remove default values that shouldn't be in the request body
|
|
349
|
+
defaults.each do |key, value|
|
|
350
|
+
hash.delete(key) if hash[key] == value
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Simplify input when possible for cleaner API requests
|
|
354
|
+
hash[:input] = simplify_input(hash[:input]) if hash[:input]
|
|
355
|
+
|
|
356
|
+
hash
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
require_relative "_base"
|
|
2
2
|
require_relative "responses/_types"
|
|
3
|
+
require_relative "responses/transforms"
|
|
3
4
|
|
|
4
5
|
module ActiveAgent
|
|
5
6
|
module Providers
|
|
6
7
|
module OpenAI
|
|
7
|
-
#
|
|
8
|
+
# Provider implementation for OpenAI's Responses API
|
|
8
9
|
#
|
|
9
|
-
# Uses the
|
|
10
|
-
#
|
|
10
|
+
# Uses the responses endpoint for improved streaming and structured function
|
|
11
|
+
# calling compared to the chat completions endpoint.
|
|
11
12
|
#
|
|
12
13
|
# @see Base
|
|
13
14
|
# @see https://platform.openai.com/docs/api-reference/responses
|
|
14
15
|
class ResponsesProvider < Base
|
|
16
|
+
include ToolChoiceClearing
|
|
17
|
+
|
|
15
18
|
# @return [Class]
|
|
16
19
|
def self.options_klass
|
|
17
20
|
Options
|
|
@@ -24,22 +27,75 @@ module ActiveAgent
|
|
|
24
27
|
|
|
25
28
|
protected
|
|
26
29
|
|
|
30
|
+
# @see BaseProvider#prepare_prompt_request
|
|
31
|
+
# @return [Request]
|
|
32
|
+
def prepare_prompt_request
|
|
33
|
+
prepare_prompt_request_tools
|
|
34
|
+
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Extracts function names from Responses API function_call items.
|
|
39
|
+
#
|
|
40
|
+
# @return [Array<String>]
|
|
41
|
+
def extract_used_function_names
|
|
42
|
+
message_stack
|
|
43
|
+
.select { |item| item[:type] == "function_call" }
|
|
44
|
+
.map { |item| item[:name] }
|
|
45
|
+
.compact
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns true if tool_choice == :required.
|
|
49
|
+
#
|
|
50
|
+
# @return [Boolean]
|
|
51
|
+
def tool_choice_forces_required?
|
|
52
|
+
request.tool_choice == :required
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns [true, name] if tool_choice is a ToolChoiceFunction model object.
|
|
56
|
+
#
|
|
57
|
+
# @return [Array<Boolean, String|nil>]
|
|
58
|
+
def tool_choice_forces_specific?
|
|
59
|
+
if request.tool_choice.is_a?(::OpenAI::Models::Responses::ToolChoiceFunction)
|
|
60
|
+
[ true, request.tool_choice.name ]
|
|
61
|
+
else
|
|
62
|
+
[ false, nil ]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
27
66
|
# @return [Object] OpenAI client's responses endpoint
|
|
28
67
|
def api_prompt_executer
|
|
29
68
|
client.responses
|
|
30
69
|
end
|
|
31
70
|
|
|
32
|
-
#
|
|
71
|
+
# @see BaseProvider#api_response_normalize
|
|
72
|
+
# @param api_response [OpenAI::Models::Responses::Response]
|
|
73
|
+
# @return [Hash] normalized response hash
|
|
74
|
+
def api_response_normalize(api_response)
|
|
75
|
+
return api_response unless api_response
|
|
76
|
+
|
|
77
|
+
Responses::Transforms.gem_to_hash(api_response)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Processes streaming response chunks from the Responses API
|
|
33
81
|
#
|
|
34
|
-
#
|
|
35
|
-
# response.
|
|
36
|
-
#
|
|
37
|
-
#
|
|
82
|
+
# Event types handled:
|
|
83
|
+
# - `:"response.created"`, `:"response.in_progress"` - response lifecycle
|
|
84
|
+
# - `:"response.output_item.added"` - message or function call added
|
|
85
|
+
# - `:"response.content_part.added"` - content part started
|
|
86
|
+
# - `:"response.output_text.delta"` - incremental text updates
|
|
87
|
+
# - `:"response.output_text.done"` - complete text
|
|
88
|
+
# - `:"response.function_call_arguments.delta"` - function argument updates
|
|
89
|
+
# - `:"response.function_call_arguments.done"` - complete function arguments
|
|
90
|
+
# - `:"response.content_part.done"` - content part completed
|
|
91
|
+
# - `:"response.output_item.done"` - message or function call completed
|
|
92
|
+
# - `:"response.completed"` - response finished
|
|
38
93
|
#
|
|
39
|
-
# @param api_response_event [Hash] streaming
|
|
94
|
+
# @param api_response_event [Hash] streaming chunk with :type key
|
|
40
95
|
# @return [void]
|
|
96
|
+
# @see Base#process_stream_chunk
|
|
41
97
|
def process_stream_chunk(api_response_event)
|
|
42
|
-
instrument("
|
|
98
|
+
instrument("stream_chunk.active_agent", chunk_type: api_response_event.type)
|
|
43
99
|
|
|
44
100
|
case api_response_event.type
|
|
45
101
|
# Response Created
|
|
@@ -55,14 +111,14 @@ module ActiveAgent
|
|
|
55
111
|
|
|
56
112
|
# -> -> -> Content Text Append
|
|
57
113
|
when :"response.output_text.delta"
|
|
58
|
-
message = message_stack.find { _1[:id] == api_response_event
|
|
59
|
-
message[:content] += api_response_event
|
|
60
|
-
broadcast_stream_update(message, api_response_event
|
|
114
|
+
message = message_stack.find { _1[:id] == api_response_event.item_id }
|
|
115
|
+
message[:content] += api_response_event.delta
|
|
116
|
+
broadcast_stream_update(message, api_response_event.delta)
|
|
61
117
|
|
|
62
118
|
# -> -> -> Content Text Completed [Full Text]
|
|
63
119
|
when :"response.output_text.done"
|
|
64
|
-
message = message_stack.find { _1[:id] == api_response_event
|
|
65
|
-
message[:content] = api_response_event
|
|
120
|
+
message = message_stack.find { _1[:id] == api_response_event.item_id }
|
|
121
|
+
message[:content] = api_response_event.text
|
|
66
122
|
broadcast_stream_update(message, nil) # Don't double send content
|
|
67
123
|
|
|
68
124
|
# -> -> -> Content Function Call Append
|
|
@@ -81,11 +137,17 @@ module ActiveAgent
|
|
|
81
137
|
# Once we are finished, close out and run tooling callbacks (Recursive)
|
|
82
138
|
process_prompt_finished
|
|
83
139
|
else
|
|
84
|
-
|
|
140
|
+
raise "Unexpected Response Chunk Type: #{api_response_event.type}"
|
|
85
141
|
end
|
|
86
142
|
end
|
|
87
143
|
|
|
88
|
-
# Processes output item added events from streaming response
|
|
144
|
+
# Processes output item added events from streaming response
|
|
145
|
+
#
|
|
146
|
+
# Handles message and function_call item types. For messages, adds to stack
|
|
147
|
+
# with empty content. For function calls, waits for completion event.
|
|
148
|
+
#
|
|
149
|
+
# Required because API returns empty array instead of empty string for
|
|
150
|
+
# initial message content due to serialization bug.
|
|
89
151
|
#
|
|
90
152
|
# @param api_response_event [Hash] response chunk with :item key
|
|
91
153
|
# @return [void]
|
|
@@ -93,15 +155,19 @@ module ActiveAgent
|
|
|
93
155
|
case api_response_event.item.type
|
|
94
156
|
when :message
|
|
95
157
|
# PATCH: API returns an empty array instead of empty string due to a bug in their serialization
|
|
96
|
-
|
|
158
|
+
item_hash = Responses::Transforms.gem_to_hash(api_response_event.item).compact_blank
|
|
159
|
+
message_stack << { content: "" }.merge(item_hash)
|
|
97
160
|
when :function_call
|
|
98
161
|
# No-Op: Wait for FC to Land (-> response.output_item.done)
|
|
99
162
|
else
|
|
100
|
-
|
|
163
|
+
raise "Unexpected Item Type: #{api_response_event.item.type}"
|
|
101
164
|
end
|
|
102
165
|
end
|
|
103
166
|
|
|
104
|
-
# Processes output item completion events from streaming response
|
|
167
|
+
# Processes output item completion events from streaming response
|
|
168
|
+
#
|
|
169
|
+
# For function calls, adds completed item to message stack.
|
|
170
|
+
# For messages, no action needed as content already updated via delta events.
|
|
105
171
|
#
|
|
106
172
|
# @param api_response_event [Hash] response chunk with completed :item
|
|
107
173
|
# @return [void]
|
|
@@ -110,35 +176,54 @@ module ActiveAgent
|
|
|
110
176
|
when :message
|
|
111
177
|
# No-Op: Message Up to Date
|
|
112
178
|
when :function_call
|
|
113
|
-
|
|
179
|
+
item_hash = Responses::Transforms.gem_to_hash(api_response_event.item)
|
|
180
|
+
message_stack << item_hash
|
|
114
181
|
else
|
|
115
|
-
|
|
182
|
+
raise "Unexpected Item Type: #{api_response_event.item.type}"
|
|
116
183
|
end
|
|
117
184
|
end
|
|
118
185
|
|
|
119
|
-
# Executes function calls and creates output messages for conversation continuation
|
|
186
|
+
# Executes function calls and creates output messages for conversation continuation
|
|
120
187
|
#
|
|
121
188
|
# @param api_function_calls [Array<Hash>] function calls with :call_id and :name keys
|
|
122
189
|
# @return [void]
|
|
190
|
+
# @see Base#process_function_calls
|
|
123
191
|
def process_function_calls(api_function_calls)
|
|
124
192
|
api_function_calls.each do |api_function_call|
|
|
125
|
-
instrument("
|
|
193
|
+
output = instrument("tool_call.active_agent", tool_name: api_function_call[:name]) do
|
|
194
|
+
process_tool_call_function(api_function_call).to_json
|
|
195
|
+
end
|
|
126
196
|
|
|
127
|
-
|
|
197
|
+
# Create native gem input item for function call output
|
|
198
|
+
message = ::OpenAI::Models::Responses::ResponseInputItem::FunctionCallOutput.new(
|
|
128
199
|
call_id: api_function_call[:call_id],
|
|
129
|
-
output:
|
|
200
|
+
output:
|
|
130
201
|
)
|
|
131
202
|
|
|
132
|
-
message_stack
|
|
203
|
+
# Convert to hash for message_stack
|
|
204
|
+
message_stack.push(Responses::Transforms.gem_to_hash(message))
|
|
133
205
|
end
|
|
134
206
|
end
|
|
135
207
|
|
|
208
|
+
# Converts OpenAI gem response object to hash for storage.
|
|
209
|
+
#
|
|
210
|
+
# @param api_response [OpenAI::Models::Responses::Response]
|
|
211
|
+
# @return [Common::PromptResponse, nil]
|
|
212
|
+
def process_prompt_finished(api_response = nil)
|
|
213
|
+
# Convert gem object to hash so that raw_response["usage"] works
|
|
214
|
+
api_response_hash = api_response ? Responses::Transforms.gem_to_hash(api_response) : nil
|
|
215
|
+
super(api_response_hash)
|
|
216
|
+
end
|
|
217
|
+
|
|
136
218
|
# Extracts messages from completed API response.
|
|
137
219
|
#
|
|
138
|
-
# @param api_response [Hash]
|
|
139
|
-
# @return [Array, nil] output array from response
|
|
220
|
+
# @param api_response [Hash] converted response hash
|
|
221
|
+
# @return [Array, nil] output array from response.output or nil
|
|
140
222
|
def process_prompt_finished_extract_messages(api_response)
|
|
141
|
-
api_response
|
|
223
|
+
return unless api_response
|
|
224
|
+
|
|
225
|
+
# Response is already a hash from process_prompt_finished
|
|
226
|
+
api_response[:output]
|
|
142
227
|
end
|
|
143
228
|
|
|
144
229
|
# Extracts function calls from message stack.
|
|
@@ -60,10 +60,8 @@ module ActiveAgent
|
|
|
60
60
|
# @see https://platform.openai.com/docs/guides/migrate-to-responses
|
|
61
61
|
def prompt
|
|
62
62
|
if api_version == :chat || context[:audio].present?
|
|
63
|
-
instrument("api_routing.provider.active_agent", api_type: :chat, api_version: api_version, has_audio: context[:audio].present?)
|
|
64
63
|
OpenAI::ChatProvider.new(raw_options).prompt
|
|
65
64
|
else # api_version == :responses || true
|
|
66
|
-
instrument("api_routing.provider.active_agent", api_type: :responses, api_version: api_version)
|
|
67
65
|
OpenAI::ResponsesProvider.new(raw_options).prompt
|
|
68
66
|
end
|
|
69
67
|
end
|
|
@@ -89,7 +87,6 @@ module ActiveAgent
|
|
|
89
87
|
# @param parameters [Hash] The embedding request parameters
|
|
90
88
|
# @return [Object] The embedding response from OpenAI
|
|
91
89
|
def api_embed_execute(parameters)
|
|
92
|
-
instrument("embeddings_request.provider.active_agent")
|
|
93
90
|
client.embeddings.create(**parameters).as_json.deep_symbolize_keys
|
|
94
91
|
end
|
|
95
92
|
end
|
|
@@ -8,8 +8,25 @@ require_relative "options"
|
|
|
8
8
|
module ActiveAgent
|
|
9
9
|
module Providers
|
|
10
10
|
module OpenRouter
|
|
11
|
-
#
|
|
11
|
+
# ActiveModel type for casting and serializing OpenRouter requests
|
|
12
|
+
#
|
|
13
|
+
# Handles conversion between hashes, Request objects, and serialized
|
|
14
|
+
# request hashes for the OpenRouter API.
|
|
15
|
+
#
|
|
16
|
+
# @example Type casting
|
|
17
|
+
# type = RequestType.new
|
|
18
|
+
# request = type.cast({ model: "openai/gpt-4", messages: "Hello" })
|
|
19
|
+
# # => #<Request ...>
|
|
20
|
+
#
|
|
21
|
+
# @example Serialization
|
|
22
|
+
# serialized = type.serialize(request)
|
|
23
|
+
# # => { model: "openai/gpt-4", messages: [...] }
|
|
12
24
|
class RequestType < ActiveModel::Type::Value
|
|
25
|
+
# Casts value to Request object
|
|
26
|
+
#
|
|
27
|
+
# @param value [Request, Hash, nil]
|
|
28
|
+
# @return [Request, nil]
|
|
29
|
+
# @raise [ArgumentError] if value cannot be cast
|
|
13
30
|
def cast(value)
|
|
14
31
|
case value
|
|
15
32
|
when Request
|
|
@@ -23,6 +40,11 @@ module ActiveAgent
|
|
|
23
40
|
end
|
|
24
41
|
end
|
|
25
42
|
|
|
43
|
+
# Serializes Request to hash for API submission
|
|
44
|
+
#
|
|
45
|
+
# @param value [Request, Hash, nil]
|
|
46
|
+
# @return [Hash, nil]
|
|
47
|
+
# @raise [ArgumentError] if value cannot be serialized
|
|
26
48
|
def serialize(value)
|
|
27
49
|
case value
|
|
28
50
|
when Request
|
|
@@ -36,6 +58,10 @@ module ActiveAgent
|
|
|
36
58
|
end
|
|
37
59
|
end
|
|
38
60
|
|
|
61
|
+
# Deserializes value from storage
|
|
62
|
+
#
|
|
63
|
+
# @param value [Object]
|
|
64
|
+
# @return [Request, nil]
|
|
39
65
|
def deserialize(value)
|
|
40
66
|
cast(value)
|
|
41
67
|
end
|