activeagent 0.6.3 → 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +210 -2
  3. data/README.md +15 -24
  4. data/lib/active_agent/base.rb +389 -39
  5. data/lib/active_agent/concerns/callbacks.rb +251 -0
  6. data/lib/active_agent/concerns/observers.rb +147 -0
  7. data/lib/active_agent/concerns/parameterized.rb +292 -0
  8. data/lib/active_agent/concerns/provider.rb +120 -0
  9. data/lib/active_agent/concerns/queueing.rb +36 -0
  10. data/lib/active_agent/concerns/rescue.rb +64 -0
  11. data/lib/active_agent/concerns/streaming.rb +282 -0
  12. data/lib/active_agent/concerns/tooling.rb +23 -0
  13. data/lib/active_agent/concerns/view.rb +150 -0
  14. data/lib/active_agent/configuration.rb +442 -20
  15. data/lib/active_agent/generation.rb +141 -47
  16. data/lib/active_agent/generation_provider/open_router/types.rb +505 -0
  17. data/lib/active_agent/generation_provider/xai_provider.rb +144 -0
  18. data/lib/active_agent/providers/_base_provider.rb +410 -0
  19. data/lib/active_agent/providers/anthropic/_types.rb +63 -0
  20. data/lib/active_agent/providers/anthropic/options.rb +53 -0
  21. data/lib/active_agent/providers/anthropic/request.rb +109 -0
  22. data/lib/active_agent/providers/anthropic/requests/_types.rb +190 -0
  23. data/lib/active_agent/providers/anthropic/requests/container_params.rb +19 -0
  24. data/lib/active_agent/providers/anthropic/requests/content/base.rb +21 -0
  25. data/lib/active_agent/providers/anthropic/requests/content/sources/base.rb +22 -0
  26. data/lib/active_agent/providers/anthropic/requests/context_management_config.rb +18 -0
  27. data/lib/active_agent/providers/anthropic/requests/messages/_types.rb +189 -0
  28. data/lib/active_agent/providers/anthropic/requests/messages/assistant.rb +23 -0
  29. data/lib/active_agent/providers/anthropic/requests/messages/base.rb +63 -0
  30. data/lib/active_agent/providers/anthropic/requests/messages/content/_types.rb +143 -0
  31. data/lib/active_agent/providers/anthropic/requests/messages/content/base.rb +21 -0
  32. data/lib/active_agent/providers/anthropic/requests/messages/content/document.rb +26 -0
  33. data/lib/active_agent/providers/anthropic/requests/messages/content/image.rb +23 -0
  34. data/lib/active_agent/providers/anthropic/requests/messages/content/redacted_thinking.rb +21 -0
  35. data/lib/active_agent/providers/anthropic/requests/messages/content/search_result.rb +27 -0
  36. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/_types.rb +171 -0
  37. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/base.rb +22 -0
  38. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_base64.rb +25 -0
  39. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_file.rb +23 -0
  40. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_text.rb +25 -0
  41. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/document_url.rb +23 -0
  42. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_base64.rb +27 -0
  43. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_file.rb +23 -0
  44. data/lib/active_agent/providers/anthropic/requests/messages/content/sources/image_url.rb +23 -0
  45. data/lib/active_agent/providers/anthropic/requests/messages/content/text.rb +22 -0
  46. data/lib/active_agent/providers/anthropic/requests/messages/content/thinking.rb +23 -0
  47. data/lib/active_agent/providers/anthropic/requests/messages/content/tool_result.rb +24 -0
  48. data/lib/active_agent/providers/anthropic/requests/messages/content/tool_use.rb +28 -0
  49. data/lib/active_agent/providers/anthropic/requests/messages/user.rb +21 -0
  50. data/lib/active_agent/providers/anthropic/requests/metadata.rb +18 -0
  51. data/lib/active_agent/providers/anthropic/requests/response_format.rb +22 -0
  52. data/lib/active_agent/providers/anthropic/requests/thinking_config/_types.rb +60 -0
  53. data/lib/active_agent/providers/anthropic/requests/thinking_config/base.rb +20 -0
  54. data/lib/active_agent/providers/anthropic/requests/thinking_config/disabled.rb +16 -0
  55. data/lib/active_agent/providers/anthropic/requests/thinking_config/enabled.rb +20 -0
  56. data/lib/active_agent/providers/anthropic/requests/tool_choice/_types.rb +78 -0
  57. data/lib/active_agent/providers/anthropic/requests/tool_choice/any.rb +17 -0
  58. data/lib/active_agent/providers/anthropic/requests/tool_choice/auto.rb +17 -0
  59. data/lib/active_agent/providers/anthropic/requests/tool_choice/base.rb +20 -0
  60. data/lib/active_agent/providers/anthropic/requests/tool_choice/none.rb +16 -0
  61. data/lib/active_agent/providers/anthropic/requests/tool_choice/tool.rb +20 -0
  62. data/lib/active_agent/providers/anthropic_provider.rb +211 -0
  63. data/lib/active_agent/providers/common/messages/_types.rb +124 -0
  64. data/lib/active_agent/providers/common/messages/assistant.rb +57 -0
  65. data/lib/active_agent/providers/common/messages/base.rb +17 -0
  66. data/lib/active_agent/providers/common/messages/system.rb +20 -0
  67. data/lib/active_agent/providers/common/messages/tool.rb +21 -0
  68. data/lib/active_agent/providers/common/messages/user.rb +20 -0
  69. data/lib/active_agent/providers/common/model.rb +361 -0
  70. data/lib/active_agent/providers/common/response.rb +13 -0
  71. data/lib/active_agent/providers/common/responses/_types.rb +51 -0
  72. data/lib/active_agent/providers/common/responses/base.rb +151 -0
  73. data/lib/active_agent/providers/common/responses/embed.rb +33 -0
  74. data/lib/active_agent/providers/common/responses/format.rb +31 -0
  75. data/lib/active_agent/providers/common/responses/message.rb +3 -0
  76. data/lib/active_agent/providers/common/responses/prompt.rb +42 -0
  77. data/lib/active_agent/providers/concerns/exception_handler.rb +72 -0
  78. data/lib/active_agent/providers/concerns/previewable.rb +150 -0
  79. data/lib/active_agent/providers/log_subscriber.rb +360 -0
  80. data/lib/active_agent/providers/mock/_types.rb +77 -0
  81. data/lib/active_agent/providers/mock/embedding_request.rb +17 -0
  82. data/lib/active_agent/providers/mock/messages/_types.rb +103 -0
  83. data/lib/active_agent/providers/mock/messages/assistant.rb +26 -0
  84. data/lib/active_agent/providers/mock/messages/base.rb +63 -0
  85. data/lib/active_agent/providers/mock/messages/user.rb +18 -0
  86. data/lib/active_agent/providers/mock/options.rb +30 -0
  87. data/lib/active_agent/providers/mock/request.rb +38 -0
  88. data/lib/active_agent/providers/mock_provider.rb +311 -0
  89. data/lib/active_agent/providers/ollama/_types.rb +5 -0
  90. data/lib/active_agent/providers/ollama/chat/_types.rb +44 -0
  91. data/lib/active_agent/providers/ollama/chat/request.rb +70 -0
  92. data/lib/active_agent/providers/ollama/chat/requests/_types.rb +3 -0
  93. data/lib/active_agent/providers/ollama/chat/requests/messages/_types.rb +116 -0
  94. data/lib/active_agent/providers/ollama/chat/requests/messages/assistant.rb +19 -0
  95. data/lib/active_agent/providers/ollama/chat/requests/messages/user.rb +19 -0
  96. data/lib/active_agent/providers/ollama/embedding/_types.rb +44 -0
  97. data/lib/active_agent/providers/ollama/embedding/request.rb +77 -0
  98. data/lib/active_agent/providers/ollama/embedding/requests/_types.rb +83 -0
  99. data/lib/active_agent/providers/ollama/embedding/requests/options.rb +104 -0
  100. data/lib/active_agent/providers/ollama/options.rb +27 -0
  101. data/lib/active_agent/providers/ollama_provider.rb +95 -0
  102. data/lib/active_agent/providers/open_ai/_base.rb +58 -0
  103. data/lib/active_agent/providers/open_ai/_types.rb +5 -0
  104. data/lib/active_agent/providers/open_ai/chat/_types.rb +44 -0
  105. data/lib/active_agent/providers/open_ai/chat/request.rb +215 -0
  106. data/lib/active_agent/providers/open_ai/chat/requests/_types.rb +229 -0
  107. data/lib/active_agent/providers/open_ai/chat/requests/audio.rb +24 -0
  108. data/lib/active_agent/providers/open_ai/chat/requests/messages/_types.rb +123 -0
  109. data/lib/active_agent/providers/open_ai/chat/requests/messages/assistant.rb +42 -0
  110. data/lib/active_agent/providers/open_ai/chat/requests/messages/base.rb +78 -0
  111. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/_types.rb +133 -0
  112. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/audio.rb +35 -0
  113. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/base.rb +24 -0
  114. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/file.rb +26 -0
  115. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/_types.rb +60 -0
  116. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/files/details.rb +41 -0
  117. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/image.rb +37 -0
  118. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/refusal.rb +25 -0
  119. data/lib/active_agent/providers/open_ai/chat/requests/messages/content/text.rb +25 -0
  120. data/lib/active_agent/providers/open_ai/chat/requests/messages/developer.rb +25 -0
  121. data/lib/active_agent/providers/open_ai/chat/requests/messages/function.rb +25 -0
  122. data/lib/active_agent/providers/open_ai/chat/requests/messages/system.rb +25 -0
  123. data/lib/active_agent/providers/open_ai/chat/requests/messages/tool.rb +26 -0
  124. data/lib/active_agent/providers/open_ai/chat/requests/messages/user.rb +32 -0
  125. data/lib/active_agent/providers/open_ai/chat/requests/prediction.rb +46 -0
  126. data/lib/active_agent/providers/open_ai/chat/requests/response_format.rb +53 -0
  127. data/lib/active_agent/providers/open_ai/chat/requests/stream_options.rb +24 -0
  128. data/lib/active_agent/providers/open_ai/chat/requests/tool_choice.rb +26 -0
  129. data/lib/active_agent/providers/open_ai/chat/requests/tools/_types.rb +5 -0
  130. data/lib/active_agent/providers/open_ai/chat/requests/tools/base.rb +22 -0
  131. data/lib/active_agent/providers/open_ai/chat/requests/tools/custom_tool.rb +41 -0
  132. data/lib/active_agent/providers/open_ai/chat/requests/tools/function_tool.rb +51 -0
  133. data/lib/active_agent/providers/open_ai/chat/requests/web_search_options.rb +45 -0
  134. data/lib/active_agent/providers/open_ai/chat_provider.rb +198 -0
  135. data/lib/active_agent/providers/open_ai/embedding/_types.rb +45 -0
  136. data/lib/active_agent/providers/open_ai/embedding/request.rb +85 -0
  137. data/lib/active_agent/providers/open_ai/embedding/requests/_types.rb +49 -0
  138. data/lib/active_agent/providers/open_ai/options.rb +74 -0
  139. data/lib/active_agent/providers/open_ai/responses/_types.rb +50 -0
  140. data/lib/active_agent/providers/open_ai/responses/request.rb +163 -0
  141. data/lib/active_agent/providers/open_ai/responses/requests/_types.rb +231 -0
  142. data/lib/active_agent/providers/open_ai/responses/requests/conversation.rb +23 -0
  143. data/lib/active_agent/providers/open_ai/responses/requests/inputs/_types.rb +264 -0
  144. data/lib/active_agent/providers/open_ai/responses/requests/inputs/assistant_message.rb +22 -0
  145. data/lib/active_agent/providers/open_ai/responses/requests/inputs/base.rb +89 -0
  146. data/lib/active_agent/providers/open_ai/responses/requests/inputs/code_interpreter_tool_call.rb +30 -0
  147. data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call.rb +28 -0
  148. data/lib/active_agent/providers/open_ai/responses/requests/inputs/computer_tool_call_output.rb +33 -0
  149. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/_types.rb +207 -0
  150. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/base.rb +22 -0
  151. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_audio.rb +26 -0
  152. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_file.rb +28 -0
  153. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_image.rb +28 -0
  154. data/lib/active_agent/providers/open_ai/responses/requests/inputs/content/input_text.rb +25 -0
  155. data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call.rb +28 -0
  156. data/lib/active_agent/providers/open_ai/responses/requests/inputs/custom_tool_call_output.rb +27 -0
  157. data/lib/active_agent/providers/open_ai/responses/requests/inputs/developer_message.rb +20 -0
  158. data/lib/active_agent/providers/open_ai/responses/requests/inputs/file_search_tool_call.rb +25 -0
  159. data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_call_output.rb +32 -0
  160. data/lib/active_agent/providers/open_ai/responses/requests/inputs/function_tool_call.rb +28 -0
  161. data/lib/active_agent/providers/open_ai/responses/requests/inputs/image_gen_tool_call.rb +27 -0
  162. data/lib/active_agent/providers/open_ai/responses/requests/inputs/input_message.rb +31 -0
  163. data/lib/active_agent/providers/open_ai/responses/requests/inputs/item_reference.rb +23 -0
  164. data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call.rb +26 -0
  165. data/lib/active_agent/providers/open_ai/responses/requests/inputs/local_shell_tool_call_output.rb +33 -0
  166. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_request.rb +30 -0
  167. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_approval_response.rb +28 -0
  168. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_list_tools.rb +29 -0
  169. data/lib/active_agent/providers/open_ai/responses/requests/inputs/mcp_tool_call.rb +35 -0
  170. data/lib/active_agent/providers/open_ai/responses/requests/inputs/output_message.rb +35 -0
  171. data/lib/active_agent/providers/open_ai/responses/requests/inputs/reasoning.rb +33 -0
  172. data/lib/active_agent/providers/open_ai/responses/requests/inputs/system_message.rb +20 -0
  173. data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_call_base.rb +27 -0
  174. data/lib/active_agent/providers/open_ai/responses/requests/inputs/tool_message.rb +23 -0
  175. data/lib/active_agent/providers/open_ai/responses/requests/inputs/user_message.rb +20 -0
  176. data/lib/active_agent/providers/open_ai/responses/requests/inputs/web_search_tool_call.rb +24 -0
  177. data/lib/active_agent/providers/open_ai/responses/requests/prompt_reference.rb +23 -0
  178. data/lib/active_agent/providers/open_ai/responses/requests/reasoning.rb +23 -0
  179. data/lib/active_agent/providers/open_ai/responses/requests/stream_options.rb +20 -0
  180. data/lib/active_agent/providers/open_ai/responses/requests/text/_types.rb +89 -0
  181. data/lib/active_agent/providers/open_ai/responses/requests/text/base.rb +22 -0
  182. data/lib/active_agent/providers/open_ai/responses/requests/text/json_object.rb +20 -0
  183. data/lib/active_agent/providers/open_ai/responses/requests/text/json_schema.rb +48 -0
  184. data/lib/active_agent/providers/open_ai/responses/requests/text/plain.rb +20 -0
  185. data/lib/active_agent/providers/open_ai/responses/requests/text.rb +41 -0
  186. data/lib/active_agent/providers/open_ai/responses/requests/tool_choice.rb +26 -0
  187. data/lib/active_agent/providers/open_ai/responses/requests/tools/_types.rb +112 -0
  188. data/lib/active_agent/providers/open_ai/responses/requests/tools/base.rb +25 -0
  189. data/lib/active_agent/providers/open_ai/responses/requests/tools/code_interpreter_tool.rb +23 -0
  190. data/lib/active_agent/providers/open_ai/responses/requests/tools/computer_tool.rb +27 -0
  191. data/lib/active_agent/providers/open_ai/responses/requests/tools/custom_tool.rb +28 -0
  192. data/lib/active_agent/providers/open_ai/responses/requests/tools/file_search_tool.rb +27 -0
  193. data/lib/active_agent/providers/open_ai/responses/requests/tools/function_tool.rb +29 -0
  194. data/lib/active_agent/providers/open_ai/responses/requests/tools/image_generation_tool.rb +37 -0
  195. data/lib/active_agent/providers/open_ai/responses/requests/tools/local_shell_tool.rb +21 -0
  196. data/lib/active_agent/providers/open_ai/responses/requests/tools/mcp_tool.rb +41 -0
  197. data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_preview_tool.rb +24 -0
  198. data/lib/active_agent/providers/open_ai/responses/requests/tools/web_search_tool.rb +25 -0
  199. data/lib/active_agent/providers/open_ai/responses_provider.rb +153 -0
  200. data/lib/active_agent/providers/open_ai/schema.yml +65937 -0
  201. data/lib/active_agent/providers/open_ai_provider.rb +97 -0
  202. data/lib/active_agent/providers/open_router/_types.rb +45 -0
  203. data/lib/active_agent/providers/open_router/options.rb +93 -0
  204. data/lib/active_agent/providers/open_router/request.rb +83 -0
  205. data/lib/active_agent/providers/open_router/requests/_types.rb +198 -0
  206. data/lib/active_agent/providers/open_router/requests/message.rb +1 -0
  207. data/lib/active_agent/providers/open_router/requests/messages/_types.rb +59 -0
  208. data/lib/active_agent/providers/open_router/requests/messages/assistant.rb +20 -0
  209. data/lib/active_agent/providers/open_router/requests/messages/content/_types.rb +97 -0
  210. data/lib/active_agent/providers/open_router/requests/messages/content/file.rb +27 -0
  211. data/lib/active_agent/providers/open_router/requests/messages/content/files/_types.rb +61 -0
  212. data/lib/active_agent/providers/open_router/requests/messages/content/files/details.rb +26 -0
  213. data/lib/active_agent/providers/open_router/requests/messages/user.rb +30 -0
  214. data/lib/active_agent/providers/open_router/requests/plugin.rb +25 -0
  215. data/lib/active_agent/providers/open_router/requests/plugins/_types.rb +46 -0
  216. data/lib/active_agent/providers/open_router/requests/plugins/pdf_config.rb +29 -0
  217. data/lib/active_agent/providers/open_router/requests/prediction.rb +17 -0
  218. data/lib/active_agent/providers/open_router/requests/provider_preferences/_types.rb +44 -0
  219. data/lib/active_agent/providers/open_router/requests/provider_preferences/max_price.rb +30 -0
  220. data/lib/active_agent/providers/open_router/requests/provider_preferences.rb +64 -0
  221. data/lib/active_agent/providers/open_router/requests/response_format.rb +49 -0
  222. data/lib/active_agent/providers/open_router_provider.rb +53 -0
  223. data/lib/active_agent/providers/openai_provider.rb +2 -0
  224. data/lib/active_agent/providers/openrouter_provider.rb +2 -0
  225. data/lib/active_agent/railtie.rb +8 -6
  226. data/lib/active_agent/schema_generator.rb +333 -166
  227. data/lib/active_agent/version.rb +1 -1
  228. data/lib/active_agent.rb +112 -36
  229. data/lib/generators/active_agent/agent/USAGE +78 -0
  230. data/lib/generators/active_agent/{agent_generator.rb → agent/agent_generator.rb} +14 -4
  231. data/lib/generators/active_agent/install/USAGE +25 -0
  232. data/lib/generators/active_agent/{install_generator.rb → install/install_generator.rb} +1 -19
  233. data/lib/generators/active_agent/templates/agent.rb.tt +7 -3
  234. data/lib/generators/active_agent/templates/application_agent.rb.tt +0 -2
  235. data/lib/generators/erb/agent_generator.rb +31 -16
  236. data/lib/generators/erb/templates/instructions.md.erb.tt +3 -0
  237. data/lib/generators/erb/templates/instructions.md.tt +3 -0
  238. data/lib/generators/erb/templates/instructions.text.tt +1 -0
  239. data/lib/generators/erb/templates/message.md.erb.tt +5 -0
  240. data/lib/generators/erb/templates/schema.json.tt +10 -0
  241. data/lib/generators/test_unit/agent_generator.rb +1 -1
  242. data/lib/generators/test_unit/templates/functional_test.rb.tt +4 -2
  243. metadata +320 -65
  244. data/lib/active_agent/action_prompt/action.rb +0 -13
  245. data/lib/active_agent/action_prompt/base.rb +0 -623
  246. data/lib/active_agent/action_prompt/message.rb +0 -126
  247. data/lib/active_agent/action_prompt/prompt.rb +0 -136
  248. data/lib/active_agent/action_prompt.rb +0 -19
  249. data/lib/active_agent/callbacks.rb +0 -33
  250. data/lib/active_agent/generation_provider/anthropic_provider.rb +0 -163
  251. data/lib/active_agent/generation_provider/base.rb +0 -55
  252. data/lib/active_agent/generation_provider/base_adapter.rb +0 -19
  253. data/lib/active_agent/generation_provider/error_handling.rb +0 -167
  254. data/lib/active_agent/generation_provider/log_subscriber.rb +0 -92
  255. data/lib/active_agent/generation_provider/message_formatting.rb +0 -107
  256. data/lib/active_agent/generation_provider/ollama_provider.rb +0 -66
  257. data/lib/active_agent/generation_provider/open_ai_provider.rb +0 -279
  258. data/lib/active_agent/generation_provider/open_router_provider.rb +0 -385
  259. data/lib/active_agent/generation_provider/parameter_builder.rb +0 -119
  260. data/lib/active_agent/generation_provider/response.rb +0 -75
  261. data/lib/active_agent/generation_provider/responses_adapter.rb +0 -44
  262. data/lib/active_agent/generation_provider/stream_processing.rb +0 -58
  263. data/lib/active_agent/generation_provider/tool_management.rb +0 -142
  264. data/lib/active_agent/generation_provider.rb +0 -67
  265. data/lib/active_agent/log_subscriber.rb +0 -44
  266. data/lib/active_agent/parameterized.rb +0 -75
  267. data/lib/active_agent/prompt_helper.rb +0 -19
  268. data/lib/active_agent/queued_generation.rb +0 -12
  269. data/lib/active_agent/rescuable.rb +0 -34
  270. data/lib/active_agent/sanitizers.rb +0 -40
  271. data/lib/active_agent/streaming.rb +0 -34
  272. data/lib/active_agent/test_case.rb +0 -125
  273. data/lib/generators/USAGE +0 -47
  274. data/lib/generators/active_agent/USAGE +0 -56
  275. data/lib/generators/erb/install_generator.rb +0 -44
  276. data/lib/generators/erb/templates/layout.html.erb.tt +0 -1
  277. data/lib/generators/erb/templates/layout.json.erb.tt +0 -1
  278. data/lib/generators/erb/templates/layout.text.erb.tt +0 -1
  279. data/lib/generators/erb/templates/view.html.erb.tt +0 -5
  280. data/lib/generators/erb/templates/view.json.erb.tt +0 -16
  281. /data/lib/active_agent/{preview.rb → concerns/preview.rb} +0 -0
  282. /data/lib/generators/erb/templates/{view.text.erb.tt → message.text.erb.tt} +0 -0
