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,410 @@
1
+ require "active_support/delegation"
2
+
3
+ require_relative "common/response"
4
+ require_relative "concerns/exception_handler"
5
+ require_relative "concerns/previewable"
6
+
7
+ # Maps provider types to their gem dependencies.
8
+ # @private
9
+ GEM_LOADERS = {
10
+ anthropic: [ "anthropic", "~> 1.12", "anthropic" ],
11
+ openai: [ "openai", "~> 0.34", "openai" ]
12
+ }
13
+
14
+ # Loads and requires a provider's gem dependency.
15
+ #
16
+ # @param type [Symbol] provider type (:anthropic, :openai)
17
+ # @param file_name [String] provider file path for error context
18
+ # @return [void]
19
+ # @raise [LoadError] when the required gem is not available
20
+ def require_gem!(type, file_name)
21
+ gem_name, requirement, package_name = GEM_LOADERS.fetch(type)
22
+ provider_name = file_name.split("/").last.delete_suffix(".rb").camelize
23
+
24
+ begin
25
+ gem(gem_name, requirement)
26
+ require(package_name)
27
+ rescue LoadError
28
+ raise LoadError, "The '#{gem_name}' gem is required for #{provider_name}. Please add it to your Gemfile and run `bundle install`."
29
+ end
30
+ end
31
+
32
+ module ActiveAgent
33
+ module Providers
34
+ # Base class for LLM provider integrations.
35
+ #
36
+ # Orchestrates API requests, streaming responses, and multi-turn tool calling.
37
+ # Each provider (OpenAI, Anthropic, etc.) subclasses this to implement
38
+ # provider-specific API interactions.
39
+ #
40
+ # @abstract Subclasses must implement {#api_prompt_execute},
41
+ # {#process_stream_chunk}, {#process_prompt_finished_extract_messages},
42
+ # and {#process_prompt_finished_extract_function_calls}
43
+ class BaseProvider
44
+ extend ActiveSupport::Delegation
45
+
46
+ include ExceptionHandler
47
+ include Previewable
48
+
49
+ class ProvidersError < StandardError; end
50
+
51
+ attr_internal :options, :context, :trace_id, # Setup
52
+ :request, :message_stack, # Runtime
53
+ :stream_broadcaster, :streaming, # Callback (Streams)
54
+ :tools_function # Callback (Tools)
55
+
56
+ # @return [String] provider name extracted from class name (e.g., "Anthropic", "OpenAI")
57
+ def self.service_name
58
+ name.split("::").last.delete_suffix("Provider")
59
+ end
60
+
61
+ # @return [String] module-qualified provider name (e.g., "Anthropic", "OpenAI::Chat")
62
+ def self.tag_name
63
+ name.delete_prefix("ActiveAgent::Providers::").delete_suffix("Provider")
64
+ end
65
+
66
+ # @return [Module] provider's namespace module (e.g., ActiveAgent::Providers::OpenAI)
67
+ def self.namespace
68
+ "#{name.deconstantize}::#{service_name}".safe_constantize
69
+ end
70
+
71
+ # @return [Class] provider's options class
72
+ def self.options_klass
73
+ namespace::Options
74
+ end
75
+
76
+ # @return [ActiveModel::Type::Value] provider-specific request type for prompt casting/serialization
77
+ def self.prompt_request_type
78
+ namespace::RequestType.new
79
+ end
80
+
81
+ # @return [ActiveModel::Type::Value] provider-specific request type for embedding casting/serialization
82
+ # @raise [NotImplementedError] when provider doesn't support embeddings
83
+ def self.embed_request_type
84
+ fail(NotImplementedError)
85
+ end
86
+
87
+ delegate :service_name, :tag_name, :namespace, :options_klass, :prompt_request_type, :embed_request_type, to: :class
88
+
89
+ # Initializes a provider instance.
90
+ #
91
+ # @param kwargs [Hash] configuration and callbacks
92
+ # @option kwargs [Symbol] :service validates against provider's service name
93
+ # @option kwargs [Proc] :stream_broadcaster callback for streaming events (:open, :update, :close)
94
+ # @option kwargs [Proc] :tools_function callback to execute tool/function calls
95
+ # @raise [RuntimeError] when service name doesn't match provider
96
+ def initialize(kwargs = {})
97
+ assert_service!(kwargs.delete(:service))
98
+
99
+ configure_exception_handler(
100
+ exception_handler: kwargs.delete(:exception_handler)
101
+ )
102
+
103
+ self.trace_id = kwargs[:trace_id]
104
+ self.stream_broadcaster = kwargs.delete(:stream_broadcaster)
105
+ self.streaming = false
106
+ self.tools_function = kwargs.delete(:tools_function)
107
+ self.options = options_klass.new(kwargs.extract!(*options_klass.keys))
108
+ self.context = kwargs
109
+ self.message_stack = []
110
+ end
111
+
112
+ # Executes a prompt request with error handling and instrumentation.
113
+ #
114
+ # @return [ActiveAgent::Providers::Common::PromptResponse]
115
+ def prompt
116
+ instrument("prompt_start.provider.active_agent") do
117
+ self.request = prompt_request_type.cast(context.except(:trace_id))
118
+ resolve_prompt
119
+ end
120
+ end
121
+
122
+ # Generates a preview of the prompt without executing the API call.
123
+ #
124
+ # Casts context into a request object and renders it as markdown for inspection.
125
+ #
126
+ # @return [String] markdown-formatted preview
127
+ def preview
128
+ self.request = prompt_request_type.cast(context.except(:trace_id))
129
+ preview_prompt
130
+ end
131
+
132
+ # Executes an embedding request with error handling and instrumentation.
133
+ #
134
+ # Converts text into vector representations for semantic search and similarity operations.
135
+ #
136
+ # @return [ActiveAgent::Providers::Common::EmbedResponse]
137
+ def embed
138
+ instrument("embed_start.provider.active_agent") do
139
+ self.request = embed_request_type.cast(context.except(:trace_id))
140
+ resolve_embed
141
+ end
142
+ end
143
+
144
+ protected
145
+
146
+ # @param name [String, nil]
147
+ # @raise [RuntimeError] when service name doesn't match provider
148
+ def assert_service!(name)
149
+ fail "Unexpected Service Name: #{name} != #{service_name}" if name && name != service_name
150
+ end
151
+
152
+ # Instruments an event for logging and metrics.
153
+ #
154
+ # @param name [String]
155
+ # @param payload [Hash]
156
+ # @yield block to instrument
157
+ # @return [Object] block result
158
+ def instrument(name, payload = {}, &block)
159
+ full_payload = { provider: service_name, provider_module: tag_name, trace_id: }.merge(payload)
160
+ ActiveSupport::Notifications.instrument(name, full_payload, &block)
161
+ end
162
+
163
+ # Orchestrates the complete prompt request lifecycle.
164
+ #
165
+ # Prepares request, executes API call, processes response, and handles
166
+ # recursive tool/function calling until completion.
167
+ #
168
+ # @return [ActiveAgent::Providers::Common::PromptResponse]
169
+ def resolve_prompt
170
+ request = prepare_prompt_request
171
+
172
+ instrument("request_prepared.provider.active_agent", message_count: request.messages.size)
173
+
174
+ # @todo Validate Request
175
+ api_parameters = api_request_build(request, prompt_request_type)
176
+ api_response = instrument("api_call.provider.active_agent", streaming: api_parameters[:stream].present?) do
177
+ with_exception_handling { api_prompt_execute(api_parameters) }
178
+ end
179
+
180
+ process_prompt_finished(api_response.as_json&.deep_symbolize_keys)
181
+ end
182
+
183
+ # Orchestrates the complete embedding request lifecycle.
184
+ #
185
+ # @return [ActiveAgent::Providers::Common::EmbedResponse]
186
+ def resolve_embed
187
+ # @todo Validate Request
188
+ api_parameters = api_request_build(request, embed_request_type)
189
+ api_response = instrument("embed_call.provider.active_agent") do
190
+ with_exception_handling { api_embed_execute(api_parameters) }
191
+ end
192
+
193
+ process_embed_finished(api_response)
194
+ end
195
+
196
+ # Prepares request for next iteration in multi-turn conversation.
197
+ #
198
+ # Appends accumulated messages from message stack and resets buffer for next cycle.
199
+ #
200
+ # @return [Request]
201
+ def prepare_prompt_request
202
+ self.request.messages = [ *request.messages, *message_stack ]
203
+ self.message_stack = []
204
+
205
+ self.request
206
+ end
207
+
208
+ # Builds API request parameters from request object.
209
+ #
210
+ # @param request [Request]
211
+ # @param request_type [ActiveModel::Type::Value] type for serialization
212
+ # @return [Hash]
213
+ def api_request_build(request, request_type)
214
+ parameters = request_type.serialize(request)
215
+ parameters[:stream] = process_stream if request.try(:stream)
216
+
217
+ if options.extra_headers.present?
218
+ parameters[:request_options] = { extra_headers: options.extra_headers }.deep_merge(parameters[:request_options] || {})
219
+ end
220
+
221
+ parameters
222
+ end
223
+
224
+ # @return [Proc] invoked for each response chunk
225
+ def process_stream
226
+ proc do |api_response_chunk|
227
+ process_stream_chunk(api_response_chunk)
228
+ end
229
+ end
230
+
231
+ # Executes prompt request against provider's API.
232
+ #
233
+ # @abstract
234
+ # @param request_parameters [Hash]
235
+ # @return [Object] provider-specific API response
236
+ # @raise [NotImplementedError]
237
+ def api_prompt_execute(parameters)
238
+ instrument("api_request.provider.active_agent", model: parameters[:model], streaming: !!parameters[:stream])
239
+
240
+ unless parameters[:stream]
241
+ api_prompt_executer.create(**parameters)
242
+ else
243
+ api_prompt_executer.stream(**parameters.except(:stream)).each(&parameters[:stream])
244
+ nil
245
+ end
246
+ end
247
+
248
+ # Returns provider-specific API executer for prompt requests.
249
+ #
250
+ # Since all currently implemented providers use stainless gems, subclasses
251
+ # only need to override endpoint selection.
252
+ #
253
+ # @abstract
254
+ # @return [Object] provider-specific API client
255
+ # @raise [NotImplementedError]
256
+ def api_prompt_executer
257
+ fail NotImplementedError, "Subclass expected to implement"
258
+ end
259
+
260
+ # Executes embedding request against provider's API.
261
+ #
262
+ # @abstract
263
+ # @param request_parameters [Hash]
264
+ # @return [Object] provider-specific embedding response
265
+ # @raise [NotImplementedError]
266
+ def api_embed_execute(request_parameters)
267
+ fail NotImplementedError, "Subclass expected to implement"
268
+ end
269
+
270
+ # Processes a single streaming response chunk.
271
+ #
272
+ # @abstract
273
+ # @param api_response_chunk [Object] provider-specific chunk format
274
+ # @raise [NotImplementedError]
275
+ def process_stream_chunk(api_response_chunk)
276
+ fail NotImplementedError, "Subclass expected to implement"
277
+ end
278
+
279
+ # Broadcasts stream open event.
280
+ #
281
+ # Fires once per request cycle even during multi-turn tool calling.
282
+ #
283
+ # @return [void]
284
+ def broadcast_stream_open
285
+ return if streaming
286
+ self.streaming = true
287
+
288
+ instrument("stream_open.provider.active_agent")
289
+ stream_broadcaster.call(nil, nil, :open)
290
+ end
291
+
292
+ # Broadcasts stream update with message content delta.
293
+ #
294
+ # @param message [Hash, Object]
295
+ # @param delta [String, nil] incremental content chunk
296
+ # @return [void]
297
+ def broadcast_stream_update(message, delta = nil)
298
+ stream_broadcaster.call(message, delta, :update)
299
+ end
300
+
301
+ # Broadcasts stream close event.
302
+ #
303
+ # Fires once per request cycle even during multi-turn tool calling.
304
+ #
305
+ # @return [void]
306
+ def broadcast_stream_close
307
+ return unless streaming
308
+ self.streaming = false
309
+
310
+ instrument("stream_close.provider.active_agent")
311
+ stream_broadcaster.call(message_stack.last, nil, :close)
312
+ end
313
+
314
+ # Processes completed API response and handles tool calling recursion.
315
+ #
316
+ # Extracts messages and function calls from the response. If tools were invoked,
317
+ # executes them and recursively continues the prompt until completion.
318
+ #
319
+ # @param api_response [Object, nil] provider-specific response
320
+ # @return [Common::PromptResponse, nil]
321
+ def process_prompt_finished(api_response = nil)
322
+ if (api_messages = process_prompt_finished_extract_messages(api_response))
323
+ instrument("messages_extracted.provider.active_agent", message_count: api_messages.size)
324
+ message_stack.push(*api_messages)
325
+ end
326
+
327
+ if (tool_calls = process_prompt_finished_extract_function_calls)&.any?
328
+ instrument("tool_calls_processing.provider.active_agent", tool_count: tool_calls.size)
329
+ process_function_calls(tool_calls)
330
+
331
+ instrument("multi_turn_continue.provider.active_agent")
332
+ resolve_prompt
333
+ else
334
+
335
+ # During a multi iteration process, we will internally open/close the stream
336
+ # with the provider, but this should all look like one big stream to the agents
337
+ # as they continue to work.
338
+ broadcast_stream_close
339
+
340
+ instrument("prompt_complete.provider.active_agent", message_count: message_stack.size)
341
+
342
+ # To convert the messages into common format we first need to merge the current
343
+ # stack and then cast them to the provider type, so we can cast them out to common.
344
+ messages = prompt_request_type.cast(
345
+ messages: [ *request.messages, *message_stack ]
346
+ ).messages
347
+
348
+ # This will returned as it closes up the recursive stack
349
+ Common::PromptResponse.new(
350
+ context:,
351
+ raw_request: prompt_request_type.serialize(request),
352
+ raw_response: api_response,
353
+ messages:,
354
+ format: request.response_format
355
+ )
356
+ end
357
+ end
358
+
359
+ # Extracts messages from API response.
360
+ #
361
+ # @abstract
362
+ # @param api_response [Object]
363
+ # @return [Array<Message>, nil]
364
+ # @raise [NotImplementedError]
365
+ def process_prompt_finished_extract_messages(api_response)
366
+ fail NotImplementedError, "Subclass expected to implement"
367
+ end
368
+
369
+ # Extracts tool/function calls from API response.
370
+ #
371
+ # @abstract
372
+ # @return [Array<Hash>, nil]
373
+ # @raise [NotImplementedError]
374
+ def process_prompt_finished_extract_function_calls
375
+ fail NotImplementedError, "Subclass expected to implement"
376
+ end
377
+
378
+ # @param api_response [Hash]
379
+ # @return [Common::EmbedResponse]
380
+ def process_embed_finished(api_response)
381
+ Common::EmbedResponse.new(
382
+ context:,
383
+ raw_request: embed_request_type.serialize(request),
384
+ raw_response: api_response,
385
+ data: process_embed_finished_data(api_response)
386
+ )
387
+ end
388
+
389
+ # Extracts embedding vectors from API response.
390
+ #
391
+ # Handles both list and single embedding response formats:
392
+ # - List: `{ "data": [{ "embedding": [...] }] }`
393
+ # - Single: `{ "embedding": [...] }`
394
+ #
395
+ # @param api_response [Hash]
396
+ # @return [Array<Hash>] embedding objects with :index, :object, :embedding keys
397
+ # @raise [RuntimeError] when response format is unexpected
398
+ def process_embed_finished_data(api_response)
399
+ case (type = api_response[:object].to_sym)
400
+ when :list
401
+ api_response[:data]
402
+ when :embedding
403
+ [ { index: 0 }.merge(api_response.slice(:index, :object, :embedding)) ]
404
+ else
405
+ fail "Unexpected Embed Object Type: #{type}"
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "requests/_types"
4
+
5
+ require_relative "options"
6
+ require_relative "request"
7
+
8
+ module ActiveAgent
9
+ module Providers
10
+ module Anthropic
11
+ # ActiveModel type for casting and serializing Anthropic Request objects.
12
+ #
13
+ # Handles conversion between Hash, Request, and serialized formats for API calls.
14
+ class RequestType < ActiveModel::Type::Value
15
+ # Casts input to Request object.
16
+ #
17
+ # @param value [Request, Hash, nil]
18
+ # @return [Request, nil]
19
+ # @raise [ArgumentError] when value cannot be cast to Request
20
+ def cast(value)
21
+ case value
22
+ when Request
23
+ value
24
+ when Hash
25
+ Request.new(**value.deep_symbolize_keys)
26
+ when nil
27
+ nil
28
+ else
29
+ raise ArgumentError, "Cannot cast #{value.class} to Request"
30
+ end
31
+ end
32
+
33
+ # Serializes Request to Hash for API submission.
34
+ #
35
+ # Removes `:response_format` key as it's a simulated feature not directly
36
+ # supported by Anthropic's API.
37
+ #
38
+ # @param value [Request, Hash, nil]
39
+ # @return [Hash, nil]
40
+ # @raise [ArgumentError] when value cannot be serialized
41
+ def serialize(value)
42
+ case value
43
+ when Request
44
+ # Response Format is a simulated feature, not directly supported by API
45
+ value.serialize.except(:response_format)
46
+ when Hash
47
+ value
48
+ when nil
49
+ nil
50
+ else
51
+ raise ArgumentError, "Cannot serialize #{value.class}"
52
+ end
53
+ end
54
+
55
+ # @param value [Object]
56
+ # @return [Request, nil]
57
+ def deserialize(value)
58
+ cast(value)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_agent/providers/common/model"
4
+
5
+ module ActiveAgent
6
+ module Providers
7
+ module Anthropic
8
+ class Options < Common::BaseModel
9
+ attribute :api_key, :string
10
+ attribute :base_url, :string, default: "https://api.anthropic.com"
11
+
12
+ attribute :anthropic_beta, :string
13
+
14
+ attribute :max_retries, :integer, default: ::Anthropic::Client::DEFAULT_MAX_RETRIES
15
+ attribute :timeout, :float, default: ::Anthropic::Client::DEFAULT_TIMEOUT_IN_SECONDS
16
+ attribute :initial_retry_delay, :float, default: ::Anthropic::Client::DEFAULT_INITIAL_RETRY_DELAY
17
+ attribute :max_retry_delay, :float, default: ::Anthropic::Client::DEFAULT_MAX_RETRY_DELAY
18
+
19
+ # Common Interface Compatibility
20
+ alias_attribute :access_token, :api_key
21
+
22
+ def initialize(kwargs = {})
23
+ kwargs = kwargs.deep_symbolize_keys if kwargs.respond_to?(:deep_symbolize_keys)
24
+
25
+ super(**deep_compact(kwargs.except(:default_url_options).merge(
26
+ api_key: kwargs[:api_key] || resolve_access_token(kwargs),
27
+ )))
28
+ end
29
+
30
+ def serialize
31
+ super.except(:anthropic_beta).tap do |hash|
32
+ hash[:extra_headers] = extra_headers unless extra_headers.blank?
33
+ end
34
+ end
35
+
36
+ def extra_headers
37
+ deep_compact(
38
+ "anthropic-beta" => anthropic_beta.presence,
39
+ )
40
+ end
41
+
42
+ private
43
+
44
+ def resolve_access_token(kwargs)
45
+ kwargs[:api_key] ||
46
+ kwargs[:access_token] ||
47
+ ENV["ANTHROPIC_ACCESS_TOKEN"] ||
48
+ ENV["ANTHROPIC_API_KEY"]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_agent/providers/common/model"
4
+ require_relative "_types"
5
+
6
+ module ActiveAgent
7
+ module Providers
8
+ module Anthropic
9
+ class Request < Common::BaseModel
10
+ # Required parameters
11
+ attribute :model, :string
12
+ attribute :messages, Requests::Messages::MessagesType.new
13
+ attribute :max_tokens, :integer, fallback: 4096
14
+
15
+ # Optional parameters - Prompting
16
+ attribute :system, Requests::Messages::SystemType.new
17
+ attribute :temperature, :float
18
+ attribute :top_k, :integer
19
+ attribute :top_p, :float
20
+ attribute :stop_sequences, default: -> { [] } # Array of strings
21
+
22
+ # Optional parameters - Tools
23
+ attribute :tools # Array of tool definitions
24
+ attribute :tool_choice, Requests::ToolChoice::ToolChoiceType.new
25
+
26
+ # Optional parameters - Thinking
27
+ attribute :thinking, Requests::ThinkingConfig::ThinkingConfigType.new
28
+
29
+ # Optional parameters - Streaming
30
+ attribute :stream, :boolean, default: false
31
+
32
+ # Optional parameters - Metadata
33
+ attribute :metadata, Requests::MetadataType.new
34
+
35
+ # Optional parameters - Context Management
36
+ attribute :context_management, Requests::ContextManagementConfigType.new
37
+
38
+ # Optional parameters - Container
39
+ attribute :container, Requests::ContainerParamsType.new
40
+
41
+ # Optional parameters - Service tier
42
+ attribute :service_tier, :string
43
+
44
+ # Optional parameters - MCP Servers
45
+ attribute :mcp_servers, default: -> { [] } # Array of MCP server definitions
46
+
47
+ # Common Format Compatibility
48
+ attribute :response_format, Requests::ResponseFormatType.new
49
+
50
+ # Validations for required fields
51
+ validates :model, :messages, :max_tokens, presence: true
52
+
53
+ # Validations for numeric parameters
54
+ validates :max_tokens, numericality: { greater_than_or_equal_to: 1 }, allow_nil: true
55
+ validates :temperature, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
56
+ validates :top_k, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
57
+ validates :top_p, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
58
+
59
+ # Validations for specific values
60
+ validates :service_tier, inclusion: { in: %w[auto standard_only] }, allow_nil: true
61
+
62
+ # Custom validations
63
+ validate :validate_stop_sequences
64
+ validate :validate_tools_format
65
+ validate :validate_mcp_servers_format
66
+
67
+ # Common Format Compatibility
68
+ alias_attribute :instructions, :system
69
+
70
+ # Handle merging in the common format
71
+ def message=(value)
72
+ self.messages ||= []
73
+ self.messages << Requests::Messages::MessageType.new.cast(value)
74
+ end
75
+
76
+ private
77
+
78
+ def validate_stop_sequences
79
+ return if stop_sequences.nil? || stop_sequences.empty?
80
+
81
+ unless stop_sequences.is_a?(Array)
82
+ errors.add(:stop_sequences, "must be an array")
83
+ end
84
+ end
85
+
86
+ def validate_tools_format
87
+ return if tools.nil?
88
+
89
+ unless tools.is_a?(Array)
90
+ errors.add(:tools, "must be an array")
91
+ end
92
+ end
93
+
94
+ def validate_mcp_servers_format
95
+ return if mcp_servers.nil? || mcp_servers.empty?
96
+
97
+ unless mcp_servers.is_a?(Array)
98
+ errors.add(:mcp_servers, "must be an array")
99
+ return
100
+ end
101
+
102
+ if mcp_servers.length > 20
103
+ errors.add(:mcp_servers, "can have at most 20 servers")
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end