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
data/lib/active_agent/base.rb
CHANGED
|
@@ -1,46 +1,396 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
4
|
-
require "
|
|
5
|
-
require "
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# The Base class uses ActiveSupport::Notifications for instrumentation and provides several
|
|
21
|
-
# private methods for setting payloads, applying defaults, and collecting responses from blocks,
|
|
22
|
-
# text, or templates.
|
|
23
|
-
#
|
|
24
|
-
# The class also includes several protected instance variables and defines hooks for loading
|
|
25
|
-
# additional functionality.
|
|
3
|
+
require "active_support/core_ext/hash/except"
|
|
4
|
+
require "active_support/core_ext/module/anonymous"
|
|
5
|
+
require "active_support/core_ext/string/inflections"
|
|
6
|
+
|
|
7
|
+
require "active_agent/concerns/callbacks"
|
|
8
|
+
require "active_agent/concerns/observers"
|
|
9
|
+
require "active_agent/concerns/parameterized"
|
|
10
|
+
require "active_agent/concerns/preview"
|
|
11
|
+
require "active_agent/concerns/provider"
|
|
12
|
+
require "active_agent/concerns/queueing"
|
|
13
|
+
require "active_agent/concerns/rescue"
|
|
14
|
+
require "active_agent/concerns/streaming"
|
|
15
|
+
require "active_agent/concerns/tooling"
|
|
16
|
+
require "active_agent/concerns/view"
|
|
17
|
+
|
|
18
|
+
require "active_agent/providers/log_subscriber"
|
|
19
|
+
|
|
26
20
|
module ActiveAgent
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
21
|
+
# Provides AI-powered agents with prompt generation, tool calling, and conversation management.
|
|
22
|
+
#
|
|
23
|
+
# @example Basic agent
|
|
24
|
+
# class MyAgent < ActiveAgent::Base
|
|
25
|
+
# generate_with :openai, model: "gpt-4"
|
|
26
|
+
#
|
|
27
|
+
# def greet
|
|
28
|
+
# prompt instructions: "Greet the user warmly"
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# @abstract
|
|
33
|
+
class Base < AbstractController::Base
|
|
34
|
+
abstract!
|
|
35
|
+
|
|
36
|
+
include AbstractController::Rendering
|
|
37
|
+
include AbstractController::Logger
|
|
38
|
+
include AbstractController::Helpers
|
|
39
|
+
include AbstractController::Translation
|
|
40
|
+
include AbstractController::AssetPaths
|
|
41
|
+
include AbstractController::Callbacks
|
|
42
|
+
include AbstractController::Caching
|
|
43
|
+
|
|
44
|
+
include Callbacks
|
|
45
|
+
include Parameterized
|
|
46
|
+
include Provider
|
|
47
|
+
include Queueing
|
|
48
|
+
include Rescue
|
|
49
|
+
include Streaming
|
|
50
|
+
include Tooling
|
|
51
|
+
include View
|
|
52
|
+
|
|
53
|
+
include Observers
|
|
54
|
+
include Previews
|
|
55
|
+
|
|
56
|
+
PROTECTED_OPTIONS = %i[exception_handler stream_broadcaster tools_function]
|
|
57
|
+
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [ :@_action_has_layout ]
|
|
58
|
+
|
|
59
|
+
# Logger instance for agent operations.
|
|
60
|
+
#
|
|
61
|
+
# Defaults to Rails.logger when used in Rails applications.
|
|
62
|
+
# Must conform to Log4r or Ruby Logger interface.
|
|
63
|
+
#
|
|
64
|
+
# @return [Logger, nil]
|
|
65
|
+
cattr_accessor :logger
|
|
66
|
+
|
|
67
|
+
class_attribute :prompt_options
|
|
68
|
+
class_attribute :embed_options
|
|
69
|
+
|
|
70
|
+
class << self
|
|
71
|
+
class_attribute :default_params, default: {
|
|
72
|
+
mime_version: "1.0",
|
|
73
|
+
charset: "UTF-8",
|
|
74
|
+
content_type: "text/plain",
|
|
75
|
+
parts_order: [ "text/markdown", "text/plain", "text/enriched", "text/html" ]
|
|
76
|
+
}.freeze
|
|
77
|
+
|
|
78
|
+
# Sets default parameters applied to all actions unless overridden.
|
|
79
|
+
#
|
|
80
|
+
# @param value [Hash, nil] parameters to merge, or nil to return current defaults
|
|
81
|
+
# @return [Hash]
|
|
82
|
+
#
|
|
83
|
+
# @example
|
|
84
|
+
# default temperature: 0.7, max_tokens: 1000
|
|
85
|
+
def default(value = nil)
|
|
86
|
+
self.default_params = default_params.merge(value).freeze if value
|
|
87
|
+
default_params
|
|
88
|
+
end
|
|
89
|
+
alias_method :default_params=, :default
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Configures generation provider and options for prompt generation.
|
|
93
|
+
#
|
|
94
|
+
# Options are merged with global provider config and inherited parent class options.
|
|
95
|
+
# Instructions are never inherited from parent classes.
|
|
96
|
+
#
|
|
97
|
+
# @param provider_reference [Symbol, String] generation provider (:openai, :anthropic, etc.)
|
|
98
|
+
# @param agent_options [Hash] configuration options shared across actions
|
|
99
|
+
# @return [void]
|
|
100
|
+
#
|
|
101
|
+
# @example
|
|
102
|
+
# generate_with :openai, model: "gpt-4", temperature: 0.7
|
|
103
|
+
def self.generate_with(provider_reference, **agent_options)
|
|
104
|
+
self.prompt_provider = provider_reference
|
|
105
|
+
|
|
106
|
+
global_options = provider_config_load(provider_reference)
|
|
107
|
+
inherited_options = (self.prompt_options || {}).except(:instructions) # Don't inherit instructions from parent
|
|
108
|
+
|
|
109
|
+
# Different Service, different APIs
|
|
110
|
+
if global_options[:service] != inherited_options[:service]
|
|
111
|
+
inherited_options.extract!(:service, :api_version)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
self.prompt_options = global_options.merge(inherited_options).merge(agent_options)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Configures embedding provider and options for embedding generation.
|
|
118
|
+
#
|
|
119
|
+
# @param provider_reference [Symbol, String] embedding provider (:openai, :anthropic, etc.)
|
|
120
|
+
# @param agent_options [Hash] configuration options for embedding generation
|
|
121
|
+
# @return [void]
|
|
122
|
+
#
|
|
123
|
+
# @example
|
|
124
|
+
# embed_with :openai, model: "text-embedding-3-large"
|
|
125
|
+
def self.embed_with(provider_reference, **agent_options)
|
|
126
|
+
self.embed_provider = provider_reference
|
|
127
|
+
|
|
128
|
+
global_options = provider_config_load(provider_reference)
|
|
129
|
+
inherited_options = self.embed_options || {}
|
|
130
|
+
|
|
131
|
+
self.embed_options = global_options.merge(inherited_options).merge(agent_options)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @api private
|
|
135
|
+
def self.method_missing(method_name, ...)
|
|
136
|
+
if action_methods.include?(method_name.name)
|
|
137
|
+
Generation.new(self, method_name, ...)
|
|
138
|
+
else
|
|
139
|
+
super
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
private_class_method :method_missing
|
|
143
|
+
|
|
144
|
+
# @api private
|
|
145
|
+
def self.respond_to_missing?(method, include_all = false)
|
|
146
|
+
action_methods.include?(method.name) || super
|
|
147
|
+
end
|
|
148
|
+
private_class_method :respond_to_missing?
|
|
149
|
+
|
|
150
|
+
delegate :agent_name, to: :class
|
|
151
|
+
|
|
152
|
+
# @!attribute [w] agent_name
|
|
153
|
+
# Agent name override for custom view lookup paths.
|
|
154
|
+
# @return [String]
|
|
155
|
+
attr_writer :agent_name
|
|
156
|
+
alias_method :controller_path, :agent_name
|
|
157
|
+
|
|
158
|
+
# @!attribute [rw] prompt_options
|
|
159
|
+
# Action-level prompt options merged with agent prompt options.
|
|
160
|
+
# @return [Hash]
|
|
161
|
+
attr_internal :prompt_options
|
|
162
|
+
|
|
163
|
+
# @!attribute [rw] embed_options
|
|
164
|
+
# Action-level embed options merged with agent embed options.
|
|
165
|
+
# @return [Hash]
|
|
166
|
+
attr_internal :embed_options
|
|
167
|
+
|
|
168
|
+
# @api private
|
|
169
|
+
def initialize # :nodoc:
|
|
170
|
+
super
|
|
171
|
+
self.prompt_options = (self.class.prompt_options&.deep_dup || {}).except(:trace_id)
|
|
172
|
+
self.embed_options = (self.class.embed_options&.deep_dup || {}).except(:trace_id)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Agent name used as a path for view lookup.
|
|
176
|
+
#
|
|
177
|
+
# @return [String] agent name or "anonymous" for anonymous agents
|
|
178
|
+
def agent_name
|
|
179
|
+
@agent_name ||= self.class.anonymous? ? "anonymous" : self.class.name.underscore
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Processes an agent action with ActiveSupport::Notifications instrumentation.
|
|
183
|
+
#
|
|
184
|
+
# Actions are triggered externally via Agent.action_name.generate_now or internally
|
|
185
|
+
# through tool calls during AI generation workflows.
|
|
186
|
+
#
|
|
187
|
+
# @param method_name [Symbol, String] action method to process
|
|
188
|
+
# @param args [Array]
|
|
189
|
+
# @param kwargs [Hash]
|
|
190
|
+
# @return [void]
|
|
191
|
+
# @api private
|
|
192
|
+
def process(method_name, *args, **kwargs)
|
|
193
|
+
payload = { agent: self.class.name, action: method_name, args:, kwargs: }
|
|
194
|
+
|
|
195
|
+
ActiveSupport::Notifications.instrument("process.active_agent", payload) do
|
|
196
|
+
super
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Merges action-level parameters into prompt context.
|
|
201
|
+
#
|
|
202
|
+
# Processing is deferred until execution to allow local overrides.
|
|
203
|
+
#
|
|
204
|
+
# @param messages [Array<String, Hash>] message strings or hashes to add to conversation
|
|
205
|
+
# @param options [Hash] parameters to merge into prompt context
|
|
206
|
+
# @return [void]
|
|
207
|
+
#
|
|
208
|
+
# @example
|
|
209
|
+
# def my_action
|
|
210
|
+
# prompt "User message", temperature: 0.8, instructions: "Be creative"
|
|
211
|
+
# end
|
|
212
|
+
def prompt(*messages, **options)
|
|
213
|
+
# Extract message/messages from options and add to messages array
|
|
214
|
+
messages += options.extract!(:message, :messages).values.flatten.compact
|
|
215
|
+
|
|
216
|
+
# Extract image and document attachments
|
|
217
|
+
messages += options.extract!(:image, :document).map { |k, v| { k => v } }
|
|
218
|
+
|
|
219
|
+
prompt_options.merge!({ messages: }.compact_blank.merge!(options))
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Merges action-level parameters into embedding context.
|
|
223
|
+
#
|
|
224
|
+
# @param input [String, Array<String>, nil] text to embed
|
|
225
|
+
# @param options [Hash] parameters to merge into embedding context
|
|
226
|
+
# @return [void]
|
|
227
|
+
#
|
|
228
|
+
# @example With direct input
|
|
229
|
+
# embed "Text to embed", model: "text-embedding-3-large"
|
|
230
|
+
#
|
|
231
|
+
# @example With template
|
|
232
|
+
# embed locals: { text: "Custom text" }
|
|
233
|
+
def embed(input = nil, **options)
|
|
234
|
+
new_options = { input: }.compact_blank.merge!(options)
|
|
235
|
+
embed_options.merge!(new_options)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Executes prompt generation using configured provider and options.
|
|
239
|
+
#
|
|
240
|
+
# Triggered by generate_now or generate_later workflows. Templates are
|
|
241
|
+
# rendered as late as possible to allow local overrides.
|
|
242
|
+
#
|
|
243
|
+
# @return [ActiveAgent::Providers::Response]
|
|
244
|
+
# @raise [RuntimeError] if no prompt provider is configured
|
|
245
|
+
def process_prompt
|
|
246
|
+
fail "Prompt Provider not Configured" unless prompt_provider_klass
|
|
247
|
+
|
|
248
|
+
run_callbacks(:generation) do
|
|
249
|
+
run_callbacks(:prompting) do
|
|
250
|
+
parameters = prepare_prompt_parameters
|
|
251
|
+
|
|
252
|
+
prompt_provider_klass.new(**parameters).prompt
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
alias_method :process_prompt!, :process_prompt
|
|
258
|
+
|
|
259
|
+
# Generates a preview of the prompt without executing generation.
|
|
260
|
+
#
|
|
261
|
+
# Useful for debugging and inspecting the final prompt that would be
|
|
262
|
+
# sent to the provider, including rendered templates and merged parameters.
|
|
263
|
+
#
|
|
264
|
+
# @return [String, Hash] preview format depends on provider implementation
|
|
265
|
+
# @raise [RuntimeError] if no prompt provider is configured
|
|
266
|
+
def preview_prompt
|
|
267
|
+
fail "Prompt Provider not Configured" unless prompt_provider_klass
|
|
268
|
+
|
|
269
|
+
parameters = prepare_prompt_parameters
|
|
270
|
+
|
|
271
|
+
prompt_provider_klass.new(**parameters).preview
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Executes embedding generation using configured provider and options.
|
|
275
|
+
#
|
|
276
|
+
# Templates are rendered as late as possible to allow local overrides.
|
|
277
|
+
#
|
|
278
|
+
# @return [ActiveAgent::Providers::Response]
|
|
279
|
+
# @raise [RuntimeError] if no embed provider is configured
|
|
280
|
+
def process_embed
|
|
281
|
+
fail "Embed Provider not Configured" unless embed_provider_klass
|
|
282
|
+
|
|
283
|
+
run_callbacks(:generation) do
|
|
284
|
+
run_callbacks(:embedding) do
|
|
285
|
+
parameters = prepare_embed_parameters
|
|
286
|
+
|
|
287
|
+
embed_provider_klass.new(**parameters).embed
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# @api private
|
|
293
|
+
def action_methods
|
|
294
|
+
super - ActiveAgent::Base.public_instance_methods(false).map(&:to_s) - [ action_name ]
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
private
|
|
298
|
+
|
|
299
|
+
# @api private
|
|
300
|
+
def prepare_prompt_parameters
|
|
301
|
+
parameters = prompt_options.deep_dup.except(:locals, *PROTECTED_OPTIONS)
|
|
302
|
+
|
|
303
|
+
# Render out proc/lamda attributes before rendering templates
|
|
304
|
+
parameters.deep_transform_values! { _1.respond_to?(:call) ? _1.call : _1 }
|
|
305
|
+
|
|
306
|
+
# Apply Callbacks
|
|
307
|
+
parameters.merge!(
|
|
308
|
+
trace_id: prompt_options[:trace_id] || SecureRandom.uuid,
|
|
309
|
+
exception_handler:,
|
|
310
|
+
stream_broadcaster:,
|
|
311
|
+
tools_function:,
|
|
43
312
|
)
|
|
313
|
+
|
|
314
|
+
# Apply Templates
|
|
315
|
+
parameters = process_prompt_templates(parameters, prompt_options)
|
|
316
|
+
|
|
317
|
+
parameters.compact
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# @api private
|
|
321
|
+
def prepare_embed_parameters
|
|
322
|
+
parameters = embed_options.deep_dup.except(:locals, *PROTECTED_OPTIONS)
|
|
323
|
+
|
|
324
|
+
# Render out proc/lamda attributes before rendering templates
|
|
325
|
+
parameters.deep_transform_values! { _1.respond_to?(:call) ? _1.call : _1 }
|
|
326
|
+
|
|
327
|
+
# Apply Callbacks
|
|
328
|
+
parameters.merge!(
|
|
329
|
+
trace_id: prompt_options[:trace_id] || SecureRandom.uuid,
|
|
330
|
+
exception_handler:
|
|
331
|
+
).compact!
|
|
332
|
+
|
|
333
|
+
# Fallback to input from template if no input provided, rendered as late as
|
|
334
|
+
# possible to allow local overrides.
|
|
335
|
+
if parameters[:input].blank?
|
|
336
|
+
template_input = embed_view_input(action_name, strict: true, **(embed_options[:locals] || {}))
|
|
337
|
+
parameters[:input] = template_input if template_input.present?
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
parameters.compact
|
|
44
341
|
end
|
|
342
|
+
|
|
343
|
+
# @api private
|
|
344
|
+
def process_prompt_templates(parameters, prompt_options)
|
|
345
|
+
# Resolve Instructions
|
|
346
|
+
parameters[:instructions] = prompt_view_instructions(parameters[:instructions])
|
|
347
|
+
|
|
348
|
+
# Resolve Message Template as fallback
|
|
349
|
+
if (parameters[:messages] || parameters[:input]).blank?
|
|
350
|
+
template_message = prompt_view_message(action_name, strict: parameters[:instructions].blank?, **(prompt_options[:locals] || {}))
|
|
351
|
+
parameters[:messages] = [ template_message ] if template_message.present?
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Resolve Response Format
|
|
355
|
+
if parameters[:response_format]
|
|
356
|
+
parameters[:response_format] = process_prompt_templates_response_format(parameters[:response_format])
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
parameters
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# @api private
|
|
363
|
+
def process_prompt_templates_response_format(response_format)
|
|
364
|
+
if response_format.is_a?(Symbol) || response_format.is_a?(String)
|
|
365
|
+
response_format = { type: response_format.to_s }
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
if response_format[:type].to_sym == :json_schema
|
|
369
|
+
response_format[:json_schema] = prompt_view_schema(response_format[:json_schema])
|
|
370
|
+
response_format[:json_schema].deep_transform_keys! { _1.to_s.camelize(:lower) }
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
response_format
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# @api private
|
|
377
|
+
def instrument_payload(key)
|
|
378
|
+
{
|
|
379
|
+
agent: agent_name,
|
|
380
|
+
key: key
|
|
381
|
+
}
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# @api private
|
|
385
|
+
def instrument_name
|
|
386
|
+
"active_agent"
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# @api private
|
|
390
|
+
def _protected_ivars
|
|
391
|
+
PROTECTED_IVARS
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
ActiveSupport.run_load_hooks(:active_agent, self)
|
|
45
395
|
end
|
|
46
396
|
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/callbacks"
|
|
4
|
+
|
|
5
|
+
module ActiveAgent
|
|
6
|
+
# Provides callback hooks for generation, prompting, and embedding lifecycles.
|
|
7
|
+
#
|
|
8
|
+
# Enables agents to execute custom logic before, after, or around AI operations.
|
|
9
|
+
# Callbacks support conditional execution via `:if` and `:unless` options, and
|
|
10
|
+
# after callbacks are skipped when the chain is terminated with `throw :abort`.
|
|
11
|
+
#
|
|
12
|
+
# == Callback Types
|
|
13
|
+
#
|
|
14
|
+
# Each lifecycle supports three timing hooks:
|
|
15
|
+
# * +before_*+ - executes before the operation
|
|
16
|
+
# * +after_*+ - executes after the operation (skipped if aborted)
|
|
17
|
+
# * +around_*+ - wraps the operation (must call +yield+)
|
|
18
|
+
#
|
|
19
|
+
# == Lifecycle Hierarchies
|
|
20
|
+
#
|
|
21
|
+
# * +generation+ - wraps both prompting and embedding operations
|
|
22
|
+
# * +prompting+ - specific to prompt execution
|
|
23
|
+
# * +embedding+ - specific to embedding operations
|
|
24
|
+
#
|
|
25
|
+
# Use +*_generation+ callbacks for cross-cutting concerns like rate limiting,
|
|
26
|
+
# authentication, and logging that apply to all AI operations.
|
|
27
|
+
#
|
|
28
|
+
# == Callback Control
|
|
29
|
+
#
|
|
30
|
+
# * +prepend_*+ - inserts callback at the beginning of the chain
|
|
31
|
+
# * +append_*+ - adds callback at the end (same as base methods)
|
|
32
|
+
# * +skip_*+ - removes a previously defined callback
|
|
33
|
+
#
|
|
34
|
+
# @example Rate limiting with generation callbacks
|
|
35
|
+
# class MyAgent < ActiveAgent::Base
|
|
36
|
+
# before_generation :check_rate_limit
|
|
37
|
+
# after_generation :record_usage
|
|
38
|
+
#
|
|
39
|
+
# def check_rate_limit
|
|
40
|
+
# raise RateLimitExceeded if RateLimiter.exceeded?(user_id)
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# def record_usage
|
|
44
|
+
# RateLimiter.increment(user_id)
|
|
45
|
+
# end
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# @example Before prompting callback
|
|
49
|
+
# class MyAgent < ActiveAgent::Base
|
|
50
|
+
# before_prompt :load_context
|
|
51
|
+
#
|
|
52
|
+
# def load_context
|
|
53
|
+
# @user_data = User.find(params[:user_id])
|
|
54
|
+
# end
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# @example After prompting with condition
|
|
58
|
+
# class MyAgent < ActiveAgent::Base
|
|
59
|
+
# after_prompt :log_response, if: :production?
|
|
60
|
+
#
|
|
61
|
+
# def log_response
|
|
62
|
+
# Logger.info("Generated response: #{context.messages.last}")
|
|
63
|
+
# end
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# @example Around embedding for timing
|
|
67
|
+
# class MyAgent < ActiveAgent::Base
|
|
68
|
+
# around_embed :measure_time
|
|
69
|
+
#
|
|
70
|
+
# def measure_time
|
|
71
|
+
# start = Time.now
|
|
72
|
+
# yield
|
|
73
|
+
# Rails.logger.info("Embedding took #{Time.now - start}s")
|
|
74
|
+
# end
|
|
75
|
+
# end
|
|
76
|
+
#
|
|
77
|
+
# @example Prepending and skipping callbacks
|
|
78
|
+
# class MyAgent < ActiveAgent::Base
|
|
79
|
+
# prepend_before_prompt :urgent_check # Runs first
|
|
80
|
+
# before_prompt :normal_check # Runs second
|
|
81
|
+
#
|
|
82
|
+
# skip_after_prompt :log_response # Removes inherited callback
|
|
83
|
+
# end
|
|
84
|
+
module Callbacks
|
|
85
|
+
extend ActiveSupport::Concern
|
|
86
|
+
|
|
87
|
+
included do
|
|
88
|
+
include ActiveSupport::Callbacks
|
|
89
|
+
define_callbacks :generation, skip_after_callbacks_if_terminated: true
|
|
90
|
+
define_callbacks :prompting, skip_after_callbacks_if_terminated: true
|
|
91
|
+
define_callbacks :embedding, skip_after_callbacks_if_terminated: true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
module ClassMethods
|
|
95
|
+
# Registers callbacks for the prompting lifecycle.
|
|
96
|
+
#
|
|
97
|
+
# Dynamically defines callback methods for each timing hook.
|
|
98
|
+
# Multiple callbacks execute in registration order for before/around,
|
|
99
|
+
# and reverse order for after.
|
|
100
|
+
#
|
|
101
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to call
|
|
102
|
+
# @param blk [Proc] optional block to execute instead of named method
|
|
103
|
+
# @yield callback implementation when using block form
|
|
104
|
+
#
|
|
105
|
+
# @example Multiple before callbacks
|
|
106
|
+
# before_prompt :load_user, :check_permissions
|
|
107
|
+
#
|
|
108
|
+
# @example Block syntax
|
|
109
|
+
# after_prompt do
|
|
110
|
+
# cache.write("last_response", context.messages.last)
|
|
111
|
+
# end
|
|
112
|
+
[ :before, :after, :around ].each do |callback|
|
|
113
|
+
define_method "#{callback}_prompt" do |*names, &blk|
|
|
114
|
+
_insert_callbacks(names, blk) do |name, options|
|
|
115
|
+
set_callback(:prompting, callback, name, options)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Prepends a callback to the beginning of the prompting chain.
|
|
120
|
+
#
|
|
121
|
+
# Useful for ensuring critical setup runs before other callbacks.
|
|
122
|
+
#
|
|
123
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to call
|
|
124
|
+
# @param blk [Proc] optional block to execute
|
|
125
|
+
#
|
|
126
|
+
# @example Prepend urgent validation
|
|
127
|
+
# prepend_before_prompt :validate_api_key
|
|
128
|
+
define_method "prepend_#{callback}_prompt" do |*names, &blk|
|
|
129
|
+
_insert_callbacks(names, blk) do |name, options|
|
|
130
|
+
set_callback(:prompting, callback, name, options.merge(prepend: true))
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Skips a previously defined prompting callback.
|
|
135
|
+
#
|
|
136
|
+
# Useful for removing inherited callbacks or disabling callbacks
|
|
137
|
+
# conditionally in subclasses.
|
|
138
|
+
#
|
|
139
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to skip
|
|
140
|
+
#
|
|
141
|
+
# @example Skip inherited callback
|
|
142
|
+
# skip_after_prompt :log_response
|
|
143
|
+
define_method "skip_#{callback}_prompt" do |*names|
|
|
144
|
+
_insert_callbacks(names) do |name, options|
|
|
145
|
+
skip_callback(:prompting, callback, name, options)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Alias for explicit append behavior (same as base method).
|
|
150
|
+
alias_method :"append_#{callback}_prompt", :"#{callback}_prompt"
|
|
151
|
+
|
|
152
|
+
# Registers callbacks for the generation lifecycle.
|
|
153
|
+
#
|
|
154
|
+
# Generation callbacks wrap both prompting and embedding operations,
|
|
155
|
+
# making them ideal for cross-cutting concerns like rate limiting,
|
|
156
|
+
# authentication, logging, and resource management that apply to all
|
|
157
|
+
# AI operations.
|
|
158
|
+
#
|
|
159
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to call
|
|
160
|
+
# @param blk [Proc] optional block to execute instead of named method
|
|
161
|
+
# @yield callback implementation when using block form
|
|
162
|
+
#
|
|
163
|
+
# @example Rate limiting for all operations
|
|
164
|
+
# before_generation :check_rate_limit
|
|
165
|
+
# after_generation :record_usage
|
|
166
|
+
#
|
|
167
|
+
# @example Authentication check
|
|
168
|
+
# around_generation :with_api_key
|
|
169
|
+
define_method "#{callback}_generation" do |*names, &blk|
|
|
170
|
+
_insert_callbacks(names, blk) do |name, options|
|
|
171
|
+
set_callback(:generation, callback, name, options)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Prepends a callback to the beginning of the generation chain.
|
|
176
|
+
#
|
|
177
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to call
|
|
178
|
+
# @param blk [Proc] optional block to execute
|
|
179
|
+
#
|
|
180
|
+
# @example Prepend rate limiting
|
|
181
|
+
# prepend_before_generation :check_rate_limit
|
|
182
|
+
define_method "prepend_#{callback}_generation" do |*names, &blk|
|
|
183
|
+
_insert_callbacks(names, blk) do |name, options|
|
|
184
|
+
set_callback(:generation, callback, name, options.merge(prepend: true))
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Skips a previously defined generation callback.
|
|
189
|
+
#
|
|
190
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to skip
|
|
191
|
+
#
|
|
192
|
+
# @example Skip inherited callback
|
|
193
|
+
# skip_before_generation :check_rate_limit
|
|
194
|
+
define_method "skip_#{callback}_generation" do |*names|
|
|
195
|
+
_insert_callbacks(names) do |name, options|
|
|
196
|
+
skip_callback(:generation, callback, name, options)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Alias for explicit append behavior (same as base method).
|
|
201
|
+
alias_method :"append_#{callback}_generation", :"#{callback}_generation"
|
|
202
|
+
|
|
203
|
+
# Registers callbacks for the embedding lifecycle.
|
|
204
|
+
#
|
|
205
|
+
# Behavior identical to prompting callbacks but invoked during
|
|
206
|
+
# embedding operations.
|
|
207
|
+
#
|
|
208
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to call
|
|
209
|
+
# @param blk [Proc] optional block to execute instead of named method
|
|
210
|
+
# @yield callback implementation when using block form
|
|
211
|
+
#
|
|
212
|
+
# @example Track embedding calls
|
|
213
|
+
# before_embed :increment_counter
|
|
214
|
+
# after_embed :store_embedding
|
|
215
|
+
define_method "#{callback}_embed" do |*names, &blk|
|
|
216
|
+
_insert_callbacks(names, blk) do |name, options|
|
|
217
|
+
set_callback(:embedding, callback, name, options)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Prepends a callback to the beginning of the embedding chain.
|
|
222
|
+
#
|
|
223
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to call
|
|
224
|
+
# @param blk [Proc] optional block to execute
|
|
225
|
+
#
|
|
226
|
+
# @example Prepend rate limiting
|
|
227
|
+
# prepend_before_embed :check_rate_limit
|
|
228
|
+
define_method "prepend_#{callback}_embed" do |*names, &blk|
|
|
229
|
+
_insert_callbacks(names, blk) do |name, options|
|
|
230
|
+
set_callback(:embedding, callback, name, options.merge(prepend: true))
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Skips a previously defined embedding callback.
|
|
235
|
+
#
|
|
236
|
+
# @param names [Symbol, Array<Symbol>] method name(s) to skip
|
|
237
|
+
#
|
|
238
|
+
# @example Skip inherited callback
|
|
239
|
+
# skip_before_embed :validate_input
|
|
240
|
+
define_method "skip_#{callback}_embed" do |*names|
|
|
241
|
+
_insert_callbacks(names) do |name, options|
|
|
242
|
+
skip_callback(:embedding, callback, name, options)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Alias for explicit append behavior (same as base method).
|
|
247
|
+
alias_method :"append_#{callback}_embed", :"#{callback}_embed"
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|