@@ -1,6 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveAgent
4
+ # Provides automatic JSON Schema generation from ActiveRecord and ActiveModel classes.
5
+ #
6
+ # This module can be included in any ActiveRecord or ActiveModel class to add the ability
7
+ # to generate JSON Schema representations. The generated schemas can be used for API
8
+ # documentation, validation, or as input to AI models that support structured outputs.
9
+ #
10
+ # @example Basic usage with ActiveRecord
11
+ # class User < ApplicationRecord
12
+ # include ActiveAgent::SchemaGenerator
13
+ # end
14
+ #
15
+ # User.to_json_schema
16
+ # # => { type: "object", properties: { ... }, required: [...] }
17
+ #
18
+ # @example With options
19
+ # User.to_json_schema(
20
+ # exclude: [:password_digest],
21
+ # include_associations: true,
22
+ # strict: true
23
+ # )
24
+ #
25
+ # @example ActiveModel usage
26
+ # class ContactForm
27
+ # include ActiveModel::Model
28
+ # include ActiveAgent::SchemaGenerator
29
+ #
30
+ # attribute :name, :string
31
+ # attribute :email, :string
32
+ # attribute :message, :text
33
+ # end
34
+ #
35
+ # ContactForm.to_json_schema
36
+ #
37
+ # @see Builder
4
38
  module SchemaGenerator
