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