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.
Files changed (282) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +210 -2
  3. data/README.md +15 -24
  4. data/lib/active_agent/base.rb +389 -39
  5. data/lib/active_agent/concerns/callbacks.rb +251 -0
  6. data/lib/active_agent/concerns/observers.rb +147 -0
  7. data/lib/active_agent/concerns/parameterized.rb +292 -0
  8. data/lib/active_agent/concerns/provider.rb +120 -0
  9. data/lib/active_agent/concerns/queueing.rb +36 -0
  10. data/lib/active_agent/concerns/rescue.rb +64 -0
  11. data/lib/active_agent/concerns/streaming.rb +282 -0
  12. data/lib/active_agent/concerns/tooling.rb +23 -0
  13. data/lib/active_agent/concerns/view.rb +150 -0
  14. data/lib/active_agent/configuration.rb +442 -20
  15. data/lib/active_agent/generation.rb +141 -47
  16. data/lib/active_agent/generation_provider/open_router/types.rb +505 -0
  17. data/lib/active_agent/generation_provider/xai_provider.rb +144 -0
  18. data/lib/active_agent/providers/_base_provider.rb +410 -0
  19. data/lib/active_agent/providers/anthropic/_types.rb +63 -0
  20. data/lib/active_agent/providers/anthropic/options.rb +53 -0
  21. data/lib/active_agent/providers/anthropic/request.rb +109 -0
  22. data/lib/active_agent/providers/anthropic/requests/_types.rb +190 -0
  23. data/lib/active_agent/providers/anthropic/requests/container_params.rb +19 -0
  24. data/lib/active_agent/providers/anthropic/requests/content/base.rb +21 -0
  25. data/lib/active_agent/providers/anthropic/requests/content/sources/base.rb +22 -0
  26. data/lib/active_agent/providers/anthropic/requests/context_management_config.rb +18 -0
  27. data/lib/active_agent/providers/anthropic/requests/messages/_types.rb +189 -0
  28. data/lib/active_agent/providers/anthropic/requests/messages/assistant.rb +23 -0
  29. data/lib/active_agent/providers/anthropic/requests/messages/base.rb +63 -0
  30. data/lib/active_agent/providers/anthropic/requests/messages/content/_types.rb +143 -0
  31. data/lib/active_agent/providers/anthropic/requests/messages/content/base.rb +21 -0
  32. data/lib/active_agent/providers/anthropic/requests/messages/content/document.rb +26 -0
  33. data/lib/active_agent/providers/anthropic/requests/messages/content/image.rb +23 -0
  34. data/lib/active_agent/providers/anthropic/requests/messages/content/redacted_thinking.rb +21 -0
  35. data/lib/active_agent/providers/anthropic/requests/messages/content/search_result.rb +27 -0
  36. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/_types.rb +171 -0
  37. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/base.rb +22 -0
  38. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_base64.rb +25 -0
  39. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_file.rb +23 -0
  40. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_text.rb +25 -0
  41. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_url.rb +23 -0
  42. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_base64.rb +27 -0
  43. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_file.rb +23 -0
  44. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_url.rb +23 -0
  45. data/lib/active_agent/providers/anthropic/requests/messages/content/text.rb +22 -0
  46. data/lib/active_agent/providers/anthropic/requests/messages/content/thinking.rb +23 -0
  47. data/lib/active_agent/providers/anthropic/requests/messages/content/tool_result.rb +24 -0
  48. data/lib/active_agent/providers/anthropic/requests/messages/content/tool_use.rb +28 -0
  49. data/lib/active_agent/providers/anthropic/requests/messages/user.rb +21 -0
  50. data/lib/active_agent/providers/anthropic/requests/metadata.rb +18 -0
  51. data/lib/active_agent/providers/anthropic/requests/response_format.rb +22 -0
  52. data/lib/active_agent/providers/anthropic/requests/thinking_config/_types.rb +60 -0
  53. data/lib/active_agent/providers/anthropic/requests/thinking_config/base.rb +20 -0
  54. data/lib/active_agent/providers/anthropic/requests/thinking_config/disabled.rb +16 -0
  55. data/lib/active_agent/providers/anthropic/requests/thinking_config/enabled.rb +20 -0
  56. data/lib/active_agent/providers/anthropic/requests/tool_choice/_types.rb +78 -0
  57. data/lib/active_agent/providers/anthropic/requests/tool_choice/any.rb +17 -0
  58. data/lib/active_agent/providers/anthropic/requests/tool_choice/auto.rb +17 -0
  59. data/lib/active_agent/providers/anthropic/requests/tool_choice/base.rb +20 -0
  60. data/lib/active_agent/providers/anthropic/requests/tool_choice/none.rb +16 -0
  61. data/lib/active_agent/providers/anthropic/requests/tool_choice/tool.rb +20 -0
  62. data/lib/active_agent/providers/anthropic_provider.rb +211 -0
  63. data/lib/active_agent/providers/common/messages/_types.rb +124 -0
  64. data/lib/active_agent/providers/common/messages/assistant.rb +57 -0
  65. data/lib/active_agent/providers/common/messages/base.rb +17 -0
  66. data/lib/active_agent/providers/common/messages/system.rb +20 -0
  67. data/lib/active_agent/providers/common/messages/tool.rb +21 -0
  68. data/lib/active_agent/providers/common/messages/user.rb +20 -0
  69. data/lib/active_agent/providers/common/model.rb +361 -0
  70. data/lib/active_agent/providers/common/response.rb +13 -0
  71. data/lib/active_agent/providers/common/responses/_types.rb +51 -0
  72. data/lib/active_agent/providers/common/responses/base.rb +151 -0
  73. data/lib/active_agent/providers/common/responses/embed.rb +33 -0
  74. data/lib/active_agent/providers/common/responses/format.rb +31 -0
  75. data/lib/active_agent/providers/common/responses/message.rb +3 -0
  76. data/lib/active_agent/providers/common/responses/prompt.rb +42 -0
  77. data/lib/active_agent/providers/concerns/exception_handler.rb +72 -0
  78. data/lib/active_agent/providers/concerns/previewable.rb +150 -0
  79. data/lib/active_agent/providers/log_subscriber.rb +360 -0
  80. data/lib/active_agent/providers/mock/_types.rb +77 -0
  81. data/lib/active_agent/providers/mock/embedding_request.rb +17 -0
  82. data/lib/active_agent/providers/mock/messages/_types.rb +103 -0
  83. data/lib/active_agent/providers/mock/messages/assistant.rb +26 -0
  84. data/lib/active_agent/providers/mock/messages/base.rb +63 -0
  85. data/lib/active_agent/providers/mock/messages/user.rb +18 -0
  86. data/lib/active_agent/providers/mock/options.rb +30 -0
  87. data/lib/active_agent/providers/mock/request.rb +38 -0
  88. data/lib/active_agent/providers/mock_provider.rb +311 -0
  89. data/lib/active_agent/providers/ollama/_types.rb +5 -0
  90. data/lib/active_agent/providers/ollama/chat/_types.rb +44 -0
  91. data/lib/active_agent/providers/ollama/chat/request.rb +70 -0
  92. data/lib/active_agent/providers/ollama/chat/requests/_types.rb +3 -0
  93. data/lib/active_agent/providers/ollama/chat/requests/messages/_types.rb +116 -0
  94. data/lib/active_agent/providers/ollama/chat/requests/messages/assistant.rb +19 -0
  95. data/lib/active_agent/providers/ollama/chat/requests/messages/user.rb +19 -0
  96. data/lib/active_agent/providers/ollama/embedding/_types.rb +44 -0
  97. data/lib/active_agent/providers/ollama/embedding/request.rb +77 -0
  98. data/lib/active_agent/providers/ollama/embedding/requests/_types.rb +83 -0
  99. data/lib/active_agent/providers/ollama/embedding/requests/options.rb +104 -0
  100. data/lib/active_agent/providers/ollama/options.rb +27 -0
  101. data/lib/active_agent/providers/ollama_provider.rb +95 -0
  102. data/lib/active_agent/providers/open_ai/_base.rb +58 -0
  103. data/lib/active_agent/providers/open_ai/_types.rb +5 -0
  104. data/lib/active_agent/providers/open_ai/chat/_types.rb +44 -0
  105. data/lib/active_agent/providers/open_ai/chat/request.rb +215 -0
  106. data/lib/active_agent/providers/open_ai/chat/requests/_types.rb +229 -0
  107. data/lib/active_agent/providers/open_ai/chat/requests/audio.rb +24 -0
  108. data/lib/active_agent/providers/open_ai/chat/requests/messages/_types.rb +123 -0
  109. data/lib/active_agent/providers/open_ai/chat/requests/messages/assistant.rb +42 -0
  110. data/lib/active_agent/providers/open_ai/chat/requests/messages/base.rb +78 -0
  111. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/_types.rb +133 -0
  112. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/audio.rb +35 -0
  113. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/base.rb +24 -0
  114. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/file.rb +26 -0
  115. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/_types.rb +60 -0
  116. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/details.rb +41 -0
  117. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/image.rb +37 -0
  118. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/refusal.rb +25 -0
  119. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/text.rb +25 -0
  120. data/lib/active_agent/providers/open_ai/chat/requests/messages/developer.rb +25 -0
  121. data/lib/active_agent/providers/open_ai/chat/requests/messages/function.rb +25 -0
  122. data/lib/active_agent/providers/open_ai/chat/requests/messages/system.rb +25 -0
  123. data/lib/active_agent/providers/open_ai/chat/requests/messages/tool.rb +26 -0
  124. data/lib/active_agent/providers/open_ai/chat/requests/messages/user.rb +32 -0
  125. data/lib/active_agent/providers/open_ai/chat/requests/prediction.rb +46 -0
  126. data/lib/active_agent/providers/open_ai/chat/requests/response_format.rb +53 -0
  127. data/lib/active_agent/providers/open_ai/chat/requests/stream_options.rb +24 -0
  128. data/lib/active_agent/providers/open_ai/chat/requests/tool_choice.rb +26 -0
  129. data/lib/active_agent/providers/open_ai/chat/requests/tools/_types.rb +5 -0
  130. data/lib/active_agent/providers/open_ai/chat/requests/tools/base.rb +22 -0
  131. data/lib/active_agent/providers/open_ai/chat/requests/tools/custom_tool.rb +41 -0
  132. data/lib/active_agent/providers/open_ai/chat/requests/tools/function_tool.rb +51 -0
  133. data/lib/active_agent/providers/open_ai/chat/requests/web_search_options.rb +45 -0
  134. data/lib/active_agent/providers/open_ai/chat_provider.rb +198 -0
  135. data/lib/active_agent/providers/open_ai/embedding/_types.rb +45 -0
  136. data/lib/active_agent/providers/open_ai/embedding/request.rb +85 -0
  137. data/lib/active_agent/providers/open_ai/embedding/requests/_types.rb +49 -0
  138. data/lib/active_agent/providers/open_ai/options.rb +74 -0
  139. data/lib/active_agent/providers/open_ai/responses/_types.rb +50 -0
  140. data/lib/active_agent/providers/open_ai/responses/request.rb +163 -0
  141. data/lib/active_agent/providers/open_ai/responses/requests/_types.rb +231 -0
  142. data/lib/active_agent/providers/open_ai/responses/requests/conversation.rb +23 -0
  143. data/lib/active_agent/providers/open_ai/responses/requests/inputs/_types.rb +264 -0
  144. data/lib/active_agent/providers/open_ai/responses/requests/inputs/assistant_message.rb +22 -0
  145. data/lib/active_agent/providers/open_ai/responses/requests/inputs/base.rb +89 -0
  146. data/lib/active_agent/providers/open_ai/responses/requests/inputs/code_interpreter_tool_call.rb +30 -0
  147. data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call.rb +28 -0
  148. data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call_output.rb +33 -0
  149. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/_types.rb +207 -0
  150. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/base.rb +22 -0
  151. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_audio.rb +26 -0
  152. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_file.rb +28 -0
  153. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_image.rb +28 -0
  154. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_text.rb +25 -0
  155. data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call.rb +28 -0
  156. data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call_output.rb +27 -0
  157. data/lib/active_agent/providers/open_ai/responses/requests/inputs/developer_message.rb +20 -0
  158. data/lib/active_agent/providers/open_ai/responses/requests/inputs/file_search_tool_call.rb +25 -0
  159. data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_call_output.rb +32 -0
  160. data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_tool_call.rb +28 -0
  161. data/lib/active_agent/providers/open_ai/responses/requests/inputs/image_gen_tool_call.rb +27 -0
  162. data/lib/active_agent/providers/open_ai/responses/requests/inputs/input_message.rb +31 -0
  163. data/lib/active_agent/providers/open_ai/responses/requests/inputs/item_reference.rb +23 -0
  164. data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call.rb +26 -0
  165. data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call_output.rb +33 -0
  166. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_request.rb +30 -0
  167. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_response.rb +28 -0
  168. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_list_tools.rb +29 -0
  169. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_tool_call.rb +35 -0
  170. data/lib/active_agent/providers/open_ai/responses/requests/inputs/output_message.rb +35 -0
  171. data/lib/active_agent/providers/open_ai/responses/requests/inputs/reasoning.rb +33 -0
  172. data/lib/active_agent/providers/open_ai/responses/requests/inputs/system_message.rb +20 -0
  173. data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_call_base.rb +27 -0
  174. data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_message.rb +23 -0
  175. data/lib/active_agent/providers/open_ai/responses/requests/inputs/user_message.rb +20 -0
  176. data/lib/active_agent/providers/open_ai/responses/requests/inputs/web_search_tool_call.rb +24 -0
  177. data/lib/active_agent/providers/open_ai/responses/requests/prompt_reference.rb +23 -0
  178. data/lib/active_agent/providers/open_ai/responses/requests/reasoning.rb +23 -0
  179. data/lib/active_agent/providers/open_ai/responses/requests/stream_options.rb +20 -0
  180. data/lib/active_agent/providers/open_ai/responses/requests/text/_types.rb +89 -0
  181. data/lib/active_agent/providers/open_ai/responses/requests/text/base.rb +22 -0
  182. data/lib/active_agent/providers/open_ai/responses/requests/text/json_object.rb +20 -0
  183. data/lib/active_agent/providers/open_ai/responses/requests/text/json_schema.rb +48 -0
  184. data/lib/active_agent/providers/open_ai/responses/requests/text/plain.rb +20 -0
  185. data/lib/active_agent/providers/open_ai/responses/requests/text.rb +41 -0
  186. data/lib/active_agent/providers/open_ai/responses/requests/tool_choice.rb +26 -0
  187. data/lib/active_agent/providers/open_ai/responses/requests/tools/_types.rb +112 -0
  188. data/lib/active_agent/providers/open_ai/responses/requests/tools/base.rb +25 -0
  189. data/lib/active_agent/providers/open_ai/responses/requests/tools/code_interpreter_tool.rb +23 -0
  190. data/lib/active_agent/providers/open_ai/responses/requests/tools/computer_tool.rb +27 -0
  191. data/lib/active_agent/providers/open_ai/responses/requests/tools/custom_tool.rb +28 -0
  192. data/lib/active_agent/providers/open_ai/responses/requests/tools/file_search_tool.rb +27 -0
  193. data/lib/active_agent/providers/open_ai/responses/requests/tools/function_tool.rb +29 -0
  194. data/lib/active_agent/providers/open_ai/responses/requests/tools/image_generation_tool.rb +37 -0
  195. data/lib/active_agent/providers/open_ai/responses/requests/tools/local_shell_tool.rb +21 -0
  196. data/lib/active_agent/providers/open_ai/responses/requests/tools/mcp_tool.rb +41 -0
  197. data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_preview_tool.rb +24 -0
  198. data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_tool.rb +25 -0
  199. data/lib/active_agent/providers/open_ai/responses_provider.rb +153 -0
  200. data/lib/active_agent/providers/open_ai/schema.yml +65937 -0
  201. data/lib/active_agent/providers/open_ai_provider.rb +97 -0
  202. data/lib/active_agent/providers/open_router/_types.rb +45 -0
  203. data/lib/active_agent/providers/open_router/options.rb +93 -0
  204. data/lib/active_agent/providers/open_router/request.rb +83 -0
  205. data/lib/active_agent/providers/open_router/requests/_types.rb +198 -0
  206. data/lib/active_agent/providers/open_router/requests/message.rb +1 -0
  207. data/lib/active_agent/providers/open_router/requests/messages/_types.rb +59 -0
  208. data/lib/active_agent/providers/open_router/requests/messages/assistant.rb +20 -0
  209. data/lib/active_agent/providers/open_router/requests/messages/content/_types.rb +97 -0
  210. data/lib/active_agent/providers/open_router/requests/messages/content/file.rb +27 -0
  211. data/lib/active_agent/providers/open_router/requests/messages/content/files/_types.rb +61 -0
  212. data/lib/active_agent/providers/open_router/requests/messages/content/files/details.rb +26 -0
  213. data/lib/active_agent/providers/open_router/requests/messages/user.rb +30 -0
  214. data/lib/active_agent/providers/open_router/requests/plugin.rb +25 -0
  215. data/lib/active_agent/providers/open_router/requests/plugins/_types.rb +46 -0
  216. data/lib/active_agent/providers/open_router/requests/plugins/pdf_config.rb +29 -0
  217. data/lib/active_agent/providers/open_router/requests/prediction.rb +17 -0
  218. data/lib/active_agent/providers/open_router/requests/provider_preferences/_types.rb +44 -0
  219. data/lib/active_agent/providers/open_router/requests/provider_preferences/max_price.rb +30 -0
  220. data/lib/active_agent/providers/open_router/requests/provider_preferences.rb +64 -0
  221. data/lib/active_agent/providers/open_router/requests/response_format.rb +49 -0
  222. data/lib/active_agent/providers/open_router_provider.rb +53 -0
  223. data/lib/active_agent/providers/openai_provider.rb +2 -0
  224. data/lib/active_agent/providers/openrouter_provider.rb +2 -0
  225. data/lib/active_agent/railtie.rb +8 -6
  226. data/lib/active_agent/schema_generator.rb +333 -166
  227. data/lib/active_agent/version.rb +1 -1
  228. data/lib/active_agent.rb +112 -36
  229. data/lib/generators/active_agent/agent/USAGE +78 -0
  230. data/lib/generators/active_agent/{agent_generator.rb → agent/agent_generator.rb} +14 -4
  231. data/lib/generators/active_agent/install/USAGE +25 -0
  232. data/lib/generators/active_agent/{install_generator.rb → install/install_generator.rb} +1 -19
  233. data/lib/generators/active_agent/templates/agent.rb.tt +7 -3
  234. data/lib/generators/active_agent/templates/application_agent.rb.tt +0 -2
  235. data/lib/generators/erb/agent_generator.rb +31 -16
  236. data/lib/generators/erb/templates/instructions.md.erb.tt +3 -0
  237. data/lib/generators/erb/templates/instructions.md.tt +3 -0
  238. data/lib/generators/erb/templates/instructions.text.tt +1 -0
  239. data/lib/generators/erb/templates/message.md.erb.tt +5 -0
  240. data/lib/generators/erb/templates/schema.json.tt +10 -0
  241. data/lib/generators/test_unit/agent_generator.rb +1 -1
  242. data/lib/generators/test_unit/templates/functional_test.rb.tt +4 -2
  243. metadata +320 -65
  244. data/lib/active_agent/action_prompt/action.rb +0 -13
  245. data/lib/active_agent/action_prompt/base.rb +0 -623
  246. data/lib/active_agent/action_prompt/message.rb +0 -126
  247. data/lib/active_agent/action_prompt/prompt.rb +0 -136
  248. data/lib/active_agent/action_prompt.rb +0 -19
  249. data/lib/active_agent/callbacks.rb +0 -33
  250. data/lib/active_agent/generation_provider/anthropic_provider.rb +0 -163
  251. data/lib/active_agent/generation_provider/base.rb +0 -55
  252. data/lib/active_agent/generation_provider/base_adapter.rb +0 -19
  253. data/lib/active_agent/generation_provider/error_handling.rb +0 -167
  254. data/lib/active_agent/generation_provider/log_subscriber.rb +0 -92
  255. data/lib/active_agent/generation_provider/message_formatting.rb +0 -107
  256. data/lib/active_agent/generation_provider/ollama_provider.rb +0 -66
  257. data/lib/active_agent/generation_provider/open_ai_provider.rb +0 -279
  258. data/lib/active_agent/generation_provider/open_router_provider.rb +0 -385
  259. data/lib/active_agent/generation_provider/parameter_builder.rb +0 -119
  260. data/lib/active_agent/generation_provider/response.rb +0 -75
  261. data/lib/active_agent/generation_provider/responses_adapter.rb +0 -44
  262. data/lib/active_agent/generation_provider/stream_processing.rb +0 -58
  263. data/lib/active_agent/generation_provider/tool_management.rb +0 -142
  264. data/lib/active_agent/generation_provider.rb +0 -67
  265. data/lib/active_agent/log_subscriber.rb +0 -44
  266. data/lib/active_agent/parameterized.rb +0 -75
  267. data/lib/active_agent/prompt_helper.rb +0 -19
  268. data/lib/active_agent/queued_generation.rb +0 -12
  269. data/lib/active_agent/rescuable.rb +0 -34
  270. data/lib/active_agent/sanitizers.rb +0 -40
  271. data/lib/active_agent/streaming.rb +0 -34
  272. data/lib/active_agent/test_case.rb +0 -125
  273. data/lib/generators/USAGE +0 -47
  274. data/lib/generators/active_agent/USAGE +0 -56
  275. data/lib/generators/erb/install_generator.rb +0 -44
  276. data/lib/generators/erb/templates/layout.html.erb.tt +0 -1
  277. data/lib/generators/erb/templates/layout.json.erb.tt +0 -1
  278. data/lib/generators/erb/templates/layout.text.erb.tt +0 -1
  279. data/lib/generators/erb/templates/view.html.erb.tt +0 -5
  280. data/lib/generators/erb/templates/view.json.erb.tt +0 -16
  281. /data/lib/active_agent/{preview.rb → concerns/preview.rb} +0 -0
  282. /data/lib/generators/erb/templates/{view.text.erb.tt → message.text.erb.tt} +0 -0