5
39
  extend ActiveSupport::Concern
6
40
 
@@ -15,248 +49,381 @@ module ActiveAgent
15
49
  end
16
50
  end
17
51
 
52
+ # Class methods added to ActiveRecord models.
18
53
  module ActiveRecordClassMethods
54
+ # Generates a JSON Schema representation of the ActiveRecord model.
55
+ #
56
+ # @param options [Hash] Options for schema generation
57
+ # @option options [Array<Symbol>] :exclude Attributes to exclude from the schema
58
+ # @option options [Boolean] :include_id (false) Whether to include the id field
59
+ # @option options [Boolean] :include_associations (false) Whether to include associations
60
+ # @option options [Array<Symbol>] :exclude_associations Associations to exclude
61
+ # @option options [Boolean] :nested_associations (false) Whether to include nested association schemas
62
+ # @option options [Boolean] :strict (false) Enable OpenAI strict mode (requires all properties)
63
+ # @option options [String] :name Custom name for the schema (used with strict mode)
64
+ # @option options [Boolean] :additional_properties (false) Allow additional properties in the schema
65
+ #
66
+ # @return [Hash] JSON Schema representation of the model
67
+ #
68
+ # @example Basic schema generation
69
+ # User.to_json_schema
70
+ #
71
+ # @example Exclude specific fields
72
+ # User.to_json_schema(exclude: [:password_digest, :created_at])
73
+ #
74
+ # @example Include associations
75
+ # User.to_json_schema(include_associations: true, nested_associations: true)
76
+ #
77
+ # @example OpenAI strict mode
78
+ # User.to_json_schema(strict: true, name: "user")
19
79
  def to_json_schema(options = {})
