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,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_agent/providers/common/model"
4
+
5
+ module ActiveAgent
6
+ module Providers
7
+ module Common
8
+ module Messages
9
+ class Base < Common::BaseModel
10
+ attribute :role, :string
11
+
12
+ validates :role, presence: true
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ActiveAgent
6
+ module Providers
7
+ module Common
8
+ module Messages
9
+ # System message - provides instructions and context to the AI
10
+ class System < Base
11
+ attribute :role, :string, as: "system"
12
+ attribute :content, :string # Text content
13
+ attribute :name, :string # Optional name for the system message
14
+
15
+ validates :content, presence: true
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ActiveAgent
6
+ module Providers
7
+ module Common
8
+ module Messages
9
+ # Tool message - messages containing tool call results
10
+ class Tool < Base
11
+ attribute :role, :string, as: "tool"
12
+ attribute :content, :string # Tool result content
13
+ attribute :tool_call_id, :string # ID of the tool call this is responding to
14
+ attribute :name, :string # Optional name of the tool
15
+
16
+ validates :content, presence: true
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
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 a message sent by the user in a conversation
10
+ class User < Base
11
+ attribute :role, :string, as: "user"
12
+ attribute :content, :string
13
+ attribute :name, :string
14
+
15
+ validates :content, presence: true
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,361 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAgent
4
+ module Providers
5
+ module Common
6
+ # BaseModel provides a foundation for structured data models with compressed serialization support.
7
+ #
8
+ # This class extends ActiveModel functionality to provide:
9
+ # - Attribute definition with default values
10
+ # - Compressed hash serialization (excludes default values)
11
+ # - Required attribute tracking
12
+ # - Deep compaction of nested structures
13
+ #
14
+ # == Example
15
+ #
16
+ # class Message < BaseModel
17
+ # attribute :role, :string, as: "user"
18
+ # attribute :type, :string, fallback: "plain/text"
19
+ # attribute :content, :string
20
+ # end
21
+ #
22
+ # message = Message.new(content: "Hello")
23
+ # message.to_h #=> { role: "user", type: "plain/text", content: "Hello" }
24
+ # message.serialize #=> { role: "user", type: "plain/text", content: "Hello" }
25
+ class BaseModel
26
+ include ActiveModel::Model
27
+ include ActiveModel::Attributes
28
+
29
+ # Returns the set of required attribute names that must be included in compressed output.
30
+ #
31
+ # Required attributes are those defined with the +as+ option, which establishes
32
+ # a default value that should always be serialized.
33
+ #
34
+ # @return [Set<String>] set of required attribute names
35
+ def self.required_attributes
36
+ @required_attributes ||= Set.new
37
+ end
38
+
39
+ # Ensures subclasses get their own required_attributes set.
40
+ #
41
+ # @param subclass [Class] the inheriting class
42
+ # @return [void]
43
+ def self.inherited(subclass)
44
+ super
45
+ subclass.instance_variable_set(:@required_attributes, required_attributes.dup)
46
+ end
47
+
48
+ # Defines an attribute on the model with special handling for default values.
49
+ #
50
+ # @param name [Symbol, String] the name of the attribute
51
+ # @param type [Class, nil] the type of the attribute (optional)
52
+ # @param options [Hash] additional options for the attribute
53
+ # @option options [Object] :as A default value that makes the attribute read-only.
54
+ # When set, attempts to assign a different value will raise an ArgumentError.
55
+ # This attribute will be included in the compressed hash representation.
56
+ # @option options [Object] :fallback A default value for the attribute.
57
+ # This attribute will be included in the compressed hash representation.
58
+ #
59
+ # @raise [ArgumentError] if attempting to set a value different from the :as default
60
+ #
61
+ # @example Define a read-only attribute with a default value
62
+ # attribute :role, :string, as: "user"
63
+ #
64
+ # @example Define an attribute with a fallback value
65
+ # attribute :temperature, :float, fallback: 0.7
66
+ #
67
+ # @example Define a regular attribute
68
+ # attribute :messages, :array
69
+ def self.attribute(name, type = nil, **options)
70
+ if options.key?(:as)
71
+ default_value = options.delete(:as)
72
+ super(name, type, default: default_value, **options)
73
+
74
+ # Track this attribute as required (must be included in compressed hash)
75
+ required_attributes << name.to_s
76
+
77
+ define_method("#{name}=") do |value|
78
+ normalized_value = value.is_a?(String) ? value.to_sym : value
79
+ normalized_default = default_value.is_a?(String) ? default_value.to_sym : default_value
80
+
81
+ next if normalized_value == normalized_default
82
+
83
+ raise ArgumentError, "Cannot set '#{name}' attribute (read-only with default value)"
84
+ end
85
+ elsif options.key?(:fallback)
86
+ default_value = options.delete(:fallback)
87
+ super(name, type, default: default_value, **options)
88
+
89
+ # Track this attribute as required (must be included in compressed hash)
90
+ required_attributes << name.to_s
91
+ else
92
+ super(name, type, **options)
93
+ end
94
+ end
95
+
96
+ # Delegates attribute accessors to another object.
97
+ #
98
+ # Creates getter and setter methods that forward to the specified target object.
99
+ # If the target is nil when setting, an empty hash is initialized.
100
+ #
101
+ # @param attributes [Array<Symbol>] attribute names to delegate
102
+ # @param to [Symbol] the target method/attribute name
103
+ #
104
+ # @example
105
+ # delegate_attributes :temperature, :max_tokens, to: :options
106
+ def self.delegate_attributes(*attributes, to:)
107
+ attributes.each do |attribute|
108
+ define_method(attribute) do
109
+ public_send(to)&.public_send(attribute)
110
+ end
111
+
112
+ define_method("#{attribute}=") do |value|
113
+ public_send("#{to}=", {}) if public_send(to).nil?
114
+
115
+ public_send(to).public_send("#{attribute}=", value)
116
+ end
117
+ end
118
+ end
119
+
120
+ # Drops specified attributes by defining no-op setters.
121
+ #
122
+ # This is useful when converting between providers that support different attributes
123
+ # or when dropping attributes during message response to request construction.
124
+ # The attributes can still be read if defined elsewhere, but setting them has no effect.
125
+ #
126
+ # @param attributes [Array<Symbol>] attribute names to drop
127
+ # @return [void]
128
+ #
129
+ # @example
130
+ # drop_attributes :metadata, :extra_info
131
+ def self.drop_attributes(*attributes)
132
+ attributes.each do |attribute|
133
+ define_method("#{attribute}=") do |value|
134
+ # No-Op: Drop the value
135
+ end
136
+ end
137
+ end
138
+
139
+ # Returns all attribute keys including aliases.
140
+ #
141
+ # Combines both the main attribute type keys and any attribute aliases,
142
+ # ensuring all possible attribute names are represented as symbols.
143
+ #
144
+ # @return [Array<Symbol>] array of all attribute keys
145
+ def self.keys
146
+ (attribute_types.keys.map(&:to_sym) | attribute_aliases.keys.map(&:to_sym))
147
+ end
148
+
149
+ # Initializes a new instance with the given attributes.
150
+ #
151
+ # Attributes can be provided as a hash. Hash keys are sorted to prioritize nested
152
+ # objects during initialization for backwards compatibility. A special internal key
153
+ # `__default_values` can be passed to get an instance with only default values
154
+ # without any overrides.
155
+ #
156
+ # @param kwargs [Hash] attributes to initialize the instance with
157
+ # @return [BaseModel] the initialized instance
158
+ #
159
+ # @example
160
+ # Message.new(role: "user", content: "Hello")
161
+ def initialize(kwargs = {})
162
+ # To allow us to get a list of attribute defaults without initialized overrides
163
+ return super(nil) if kwargs.key?(:'__default_values')
164
+
165
+ # Backwards Compatibility: This sorts object construction to the top to protect the assignment
166
+ # of backward compatibility assignments.
167
+ kwargs = kwargs.sort_by { |k, v| v.is_a?(Hash) ? 0 : 1 }.to_h if kwargs.is_a?(Hash)
168
+
169
+ super(kwargs)
170
+ end
171
+
172
+ # Merges the given attributes into the current instance.
173
+ #
174
+ # Only attributes with corresponding setter methods are updated.
175
+ # Keys are symbolized before merging.
176
+ #
177
+ # @param kwargs [Hash] attribute keyword arguments to merge
178
+ # @return [BaseModel] self for method chaining
179
+ def merge!(kwargs = {})
180
+ kwargs.deep_symbolize_keys.each do |key, value|
181
+ public_send("#{key}=", value) if respond_to?("#{key}=")
182
+ end
183
+
184
+ self
185
+ end
186
+
187
+ # Recursively removes nil values and empty collections from a hash.
188
+ #
189
+ # Nested hashes and arrays are processed recursively. Empty hashes and
190
+ # arrays after compaction are also removed.
191
+ #
192
+ # @param kwargs [Hash] hash to compact
193
+ # @return [Hash] compacted hash with nil values and empty collections removed
194
+ #
195
+ # @example
196
+ # deep_compact({ a: 1, b: nil, c: { d: nil, e: 2 } })
197
+ # #=> { a: 1, c: { e: 2 } }
198
+ def deep_compact(kwargs = {})
199
+ kwargs.each_with_object({}) do |(key, value), result|
200
+ compacted_value = case value
201
+ when Hash
202
+ deep_compacted = deep_compact(value)
203
+ deep_compacted unless deep_compacted.empty?
204
+ when Array
205
+ compacted_array = value.map { |v| v.is_a?(Hash) ? deep_compact(v) : v }.compact
206
+ compacted_array unless compacted_array.empty?
207
+ else
208
+ value
209
+ end
210
+
211
+ result[key] = compacted_value unless compacted_value.nil?
212
+ end
213
+ end
214
+
215
+ # Converts the model to a hash representation.
216
+ #
217
+ # Recursively converts nested BaseModel instances and arrays to hashes.
218
+ # Nil values and empty collections are removed via deep_compact.
219
+ #
220
+ # @return [Hash] hash representation of all attributes
221
+ #
222
+ # @example
223
+ # message.to_hash
224
+ # #=> { role: "user", content: "Hello", metadata: { id: 1 } }
225
+ def to_hash
226
+ deep_compact(attribute_names.each_with_object({}) do |name, hash|
227
+ value = public_send(name)
228
+
229
+ hash[name.to_sym] = case value
230
+ when BaseModel then value.to_hash
231
+ when Array then value.map { _1.is_a?(BaseModel) ? _1.to_hash : _1 }
232
+ else
233
+ value
234
+ end
235
+ end)
236
+ end
237
+
238
+ # Alias for {#to_hash}.
239
+ #
240
+ # @return [Hash] hash representation of all attributes
241
+ # @see #to_hash
242
+ def to_h = to_hash
243
+
244
+ # Creates a deep duplicate of the model.
245
+ #
246
+ # Duplicates the model instance and recursively duplicates any array or hash attributes
247
+ # to ensure complete independence from the original object.
248
+ #
249
+ # @return [BaseModel] deep duplicate of the model
250
+ def deep_dup
251
+ dup.tap do |duplicated|
252
+ attribute_names.each do |name|
253
+ value = public_send(name)
254
+ next if value.nil?
255
+
256
+ duplicated.public_send("#{name}=", case value
257
+ when Array
258
+ value.map { |v| v.respond_to?(:deep_dup) ? v.deep_dup : v.dup rescue v }
259
+ when Hash
260
+ value.deep_dup
261
+ when BaseModel
262
+ value.deep_dup
263
+ else
264
+ value.dup rescue value
265
+ end)
266
+ end
267
+ end
268
+ end
269
+
270
+ # Serializes the model using attribute type serializers.
271
+ #
272
+ # Iterates through each attribute and uses its ActiveModel::Type serializer
273
+ # to convert the value to its serialized form. Only non-default values are included,
274
+ # except for required attributes (those defined with `:as` or `:fallback` options).
275
+ # This provides a compressed serialization that respects custom type logic.
276
+ #
277
+ # @return [Hash] serialized representation with non-default and required attributes
278
+ #
279
+ # @example
280
+ # message = Message.new(role: "user", content: "Hello")
281
+ # message.serialize #=> { role: "user", content: "Hello" }
282
+ def serialize
283
+ default_values = self.class.new(__default_values: true).attributes
284
+ required_attrs = self.class.required_attributes
285
+
286
+ deep_compact(attribute_names.each_with_object({}) do |name, hash|
287
+ value = public_send(name)
288
+
289
+ # Always include required attributes (those defined with 'as' option)
290
+ # or attributes that differ from their default value
291
+ next if value == default_values[name] && !required_attrs.include?(name)
292
+
293
+ # Use the attribute's type serializer
294
+ attr_type = self.class.attribute_types[name]
295
+ hash[name.to_sym] = attr_type.serialize(value)
296
+ end)
297
+ end
298
+
299
+ # Returns a string representation for inspection.
300
+ #
301
+ # Provides a readable view of the model showing the class name and non-default attributes
302
+ # in a format similar to standard Ruby object inspection.
303
+ #
304
+ # @return [String] formatted string representation
305
+ # @see #serialize
306
+ #
307
+ # @example
308
+ # message = Message.new(role: "user", content: "Hello")
309
+ # message.inspect
310
+ # #=> "#<Message role: \"user\", content: \"Hello\">"
311
+ def inspect
312
+ attrs = JSON.pretty_generate(serialize, {
313
+ space: " ",
314
+ indent: " ",
315
+ object_nl: "\n",
316
+ array_nl: "\n"
317
+ }).lines.drop(1).join.chomp.sub(/\}\z/, "").strip
318
+
319
+ return "#<#{self.class.name}>" if attrs.empty?
320
+
321
+ "#<#{self.class.name} {\n #{attrs}\n}>"
322
+ end
323
+
324
+ # @see #inspect
325
+ alias_method :to_s, :inspect
326
+
327
+ # Compares two models based on their serialized representations.
328
+ #
329
+ # Uses the serialized hash to compare models, allowing for sorting and equality
330
+ # comparisons based on attribute values rather than object identity.
331
+ #
332
+ # @param other [BaseModel] the model to compare against
333
+ # @return [Integer, nil] -1, 0, 1, or nil if not comparable
334
+ #
335
+ # @example
336
+ # model1 = Message.new(content: "A")
337
+ # model2 = Message.new(content: "B")
338
+ # model1 <=> model2 #=> -1
339
+ def <=>(other)
340
+ serialize <=> other&.serialize
341
+ end
342
+
343
+ # Compares equality based on serialized representation.
344
+ #
345
+ # Two models are equal if their serialized hashes are equal, regardless
346
+ # of object identity. This allows value-based equality comparisons.
347
+ #
348
+ # @param other [BaseModel] the model to compare against
349
+ # @return [Boolean]
350
+ #
351
+ # @example
352
+ # model1 = Message.new(content: "Hello")
353
+ # model2 = Message.new(content: "Hello")
354
+ # model1 == model2 #=> true
355
+ def ==(other)
356
+ serialize == other&.serialize
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "responses/prompt"
4
+ require_relative "responses/embed"
5
+
6
+ module ActiveAgent
7
+ module Providers
8
+ module Common
9
+ PromptResponse = Responses::Prompt
10
+ EmbedResponse = Responses::Embed
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../messages/_types"
4
+
5
+ require_relative "format"
6
+
7
+ module ActiveAgent
8
+ module Providers
9
+ module Common
10
+ module Responses
11
+ module Types
12
+ # Type for Messages array - delegates to the shared common messages type
13
+ class MessagesType < Common::Messages::Types::MessagesType
14
+ end
15
+
16
+ class FormatType < ActiveModel::Type::Value
17
+ def cast(value)
18
+ case value
19
+ when BaseModel
20
+ Responses::Format.new(**value.serialize)
21
+ when Hash
22
+ Responses::Format.new(**value.deep_symbolize_keys)
23
+ when nil
24
+ nil
25
+ else
26
+ raise ArgumentError, "Cannot cast #{value.class} to Format"
27
+ end
28
+ end
29
+
30
+ def serialize(value)
31
+ case value
32
+ when Format
33
+ value.serialize
34
+ when Hash
35
+ value
36
+ when nil
37
+ nil
38
+ else
39
+ raise ArgumentError, "Cannot serialize #{value.class}"
40
+ end
41
+ end
42
+
43
+ def deserialize(value)
44
+ cast(value)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_agent/providers/common/model"
4
+
5
+ module ActiveAgent
6
+ module Providers
7
+ module Common
8
+ module Responses
9
+ # Base response model for provider responses.
10
+ #
11
+ # This class represents the standard response structure from AI providers
12
+ # across different services (OpenAI, Anthropic, etc.). It provides a unified
13
+ # interface for accessing response data, usage statistics, and request context.
14
+ #
15
+ # @abstract Subclass and override {#usage} if provider uses non-standard format
16
+ #
17
+ # @note This is a base class. Use specialized subclasses for specific response types:
18
+ # - {Prompt} for conversational/completion responses with messages
19
+ # - {Embed} for embedding responses with vector data
20
+ #
21
+ # @example Accessing response data
22
+ # response = agent.prompt.generate_now
23
+ # response.success? #=> true
24
+ # response.usage #=> { "prompt_tokens" => 10, "completion_tokens" => 20 }
25
+ # response.total_tokens #=> 30
26
+ #
27
+ # @example Inspecting raw provider data
28
+ # response.raw_request #=> { "model" => "gpt-4", "messages" => [...] }
29
+ # response.raw_response #=> { "id" => "chatcmpl-...", "choices" => [...] }
30
+ #
31
+ # @see Prompt
32
+ # @see Embed
33
+ # @see BaseModel
34
+ class Base < BaseModel
35
+ # @!attribute [r] context
36
+ # The original context that was sent to the provider.
37
+ #
38
+ # Contains structured information about the request including instructions,
39
+ # messages, tools, and other configuration passed to the LLM.
40
+ #
41
+ # @return [Hash] the request context
42
+ attribute :context, writable: false
43
+
44
+ # @!attribute [r] raw_request
45
+ # The most recent request in provider-specific format.
46
+ #
47
+ # Contains the actual API request payload sent to the provider,
48
+ # useful for debugging and logging.
49
+ #
50
+ # @return [Hash] the provider-formatted request
51
+ attribute :raw_request, writable: false
52
+
53
+ # @!attribute [r] raw_response
54
+ # The most recent response in provider-specific format.
55
+ #
56
+ # Contains the raw API response from the provider, including all
57
+ # metadata, usage stats, and provider-specific fields.
58
+ #
59
+ # @return [Hash] the provider-formatted response
60
+ attribute :raw_response, writable: false
61
+
62
+ # Initializes a new response object with deep-duplicated attributes.
63
+ #
64
+ # Deep duplication ensures that the response object maintains its own
65
+ # independent copy of the data, preventing external modifications from
66
+ # affecting the response's internal state.
67
+ #
68
+ # @param kwargs [Hash] response attributes
69
+ # @option kwargs [Hash] :context the original request context
70
+ # @option kwargs [Hash] :raw_request the provider-formatted request
71
+ # @option kwargs [Hash] :raw_response the provider-formatted response
72
+ #
73
+ # @return [Base] the initialized response object
74
+ def initialize(kwargs = {})
75
+ super(kwargs.deep_dup) # Ensure that userland can't fuck with our memory space
76
+ end
77
+
78
+ # Extracts instructions from the context.
79
+ #
80
+ # @return [String, Array<Hash>, nil] the instructions that were sent to the provider
81
+ def instructions
82
+ context[:instructions]
83
+ end
84
+
85
+ # Indicates whether the generation request was successful.
86
+ #
87
+ # @todo Better handling of failure flows
88
+ #
89
+ # @return [Boolean] true if successful, false otherwise
90
+ def success?
91
+ true
92
+ end
93
+
94
+ # Extracts usage statistics from the raw response.
95
+ #
96
+ # Most providers (OpenAI, Anthropic, etc.) return usage data in a
97
+ # standardized format within the response. This method extracts that
98
+ # information for token counting and billing purposes.
99
+ #
100
+ # @return [Hash, nil] usage statistics hash with keys like "prompt_tokens",
101
+ # "completion_tokens", and "total_tokens", or nil if not available
102
+ #
103
+ # @example Usage data structure
104
+ # {
105
+ # "prompt_tokens" => 10,
106
+ # "completion_tokens" => 20,
107
+ # "total_tokens" => 30
108
+ # }
109
+ def usage
110
+ return nil unless raw_response
111
+
112
+ # Most providers store usage in the same format
113
+ if raw_response.is_a?(Hash) && raw_response["usage"]
114
+ raw_response["usage"]
115
+ end
116
+ end
117
+
118
+ # Extracts the number of tokens used in the prompt/input.
119
+ #
120
+ # @return [Integer, nil] number of prompt tokens used, or nil if unavailable
121
+ #
122
+ # @example
123
+ # response.prompt_tokens #=> 10
124
+ def prompt_tokens
125
+ usage&.dig("prompt_tokens")
126
+ end
127
+
128
+ # Extracts the number of tokens used in the completion/output.
129
+ #
130
+ # @return [Integer, nil] number of completion tokens used, or nil if unavailable
131
+ #
132
+ # @example
133
+ # response.completion_tokens #=> 20
134
+ def completion_tokens
135
+ usage&.dig("completion_tokens")
136
+ end
137
+
138
+ # Extracts the total number of tokens used (prompt + completion).
139
+ #
140
+ # @return [Integer, nil] total number of tokens used, or nil if unavailable
141
+ #
142
+ # @example
143
+ # response.total_tokens #=> 30
144
+ def total_tokens
145
+ usage&.dig("total_tokens")
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module ActiveAgent
6
+ module Providers
7
+ module Common
8
+ module Responses
9
+ # Response model for embedding responses
10
+ #
11
+ # This class represents responses from embedding endpoints.
12
+ # It includes the embedding data, the original context, raw API data,
13
+ # and usage statistics.
14
+ #
15
+ # == Example
16
+ #
17
+ # response = EmbedResponse.new(
18
+ # context: context_hash,
19
+ # data: [embedding_array],
20
+ # raw_response: { "usage" => { "prompt_tokens" => 10 } }
21
+ # )
22
+ #
23
+ # response.data #=> [[0.1, 0.2, ...]]
24
+ # response.prompt_tokens #=> 10
25
+ # response.usage #=> { "prompt_tokens" => 10, ... }
26
+ class Embed < Base
27
+ # The embedding data
28
+ attribute :data, writable: false
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end