@@ -1,623 +0,0 @@
1
- require "active_agent/collector"
2
- require "active_support/core_ext/string/inflections"
3
- require "active_support/core_ext/hash/except"
4
- require "active_support/core_ext/module/anonymous"
5
- require "active_agent/action_prompt/message"
6
- require "active_agent/action_prompt/action"
7
-
8
- # require "active_agent/log_subscriber"
9
- require "active_agent/rescuable"
10
- module ActiveAgent
11
- module ActionPrompt
12
- class Base < AbstractController::Base
13
- include Callbacks
14
- include GenerationProvider
15
- include Streaming
16
- include QueuedGeneration
17
- include Rescuable
18
- include Parameterized
19
- include Previews
20
- # include FormBuilder
21
-
22
- abstract!
23
-
24
- include AbstractController::Rendering
25
-
26
- include AbstractController::Logger
27
- include AbstractController::Helpers
28
- include AbstractController::Translation
29
- include AbstractController::AssetPaths
30
- include AbstractController::Callbacks
31
- include AbstractController::Caching
32
-
33
- include ActionView::Layouts
34
-
35
- PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [ :@_action_has_layout ]
36
-
37
- helper ActiveAgent::PromptHelper
38
-
39
- # Delegate response to generation_provider for easy access in callbacks
40
- delegate :response, to: :generation_provider, allow_nil: true
41
-
42
- class_attribute :options
43
-
44
- class_attribute :default_params, default: {
45
- mime_version: "1.0",
46
- charset: "UTF-8",
47
- content_type: "text/plain",
48
- parts_order: [ "text/plain", "text/enriched", "text/html" ]
49
- }.freeze
50
-
51
- class << self
52
- # Register one or more Observers which will be notified when prompt is generated.
53
- def register_observers(*observers)
54
- observers.flatten.compact.each { |observer| register_observer(observer) }
55
- end
56
-
57
- # Unregister one or more previously registered Observers.
58
- def unregister_observers(*observers)
59
- observers.flatten.compact.each { |observer| unregister_observer(observer) }
60
- end
61
-
62
- # Register one or more Interceptors which will be called before prompt is sent.
63
- def register_interceptors(*interceptors)
64
- interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
65
- end
66
-
67
- # Unregister one or more previously registered Interceptors.
68
- def unregister_interceptors(*interceptors)
69
- interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) }
70
- end
71
-
72
- # Register an Observer which will be notified when prompt is generated.
73
- # Either a class, string, or symbol can be passed in as the Observer.
74
- # If a string or symbol is passed in it will be camelized and constantized.
75
- def register_observer(observer)
76
- Prompt.register_observer(observer_class_for(observer))
77
- end
78
-
79
- # Unregister a previously registered Observer.
80
- # Either a class, string, or symbol can be passed in as the Observer.
81
- # If a string or symbol is passed in it will be camelized and constantized.
82
- def unregister_observer(observer)
83
- Prompt.unregister_observer(observer_class_for(observer))
84
- end
85
-
86
- # Register an Interceptor which will be called before prompt is sent.
87
- # Either a class, string, or symbol can be passed in as the Interceptor.
88
- # If a string or symbol is passed in it will be camelized and constantized.
89
- def register_interceptor(interceptor)
90
- Prompt.register_interceptor(observer_class_for(interceptor))
91
- end
92
-
93
- # Unregister a previously registered Interceptor.
94
- # Either a class, string, or symbol can be passed in as the Interceptor.
95
- # If a string or symbol is passed in it will be camelized and constantized.
96
- def unregister_interceptor(interceptor)
97
- Prompt.unregister_interceptor(observer_class_for(interceptor))
98
- end
99
-
100
- def observer_class_for(value) # :nodoc:
101
- case value
102
- when String, Symbol
103
- value.to_s.camelize.constantize
104
- else
105
- value
106
- end
107
- end
108
- private :observer_class_for
109
-
110
- # Define how the agent should generate content
111
- def generate_with(provider, **options)
112
- self.generation_provider = provider
113
- if options.has_key?(:instructions) || (self.options || {}).empty?
114
- # Either instructions explicitly provided, or no inherited options exist
115
- self.options = (self.options || {}).merge(options)
116
- else
117
- # Don't inherit instructions from parent if not explicitly set
118
- inherited_options = (self.options || {}).except(:instructions)
119
- self.options = inherited_options.merge(options)
120
- end
121
- end
122
-
123
- def stream_with(&stream)
124
- self.options = (options || {}).merge(stream: stream)
125
- end
126
-
127
- # Returns the name of the current agent. This method is also being used as a path for a view lookup.
128
- # If this is an anonymous agent, this method will return +anonymous+ instead.
129
- def agent_name
130
- @agent_name ||= anonymous? ? "anonymous" : name.underscore
131
- end
132
- # Allows to set the name of current agent.
133
- attr_writer :agent_name
134
- alias_method :controller_path, :agent_name
135
-
136
- # Sets the defaults through app configuration:
137
- #
138
- # config.action_agent.default(from: "no-reply@example.org")
139
- #
140
- # Aliased by ::default_options=
141
- def default(value = nil)
142
- self.default_params = default_params.merge(value).freeze if value
143
- default_params
144
- end
145
- # Allows to set defaults through app configuration:
146
- #
147
- # config.action_agent.default_options = { from: "no-reply@example.org" }
148
- alias_method :default_options=, :default
149
-
150
- # Wraps a prompt generation inside of ActiveSupport::Notifications instrumentation.
151
- #
152
- # This method is actually called by the +ActionPrompt::Prompt+ object itself
153
- # through a callback when you call <tt>:generate_prompt</tt> on the +ActionPrompt::Prompt+,
154
- # calling +generate_prompt+ directly and passing an +ActionPrompt::Prompt+ will do
155
- # nothing except tell the logger you generated the prompt.
156
- def generate_prompt(prompt) # :nodoc:
157
- ActiveSupport::Notifications.instrument("deliver.active_agent") do |payload|
158
- set_payload_for_prompt(payload, prompt)
159
- yield # Let Prompt do the generation actions
160
- end
161
- end
162
-
163
- private
164
-
165
- def set_payload_for_prompt(payload, prompt)
166
- payload[:prompt] = prompt.encoded
167
- payload[:agent] = agent_name
168
- payload[:message_id] = prompt.message_id
169
- payload[:date] = prompt.date
170
- payload[:perform_generations] = prompt.perform_generations
171
- end
172
-
173
- def method_missing(method_name, ...)
174
- if action_methods.include?(method_name.name)
175
- Generation.new(self, method_name, ...)
176
- else
177
- super
178
- end
179
- end
180
-
181
- def respond_to_missing?(method, include_all = false)
182
- action_methods.include?(method.name) || super
183
- end
184
- end
185
-
186
- attr_internal :context
187
-
188
- def agent_stream
189
- proc do |message, delta, stop, action_name|
190
- @_action_name = action_name
191
-
192
- run_stream_callbacks(message, delta, stop) do |message, delta, stop|
193
- yield message, delta, stop if block_given?
194
- end
195
- end
196
- end
197
-
198
- def embed
199
- context.options.merge(options)
200
- generation_provider.embed(context) if context && generation_provider
201
- handle_response(generation_provider.response)
202
- end
203
-
204
- # Add embedding capability to Message class
205
- ActiveAgent::ActionPrompt::Message.class_eval do
206
- def embed
207
- agent_class = ApplicationAgent
208
- agent = agent_class.new
209
- agent.context = ActiveAgent::ActionPrompt::Prompt.new(message: self, agent_instance: agent)
210
- agent.embed
211
- self
212
- end
213
- end
214
-
215
- # Make context accessible for chaining
216
- # attr_accessor :context
217
-
218
- def perform_generation
219
- generation_provider.generate(context) if context && generation_provider
220
- handle_response(generation_provider.response)
221
- end
222
-
223
- def handle_response(response)
224
- return response unless response.message.requested_actions.present?
225
-
226
- # The assistant message with tool_calls is already added by update_context in the provider
227
- # Now perform the requested actions which will add tool response messages
228
- perform_actions(requested_actions: response.message.requested_actions)
229
-
230
- # Continue generation with updated context
231
- continue_generation
232
- end
233
-
234
- def continue_generation
235
- # Continue generating with the updated context that includes tool results
236
- generation_provider.generate(context) if context && generation_provider
237
- handle_response(generation_provider.response)
238
- end
239
-
240
- def update_context(response)
241
- ActiveAgent::GenerationProvider::Response.new(prompt: context)
242
- end
243
-
244
- def perform_actions(requested_actions:)
245
- requested_actions.each do |action|
246
- perform_action(action)
247
- end
248
- end
249
-
250
- def perform_action(action)
251
- # Save the current messages to preserve conversation history
252
- original_messages = context.messages.dup
253
- original_params = context.params || {}
254
-
255
- if action.params.is_a?(Hash)
256
- self.params = original_params.merge(action.params)
257
- else
258
- self.params = original_params
259
- end
260
-
261
- # Save the current prompt_was_called state and reset it so the action can render
262
- original_prompt_was_called = @_prompt_was_called
263
- @_prompt_was_called = false
264
-
265
- # Process the action, which will render the view and populate context
266
- process(action.name)
267
-
268
- # The action should have called prompt which populates context.message
269
- # Create a tool message from the rendered response
270
- tool_message = context.message.dup
271
- tool_message.role = :tool
272
- tool_message.action_id = action.id
273
- tool_message.action_name = action.name
274
- tool_message.generation_id = action.id
275
-
276
- # Restore the messages with the new tool message
277
- context.messages = original_messages + [ tool_message ]
278
-
279
- # Restore the prompt_was_called state
280
- @_prompt_was_called = original_prompt_was_called
281
- end
282
-
283
- def initialize # :nodoc:
284
- super
285
- @_prompt_was_called = false
286
- @_context = ActiveAgent::ActionPrompt::Prompt.new(options: self.class.options&.deep_dup || {}, agent_instance: self)
287
- end
288
-
289
- def process(method_name, *args) # :nodoc:
290
- payload = {
291
- agent: self.class.name,
292
- action: method_name,
293
- args: args
294
- }
295
-
296
- ActiveSupport::Notifications.instrument("process.active_agent", payload) do
297
- super
298
- @_context = ActiveAgent::ActionPrompt::Prompt.new(agent_instance: self) unless @_prompt_was_called
299
- end
300
- end
301
- ruby2_keywords(:process)
302
-
303
- class NullPrompt # :nodoc:
304
- def message
305
- ""
306
- end
307
-
308
- def header
309
- {}
310
- end
311
-
312
- def respond_to?(string, include_all = false)
313
- true
314
- end
315
-
316
- def method_missing(...)
317
- nil
318
- end
319
- end
320
-
321
- # Returns the name of the agent object.
322
- def agent_name
323
- self.class.agent_name
324
- end
325
-
326
- def headers(args = nil)
327
- if args
328
- @_context.headers(args)
329
- else
330
- @_context
331
- end
332
- end
333
-
334
- def prompt_with(*)
335
- context.update_context(*)
336
- end
337
-
338
- def prompt(headers = {}, &block)
339
- return context if @_prompt_was_called && headers.blank? && !block
340
- # Apply option hierarchy: prompt options > agent options > config options
341
- merged_options = merge_options(headers[:options] || {})
342
-
343
- raw_instructions = headers.has_key?(:instructions) ? headers[:instructions] : context.options[:instructions]
344
-
345
- context.instructions = prepare_instructions(raw_instructions)
346
-
347
- context.options.merge!(merged_options)
348
- context.options[:stream] = agent_stream if context.options[:stream]
349
- content_type = headers[:content_type]
350
-
351
- headers = apply_defaults(headers)
352
- context.messages = headers[:messages] || []
353
- context.mcp_servers = headers[:mcp_servers] || []
354
- context.context_id = headers[:context_id]
355
- context.params = params
356
- context.action_name = action_name
357
-
358
- context.output_schema = render_schema(headers[:output_schema], set_prefixes(headers[:output_schema], lookup_context.prefixes))
359
-
360
- context.charset = charset = headers[:charset]
361
-
362
- headers = prepare_message(headers)
363
- # wrap_generation_behavior!(headers[:generation_method], headers[:generation_method_options])
364
- # assign_headers_to_context(context, headers)
365
- responses = collect_responses(headers, &block)
366
-
367
- @_prompt_was_called = true
368
-
369
- create_parts_from_responses(context, responses)
370
-
371
- context.content_type = set_content_type(context, content_type, headers[:content_type])
372
-
373
- context.charset = charset
374
- context.actions = headers[:actions] || action_schemas
375
-
376
- context
377
- end
378
-
379
- def action_methods
380
- super - ActiveAgent::Base.public_instance_methods(false).map(&:to_s) - [ action_name ]
381
- end
382
-
383
- def action_schemas
384
- prefixes = set_prefixes(action_name, lookup_context.prefixes)
385
-
386
- action_methods.map do |action|
387
- render_schema(action, prefixes)
388
- end.compact
389
- end
390
-
391
- private
392
- def prepare_message(headers)
393
- if headers[:message].present? && headers[:message].is_a?(ActiveAgent::ActionPrompt::Message)
394
- headers[:body] = headers[:message].content
395
- headers[:role] = headers[:message].role
396
- elsif headers[:message].present? && headers[:message].is_a?(Array)
397
- # Handle array of multipart content like [{type: "text", text: "..."}, {type: "file", file: {...}}]
398
- headers[:body] = headers[:message]
399
- headers[:role] = :user
400
- elsif headers[:message].present? && headers[:message].is_a?(String)
401
- headers[:body] = headers[:message]
402
- headers[:role] = :user
403
- end
404
- load_input_data(headers)
405
-
406
- headers
407
- end
408
-
409
- def load_input_data(headers)
410
- if headers[:image_data].present?
411
- headers[:body] = [
412
- ActiveAgent::ActionPrompt::Message.new(content: headers[:image_data], content_type: "image_data"),
413
- ActiveAgent::ActionPrompt::Message.new(content: headers[:body], content_type: "input_text")
414
- ]
415
- elsif headers[:file_data].present?
416
- headers[:body] = [
417
- ActiveAgent::ActionPrompt::Message.new(content: headers[:file_data], metadata: { filename: "resume.pdf" }, content_type: "file_data"),
418
- ActiveAgent::ActionPrompt::Message.new(content: headers[:body], content_type: "input_text")
419
- ]
420
- end
421
- headers
422
- end
423
-
424
- def set_prefixes(action, prefixes)
425
- prefixes = lookup_context.prefixes | [ self.class.agent_name ]
426
- end
427
-
428
- def render_schema(schema_or_action, prefixes)
429
- # If it's already a hash (direct schema), return it
430
- return schema_or_action if schema_or_action.is_a?(Hash)
431
-
432
- # Otherwise try to load from template
433
- return unless lookup_context.template_exists?(schema_or_action, prefixes, false, formats: [ :json ])
434
-
435
- JSON.parse render_to_string(locals: { action_name: schema_or_action }, action: schema_or_action, formats: :json)
436
- end
437
-
438
- def merge_options(prompt_options)
439
- config_options = generation_provider&.config || {}
440
- agent_options = (self.class.options || {}).deep_dup # Defensive copy to prevent mutation
441
-
442
- parent_options = self.class.superclass.respond_to?(:options) ? (self.class.superclass.options || {}) : {}
443
-
444
- # Extract runtime options from prompt_options (exclude instructions as it has special template logic)
445
- runtime_options = prompt_options.slice(
446
- :model, :temperature, :max_tokens, :stream, :top_p, :frequency_penalty,
447
- :presence_penalty, :response_format, :seed, :stop, :tools_choice, :plugins,
448
-
449
- # OpenRouter Provider Settings
450
- :data_collection, :require_parameters, :only, :ignore, :quantizations, :sort, :max_price,
451
-
452
- # Built-in Tools Support (OpenAI Responses API)
453
- :tools
454
- )
455
- # Handle explicit options parameter
456
- explicit_options = prompt_options[:options] || {}
457
-
458
- # Merge with proper precedence: config < agent < explicit_options
459
- # Don't include instructions in automatic merging as it has special template fallback logic
460
- config_options_filtered = config_options.except(:instructions)
461
- agent_options_filtered = agent_options.except(:instructions)
462
- explicit_options_filtered = explicit_options.except(:instructions)
463
-
464
- merged = config_options_filtered.merge(agent_options_filtered).merge(explicit_options_filtered)
465
-
466
- # Only merge runtime options that are actually present (not nil)
467
- runtime_options.each do |key, value|
468
- next if value.nil?
469
-
470
- merged[key] = value
471
- end
472
-
473
- merged
474
- end
475
-
476
- def set_content_type(prompt_context, user_content_type, class_default) # :doc:
477
- if user_content_type.present?
478
- user_content_type
479
- elsif context.multimodal?
480
- "multipart/mixed"
481
- elsif prompt_context.body.is_a?(Array)
482
- prompt_context.content_type || class_default
483
- end
484
- end
485
-
486
- # Translates the +subject+ using \Rails I18n class under <tt>[agent_scope, action_name]</tt> scope.
487
- # If it does not find a translation for the +subject+ under the specified scope it will default to a
488
- # humanized version of the <tt>action_name</tt>.
489
- # If the subject has interpolations, you can pass them through the +interpolations+ parameter.
490
- def default_i18n_subject(interpolations = {}) # :doc:
491
- agent_scope = self.class.agent_name.tr("/", ".")
492
- I18n.t(:subject, **interpolations.merge(scope: [ agent_scope, action_name ], default: action_name.humanize))
493
- end
494
-
495
- def apply_defaults(headers)
496
- default_values = self.class.default.except(*headers.keys).transform_values do |value|
497
- compute_default(value)
498
- end
499
-
500
- headers.reverse_merge(default_values)
501
- end
502
-
503
- def compute_default(value)
504
- return value unless value.is_a?(Proc)
505
-
506
- if value.arity == 1
507
- instance_exec(self, &value)
508
- else
509
- instance_exec(&value)
510
- end
511
- end
512
-
513
- def assign_headers_to_context(context, headers)
514
- assignable = headers.except(:parts_order, :content_type, :body, :role, :template_name,
515
- :template_path, :delivery_method, :delivery_method_options)
516
-
517
- assignable.each { |k, v| context.send(k, v) if context.respond_to?(k) }
518
- end
519
-
520
- def collect_responses(headers, &)
521
- if block_given?
522
- collect_responses_from_block(headers, &)
523
- elsif headers[:body]
524
- collect_responses_from_text(headers)
525
- else
526
- collect_responses_from_templates(headers)
527
- end
528
- end
529
-
530
- def collect_responses_from_block(headers)
531
- templates_name = headers[:template_name] || action_name
532
- collector = ActiveAgent::Collector.new(lookup_context) { render(templates_name) }
533
- yield(collector)
534
- collector.responses
535
- end
536
-
537
- def collect_responses_from_text(headers)
538
- [ {
539
- body: headers.delete(:body),
540
- content_type: headers[:content_type] || "text/plain"
541
- } ]
542
- end
543
-
544
- def collect_responses_from_templates(headers)
545
- templates_path = headers[:template_path] || self.class.agent_name
546
- templates_name = headers[:template_name] || action_name
547
- each_template(Array(templates_path), templates_name).map do |template|
548
- next if template.format == :json && headers[:format] != :json
549
- format = template.format || formats.first
550
- {
551
- body: render(template: template, formats: [ format ]),
552
- content_type: Mime[format].to_s
553
- }
554
- end.compact
555
- end
556
-
557
- def each_template(paths, name, &)
558
- templates = lookup_context.find_all(name, paths)
559
- if templates.empty?
560
- raise ActionView::MissingTemplate.new(paths, name, paths, false, "agent")
561
- else
562
- templates.uniq(&:format).each(&)
563
- end
564
- end
565
-
566
- def create_parts_from_responses(context, responses)
567
- if responses.size > 1
568
- responses.each { |r| insert_part(context, r, context.charset) }
569
- else
570
- responses.each { |r| insert_part(context, r, context.charset) }
571
- end
572
- end
573
-
574
- def insert_part(context, response, charset)
575
- message = ActiveAgent::ActionPrompt::Message.new(
576
- content: response[:body],
577
- content_type: response[:content_type],
578
- charset: charset
579
- )
580
- context.add_part(message)
581
- end
582
-
583
- def prepare_instructions(instructions)
584
- case instructions
585
- when Hash
586
- raise ArgumentError, "Expected `:template` key in instructions hash" unless instructions[:template]
587
- return unless lookup_context.exists?(instructions[:template], agent_name, false, [], formats: [ :text ])
588
-
589
- template = lookup_context.find_template(instructions[:template], agent_name, false, [], formats: [ :text ])
590
- render_to_string(template: template.virtual_path, locals: instructions[:locals] || {}, layout: false)
591
- when String
592
- instructions
593
- when NilClass
594
- default_template_name = "instructions"
595
- return unless lookup_context.exists?(default_template_name, agent_name, false, [], formats: [ :text ])
596
-
597
- template = lookup_context.find_template(default_template_name, agent_name, false, [], formats: [ :text ])
598
- render_to_string(template: template.virtual_path, layout: false)
599
- else
600
- raise ArgumentError, "Instructions must be Hash, String or NilClass objects"
601
- end
602
- end
603
-
604
- # This and #instrument_name is for caching instrument
605
- def instrument_payload(key)
606
- {
607
- agent: agent_name,
608
- key: key
609
- }
610
- end
611
-
612
- def instrument_name
613
- "active_agent"
614
- end
615
-
616
- def _protected_ivars
617
- PROTECTED_IVARS
618
- end
619
-
620
- ActiveSupport.run_load_hooks(:active_agent, self)
621
- end
622
- end
623
- end