20
80
  ActiveAgent::SchemaGenerator::Builder.json_schema_from_model(self, options)
21
81
  end
22
82
  end
23
83
 
84
+ # Class methods added to ActiveModel classes.
24
85
  module ActiveModelClassMethods
86
+ # Generates a JSON Schema representation of the ActiveModel class.
87
+ #
88
+ # @param options [Hash] Options for schema generation
89
+ # @option options [Array<Symbol>] :exclude Attributes to exclude from the schema
90
+ # @option options [Boolean] :strict (false) Enable OpenAI strict mode (requires all properties)
91
+ # @option options [String] :name Custom name for the schema (used with strict mode)
92
+ # @option options [Boolean] :additional_properties (false) Allow additional properties in the schema
93
+ #
94
+ # @return [Hash] JSON Schema representation of the model
95
+ #
96
+ # @example Basic schema generation
97
+ # ContactForm.to_json_schema
98
+ #
99
+ # @example Exclude specific fields
100
+ # ContactForm.to_json_schema(exclude: [:internal_notes])
101
+ #
102
+ # @example OpenAI strict mode
103
+ # ContactForm.to_json_schema(strict: true, name: "contact_form")
25
104
  def to_json_schema(options = {})
26
105
  ActiveAgent::SchemaGenerator::Builder.json_schema_from_model(self, options)
27
106
  end
28
107
  end
29
108
 
109
+ # Internal builder class for constructing JSON Schemas from model classes.
110
+ #
111
+ # This class handles the actual schema generation logic, supporting both
112
+ # ActiveRecord and ActiveModel classes with various options and configurations.
113
+ #
114
+ # @api private
30
115
  class Builder
116
+ # Generates a JSON Schema from a model class.
117
+ #
118
+ # @param model_class [Class] The ActiveRecord or ActiveModel class
119
+ # @param options [Hash] Schema generation options
120
+ #
121
+ # @return [Hash] The generated JSON Schema
122
+ #
123
+ # @raise [ArgumentError] If model_class is not an ActiveRecord or ActiveModel class
31
124
  def self.json_schema_from_model(model_class, options = {})
32
125
  schema = {
33
126
  type: "object",
34
127
  properties: {},
35
128
  required: [],
36
- additionalProperties: options.fetch(:additional_properties, false)
129
+ additional_properties: options.fetch(:additional_properties, false)
37
130
  }
38
131
 
39
132
  if defined?(ActiveRecord::Base) && model_class < ActiveRecord::Base
40
- build_activerecord_schema(model_class, schema, options)
133
+ schema = build_activerecord_schema(schema, model_class, options)
41
134
  elsif defined?(ActiveModel::Model) && model_class.include?(ActiveModel::Model)
42
- build_activemodel_schema(model_class, schema, options)
135
+ schema = build_activemodel_schema(schema, model_class, options)
43
136
  else
44
137
  raise ArgumentError, "Model must be an ActiveRecord or ActiveModel class"
45
138
  end
46
139
 
47
- if options[:strict]
48
- # OpenAI strict mode requires all properties to be in the required array
49
- # So we add all properties to required if strict mode is enabled
50
- schema[:required] = schema[:properties].keys.map(&:to_s).sort
140
+ # OpenAI strict mode requires all properties to be in the required array
141
+ # So we add all properties to required if strict mode is enabled
142
+ schema[:required] = schema[:properties].keys.sort if options[:strict]
51
143
 
52
- {
53
- name: options[:name] || model_class.name.underscore,
54
- schema: schema,
55
- strict: true
56
- }
57
- else
58
- schema
59
- end
144
+ {
145
+ name: options[:name] || model_class.name.underscore,
146
+ schema: schema,
147
+ strict: options[:strict]
148
+ }.compact
60
149
  end
61
150
 
62
151
  class << self
63
152
  private
64
153
 
65
- def build_activerecord_schema(model_class, schema, options)
154
+ # Builds a JSON Schema from an ActiveRecord model.
155
+ #
156
+ # Extracts column information, associations, and validations to build
157
+ # a comprehensive schema representation.
158
+ #
159
+ # @param schema [Hash] The schema hash to populate
160
+ # @param model_class [Class] The ActiveRecord model class
161
+ # @param options [Hash] Schema generation options
162
+ #
163
+ # @return [void]
164
+ def build_activerecord_schema(schema, model_class, options)
66
165
  model_class.columns.each do |column|
67
166
  next if options[:exclude]&.include?(column.name.to_sym)
68
167
  next if column.name == "id" && !options[:include_id]
69
168
 
70
169
  property = build_property_from_column(column)
71
- schema[:properties][column.name] = property
170
+ schema[:properties][column.name.to_sym] = property
72
171
 
73
172
  if !column.null && column.name != "id"
74
- schema[:required] << column.name
173
+ schema[:required] << column.name.to_sym
75
174
  end
76
175
  end
77
176
 
78
177
  if model_class.reflect_on_all_associations.any?
79
- add_associations_to_schema(model_class, schema, options)
178
+ schema = add_associations_to_schema(schema, model_class, options)
80
179
  end
81
180
 
82
181
  if model_class.respond_to?(:validators)
83
- add_validations_to_schema(model_class, schema, options)
182
+ schema = add_validations_to_schema(schema, model_class, options)
84
183
  end
85
- end
86
184
 
87
- def build_activemodel_schema(model_class, schema, options)
88
- if model_class.respond_to?(:attribute_types)
89
- model_class.attribute_types.each do |name, type|
90
- next if options[:exclude]&.include?(name.to_sym)
185
+ schema
186
+ end
187
+
188
+ # Builds a JSON Schema from an ActiveModel class.
189
+ #
190
+ # Extracts attribute types and validations to build a schema representation.
191
+ #
192
+ # @param schema [Hash] The schema hash to populate
193
+ # @param model_class [Class] The ActiveModel class
194
+ # @param options [Hash] Schema generation options
195
+ #
196
+ # @return [void]
197
+ def build_activemodel_schema(schema, model_class, options)
198
+ if model_class.respond_to?(:attribute_types)
199
+ model_class.attribute_types.each do |name, type|
200
+ next if options[:exclude]&.include?(name.to_sym)
201
+
202
+ property = build_property_from_type(type)
203
+ schema[:properties][name.to_sym] = property
204
+ end
205
+ else
206
+ raise ArgumentError, "#{model_class.name} does not define any attributes. Use `attribute :name, :type` to define attributes."
207
+ end
91
208
 
92
- property = build_property_from_type(type)
93
- schema[:properties][name] = property
209
+ if model_class.respond_to?(:validators)
210
+ schema = add_validations_to_schema(schema, model_class, options)
94
211
  end
95
- end
96
212
 
97
- if model_class.respond_to?(:validators)
98
- add_validations_to_schema(model_class, schema, options)
213
+ schema
99
214
  end
100
- end
101
215
 
216
+ # Builds a JSON Schema property definition from an ActiveRecord column.
217
+ #
218
+ # Maps database column types to JSON Schema types and includes metadata
219
+ # like length constraints, formats, and default values.
220
+ #
221
+ # @param column [ActiveRecord::ConnectionAdapters::Column] The database column
222
+ #
223
+ # @return [Hash] JSON Schema property definition
102
224
  def build_property_from_column(column)
103
- property = {
104
- type: map_sql_type_to_json_type(column.type),
105
- description: "#{column.name.humanize} field"
106
- }
225
+ property = {
226
+ type: map_sql_type_to_json_type(column.type),
227
+ description: "#{column.name.humanize} field"
228
+ }
107
229
 
108
- case column.type
109
- when :string, :text
110
- if column.limit
111
- property[:maxLength] = column.limit
230
+ case column.type
231
+ when :string, :text
232
+ if column.limit
233
+ property[:max_length] = column.limit
234
+ end
235
+ when :integer, :bigint
236
+ property[:type] = "integer"
237
+ when :decimal, :float
238
+ property[:type] = "number"
239
+ when :boolean
240
+ property[:type] = "boolean"
241
+ when :date, :datetime, :timestamp
242
+ property[:type] = "string"
243
+ property[:format] = (column.type == :date) ? "date" : "date-time"
244
+ when :json, :jsonb
245
+ property[:type] = "object"
246
+ else
247
+ property[:type] = "string"
112
248
  end
113
- when :integer, :bigint
114
- property[:type] = "integer"
115
- when :decimal, :float
116
- property[:type] = "number"
117
- when :boolean
118
- property[:type] = "boolean"
119
- when :date, :datetime, :timestamp
120
- property[:type] = "string"
121
- property[:format] = (column.type == :date) ? "date" : "date-time"
122
- when :json, :jsonb
123
- property[:type] = "object"
124
- else
125
- property[:type] = "string"
126
- end
127
249
 
128
- if column.default
129
- property[:default] = column.default
130
- end
250
+ if column.default
251
+ property[:default] = column.default
252
+ end
131
253
 
132
- property
133
- end
254
+ property
255
+ end
134
256
 
257
+ # Builds a JSON Schema property definition from an ActiveModel type.
258
+ #
259
+ # @param type [ActiveModel::Type::Value] The ActiveModel type
260
+ #
261
+ # @return [Hash] JSON Schema property definition
135
262
  def build_property_from_type(type)
136
- property = { type: "string" }
137
-
138
- case type
139
- when ActiveModel::Type::String
140
- property[:type] = "string"
141
- when ActiveModel::Type::Integer
142
- property[:type] = "integer"
143
- when ActiveModel::Type::Float, ActiveModel::Type::Decimal
144
- property[:type] = "number"
145
- when ActiveModel::Type::Boolean
146
- property[:type] = "boolean"
147
- when ActiveModel::Type::Date
148
- property[:type] = "string"
149
- property[:format] = "date"
150
- when ActiveModel::Type::DateTime, ActiveModel::Type::Time
151
- property[:type] = "string"
152
- property[:format] = "date-time"
153
- else
154
- property[:type] = "string"
155
- end
263
+ property = { type: "string" }
264
+
265
+ case type
266
+ when ActiveModel::Type::String
267
+ property[:type] = "string"
268
+ when ActiveModel::Type::Integer
269
+ property[:type] = "integer"
270
+ when ActiveModel::Type::Float, ActiveModel::Type::Decimal
271
+ property[:type] = "number"
272
+ when ActiveModel::Type::Boolean
273
+ property[:type] = "boolean"
274
+ when ActiveModel::Type::Date
275
+ property[:type] = "string"
276
+ property[:format] = "date"
277
+ when ActiveModel::Type::DateTime, ActiveModel::Type::Time
278
+ property[:type] = "string"
279
+ property[:format] = "date-time"
280
+ else
281
+ property[:type] = "string"
282
+ end
156
283
 
157
- property
158
- end
284
+ property
285
+ end
159
286
 
287
+ # Maps SQL column types to JSON Schema types.
288
+ #
289
+ # @param sql_type [Symbol] The SQL column type
290
+ #
291
+ # @return [String] The corresponding JSON Schema type
160
292
  def map_sql_type_to_json_type(sql_type)
161
- case sql_type
162
- when :string, :text
163
- "string"
164
- when :integer, :bigint
165
- "integer"
166
- when :decimal, :float
167
- "number"
168
- when :boolean
169
- "boolean"
170
- when :json, :jsonb
171
- "object"
172
- when :array
173
- "array"
174
- else
175
- "string"
293
+ case sql_type
294
+ when :string, :text
295
+ "string"
296
+ when :integer, :bigint
297
+ "integer"
298
+ when :decimal, :float
299
+ "number"
300
+ when :boolean
301
+ "boolean"
302
+ when :json, :jsonb
303
+ "object"
304
+ when :array
305
+ "array"
306
+ else
307
+ "string"
308
+ end
176
309
  end
177
- end
178
310
 
179
- def add_associations_to_schema(model_class, schema, options)
180
- return unless options[:include_associations]
181
-
182
- schema[:$defs] ||= {}
183
-
184
- model_class.reflect_on_all_associations.each do |association|
185
- next if options[:exclude_associations]&.include?(association.name)
186
-
187
- case association.macro
188
- when :has_many, :has_and_belongs_to_many
189
- schema[:properties][association.name.to_s] = {
190
- type: "array",
191
- items: { "$ref": "#/$defs/#{association.name.to_s.singularize}" }
192
- }
193
- if options[:nested_associations]
194
- nested_schema = json_schema_from_model(
195
- association.klass,
196
- options.merge(include_associations: false)
197
- )
198
- schema[:$defs][association.name.to_s.singularize] = nested_schema
199
- end
200
- when :has_one, :belongs_to
201
- schema[:properties][association.name.to_s] = {
202
- "$ref": "#/$defs/#{association.name}"
203
- }
204
- if options[:nested_associations]
205
- nested_schema = json_schema_from_model(
206
- association.klass,
207
- options.merge(include_associations: false)
208
- )
209
- schema[:$defs][association.name.to_s] = nested_schema
311
+ # Adds association definitions to the schema.
312
+ #
313
+ # Supports has_many, has_one, belongs_to, and has_and_belongs_to_many
314
+ # associations. Can optionally include nested schemas for associated models.
315
+ #
316
+ # @param schema [Hash] The schema hash to populate
317
+ # @param model_class [Class] The ActiveRecord model class
318
+ # @param options [Hash] Schema generation options
319
+ #
320
+ # @return [void]
321
+ def add_associations_to_schema(schema, model_class, options)
322
+ return schema unless options[:include_associations]
323
+
324
+ schema[:$defs] ||= {}
325
+
326
+ model_class.reflect_on_all_associations.each do |association|
327
+ next if options[:exclude_associations]&.include?(association.name)
328
+
329
+ case association.macro
330
+ when :has_many, :has_and_belongs_to_many
331
+ schema[:properties][association.name.to_sym] = {
332
+ type: "array",
333
+ items: { "$ref": "#/$defs/#{association.name.to_s.singularize}" }
334
+ }
335
+ if options[:nested_associations]
336
+ nested_schema = json_schema_from_model(
337
+ association.klass,
338
+ options.merge(include_associations: false)
339
+ )
340
+ schema[:$defs][association.name.to_s.singularize.to_sym] = nested_schema
341
+ end
342
+ when :has_one, :belongs_to
343
+ schema[:properties][association.name.to_sym] = {
344
+ "$ref": "#/$defs/#{association.name}"
345
+ }
346
+ if options[:nested_associations]
347
+ nested_schema = json_schema_from_model(
348
+ association.klass,
349
+ options.merge(include_associations: false)
350
+ )
351
+ schema[:$defs][association.name.to_sym] = nested_schema
352
+ end
210
353
  end
211
354
  end
355
+
356
+ schema
212
357
  end
213
- end
214
358
 
215
- def add_validations_to_schema(model_class, schema, options)
216
- model_class.validators.each do |validator|
217
- validator.attributes.each do |attribute|
218
- next unless schema[:properties][attribute.to_s]
219
-
220
- case validator
221
- when ActiveModel::Validations::PresenceValidator
222
- schema[:required] << attribute.to_s unless schema[:required].include?(attribute.to_s)
223
- when ActiveModel::Validations::LengthValidator
224
- if validator.options[:minimum]
225
- schema[:properties][attribute.to_s][:minLength] = validator.options[:minimum]
226
- end
227
- if validator.options[:maximum]
228
- schema[:properties][attribute.to_s][:maxLength] = validator.options[:maximum]
229
- end
230
- when ActiveModel::Validations::NumericalityValidator
231
- if validator.options[:greater_than]
232
- schema[:properties][attribute.to_s][:exclusiveMinimum] = validator.options[:greater_than]
233
- end
234
- if validator.options[:less_than]
235
- schema[:properties][attribute.to_s][:exclusiveMaximum] = validator.options[:less_than]
236
- end
237
- if validator.options[:greater_than_or_equal_to]
238
- schema[:properties][attribute.to_s][:minimum] = validator.options[:greater_than_or_equal_to]
239
- end
240
- if validator.options[:less_than_or_equal_to]
241
- schema[:properties][attribute.to_s][:maximum] = validator.options[:less_than_or_equal_to]
242
- end
243
- when ActiveModel::Validations::InclusionValidator
244
- if validator.options[:in]
245
- schema[:properties][attribute.to_s][:enum] = validator.options[:in]
246
- end
247
- when ActiveModel::Validations::FormatValidator
248
- if validator.options[:with] == URI::MailTo::EMAIL_REGEXP
249
- schema[:properties][attribute.to_s][:format] = "email"
250
- elsif validator.options[:with]
251
- schema[:properties][attribute.to_s][:pattern] = validator.options[:with].source
359
+ # Adds validation constraints to the schema.
360
+ #
361
+ # Translates ActiveModel validations (presence, length, numericality,
362
+ # inclusion, format) into corresponding JSON Schema constraints.
363
+ #
364
+ # @param schema [Hash] The schema hash to populate
365
+ # @param model_class [Class] The model class
366
+ # @param options [Hash] Schema generation options (unused but kept for consistency)
367
+ #
368
+ # @return [void]
369
+ def add_validations_to_schema(schema, model_class, options)
370
+ model_class.validators.each do |validator|
371
+ validator.attributes.each do |attribute|
372
+ next unless schema[:properties][attribute.to_sym]
373
+
374
+ case validator
375
+ when ActiveModel::Validations::PresenceValidator
376
+ schema[:required] << attribute.to_sym unless schema[:required].include?(attribute.to_sym)
377
+ when ActiveModel::Validations::LengthValidator
378
+ if validator.options[:minimum]
379
+ schema[:properties][attribute.to_sym][:min_length] = validator.options[:minimum]
380
+ end
381
+ if validator.options[:maximum]
382
+ schema[:properties][attribute.to_sym][:max_length] = validator.options[:maximum]
383
+ end
384
+ when ActiveModel::Validations::NumericalityValidator
385
+ if validator.options[:greater_than]
386
+ schema[:properties][attribute.to_sym][:exclusive_minimum] = validator.options[:greater_than]
387
+ end
388
+ if validator.options[:less_than]
389
+ schema[:properties][attribute.to_sym][:exclusive_maximum] = validator.options[:less_than]
390
+ end
391
+ if validator.options[:greater_than_or_equal_to]
392
+ schema[:properties][attribute.to_sym][:minimum] = validator.options[:greater_than_or_equal_to]
393
+ end
394
+ if validator.options[:less_than_or_equal_to]
395
+ schema[:properties][attribute.to_sym][:maximum] = validator.options[:less_than_or_equal_to]
396
+ end
397
+ when ActiveModel::Validations::InclusionValidator
398
+ if validator.options[:in]
399
+ schema[:properties][attribute.to_sym][:enum] = validator.options[:in]
400
+ end
401
+ when ActiveModel::Validations::FormatValidator
402
+ if validator.options[:with] == URI::MailTo::EMAIL_REGEXP
403
+ schema[:properties][attribute.to_sym][:format] = "email"
404
+ elsif validator.options[:with]
405
+ schema[:properties][attribute.to_sym][:pattern] = validator.options[:with].source
406
+ end
252
407
  end
253
408
  end
254
409
  end
410
+
411
+ schema
255
412
  end
256
413
  end
257
- end
258
414
  end
259
415
 
416
+ # Generates a JSON string representation of a model's schema.
417
+ #
418
+ # This is an instance method that can be used to generate schema views
419
+ # for individual model instances, though it operates on the class level.
420
+ #
421
+ # @param model_class [Class] The model class to generate a schema for
422
+ # @param options [Hash] Schema generation options (see {ActiveRecordClassMethods#to_json_schema})
423
+ #
424
+ # @return [String] JSON string representation of the schema
425
+ #
426
+ # @deprecated This method may be removed in future versions. Use the class method `to_json_schema` instead.
260
427
  def generate_schema_view(model_class, options = {})
261
428
  schema = ActiveAgent::SchemaGenerator::Builder.json_schema_from_model(model_class, options)
262
429
  schema.to_json
@@ -1,3 +1,3 @@
1
1
  module ActiveAgent
2
- VERSION = "0.6.3"
2
+ VERSION = "1.0.0.rc1"
3
3
  end