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,505 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAgent
4
+ module GenerationProvider
5
+ module OpenRouter
6
+ # Base class for OpenRouter request objects with ActiveModel support
7
+ class ConfigObject
8
+ include ActiveModel::Model
9
+ include ActiveModel::Attributes
10
+ include ActiveModel::Validations
11
+
12
+ def self.alias_names = []
13
+
14
+ def self.accepts_attributes_for(association_name, klass)
15
+ attributes = klass.attribute_names | klass.alias_names
16
+
17
+ attributes.each do |attribute|
18
+ define_method(attribute) do
19
+ public_send(association_name)&.public_send(attribute)
20
+ end
21
+
22
+ define_method("#{attribute}=") do |value|
23
+ unless public_send(association_name)
24
+ public_send("#{association_name}=", {})
25
+ end
26
+
27
+ public_send(association_name).public_send("#{attribute}=", value)
28
+ end
29
+ end
30
+ end
31
+
32
+ def to_h
33
+ attributes.compact.transform_values do |value|
34
+ case value
35
+ when ConfigObject
36
+ value.to_h
37
+ when Array
38
+ value.map { |v| v.is_a?(ConfigObject) ? v.to_h : v }
39
+ else
40
+ value
41
+ end
42
+ end
43
+ end
44
+
45
+ alias_method :to_hash, :to_h
46
+ end
47
+
48
+ # Text content part for messages
49
+ class TextContent < ConfigObject
50
+ attribute :type, :string, default: "text"
51
+ attribute :text, :string
52
+
53
+ validates :type, inclusion: { in: %w[text] }
54
+ validates :text, presence: true
55
+ end
56
+
57
+ # Image content part for messages
58
+ class ImageContentPart < ConfigObject
59
+ attribute :type, :string, default: "image_url"
60
+ attribute :image_url, default: -> { {} }
61
+
62
+ validates :type, inclusion: { in: %w[image_url] }
63
+ validates :image_url, presence: true
64
+
65
+ def image_url=(value)
66
+ if value.is_a?(Hash)
67
+ super({
68
+ url: value[:url] || value["url"],
69
+ detail: value[:detail] || value["detail"]
70
+ }.compact)
71
+ else
72
+ super(value)
73
+ end
74
+ end
75
+
76
+ validate :validate_image_url_structure
77
+
78
+ private
79
+
80
+ def validate_image_url_structure
81
+ return if image_url.blank?
82
+ return unless image_url.is_a?(Hash)
83
+
84
+ if image_url[:url].blank? && image_url["url"].blank?
85
+ errors.add(:image_url, "must contain a url")
86
+ end
87
+ end
88
+ end
89
+
90
+ # Message object for chat completions
91
+ class Message < ConfigObject
92
+ attribute :role, :string
93
+ attribute :content # Can be string or array of ContentParts
94
+ attribute :name, :string
95
+ attribute :tool_call_id, :string
96
+
97
+ validates :role, presence: true, inclusion: { in: %w[user assistant system tool] }
98
+ validates :content, presence: true
99
+
100
+ validate :validate_role_specific_fields
101
+
102
+ def content=(value)
103
+ if value.is_a?(Array)
104
+ super(value.map do |part|
105
+ next part if part.is_a?(TextContent) || part.is_a?(ImageContentPart)
106
+
107
+ case part
108
+ when Hash
109
+ if part[:type] == "text" || part["type"] == "text"
110
+ TextContent.new(part)
111
+ elsif part[:type] == "image_url" || part["type"] == "image_url"
112
+ ImageContentPart.new(part)
113
+ else
114
+ part
115
+ end
116
+ else
117
+ part
118
+ end
119
+ end)
120
+ else
121
+ super(value)
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def validate_role_specific_fields
128
+ if role == "tool"
129
+ errors.add(:tool_call_id, "is required for tool role") if tool_call_id.blank?
130
+ elsif role == "user" && content.is_a?(Array)
131
+ # ContentParts are only valid for user role
132
+ content.each_with_index do |part, index|
133
+ next if part.is_a?(String)
134
+ next if part.is_a?(TextContent) || part.is_a?(ImageContentPart)
135
+
136
+ errors.add(:content, "invalid content part at index #{index}")
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ # Function description for tools
143
+ class FunctionDescription < ConfigObject
144
+ attribute :name, :string
145
+ attribute :description, :string
146
+ attribute :parameters, default: -> { {} } # JSON Schema object
147
+
148
+ validates :name, presence: true
149
+ validates :parameters, presence: true
150
+
151
+ validate :validate_parameters_is_object
152
+
153
+ private
154
+
155
+ def validate_parameters_is_object
156
+ return if parameters.blank?
157
+ unless parameters.is_a?(Hash)
158
+ errors.add(:parameters, "must be a JSON Schema object (Hash)")
159
+ end
160
+ end
161
+ end
162
+
163
+ # Tool definition
164
+ class Tool < ConfigObject
165
+ attribute :type, :string, default: "function"
166
+ attribute :function
167
+
168
+ validates :type, inclusion: { in: %w[function] }
169
+ validates :function, presence: true
170
+
171
+ def function=(value)
172
+ if value.is_a?(Hash) && !value.is_a?(FunctionDescription)
173
+ super(FunctionDescription.new(value))
174
+ else
175
+ super(value)
176
+ end
177
+ end
178
+
179
+ validate :validate_function_object
180
+
181
+ private
182
+
183
+ def validate_function_object
184
+ return if function.blank?
185
+
186
+ if function.is_a?(FunctionDescription)
187
+ unless function.valid?
188
+ function.errors.full_messages.each do |msg|
189
+ errors.add(:function, msg)
190
+ end
191
+ end
192
+ elsif !function.is_a?(Hash)
193
+ errors.add(:function, "must be a FunctionDescription or Hash")
194
+ end
195
+ end
196
+ end
197
+
198
+ # Tool choice for controlling tool usage
199
+ class ToolChoice < ConfigObject
200
+ attribute :type, :string
201
+ attribute :function, default: -> { {} }
202
+
203
+ validates :type, inclusion: { in: %w[function] }, if: -> { type.present? }
204
+
205
+ validate :validate_tool_choice_format
206
+
207
+ # Allow simple string values like "none" or "auto"
208
+ def self.from_value(value)
209
+ case value
210
+ when String
211
+ # Return the string directly for "none" or "auto"
212
+ value
213
+ when Hash
214
+ new(value)
215
+ else
216
+ value
217
+ end
218
+ end
219
+
220
+ private
221
+
222
+ def validate_tool_choice_format
223
+ return if type.blank?
224
+
225
+ if type == "function"
226
+ if function.blank? || (function.is_a?(Hash) && function[:name].blank? && function["name"].blank?)
227
+ errors.add(:function, "must contain a name when type is 'function'")
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ # Provider preferences for routing
234
+ class ProviderPreferences < ConfigObject
235
+ attribute :allow_fallbacks, :boolean
236
+ attribute :require_parameters, :boolean
237
+ attribute :data_collection, :string
238
+ attribute :order, default: -> { [] }
239
+ attribute :ignore, default: -> { [] }
240
+ attribute :quantizations, default: -> { [] }
241
+
242
+ validates :data_collection,
243
+ inclusion: { in: %w[allow deny] },
244
+ allow_nil: true
245
+
246
+ validate :validate_order_and_ignore_arrays
247
+
248
+ private
249
+
250
+ def validate_order_and_ignore_arrays
251
+ if order.present? && !order.is_a?(Array)
252
+ errors.add(:order, "must be an array of provider names")
253
+ end
254
+
255
+ if ignore.present? && !ignore.is_a?(Array)
256
+ errors.add(:ignore, "must be an array of provider names")
257
+ end
258
+
259
+ if quantizations.present? && !quantizations.is_a?(Array)
260
+ errors.add(:quantizations, "must be an array")
261
+ end
262
+ end
263
+ end
264
+
265
+ # Response format specification
266
+ class ResponseFormat < ConfigObject
267
+ attribute :type, :string
268
+
269
+ validates :type, inclusion: { in: %w[json_object] }
270
+ end
271
+
272
+ # Prediction for latency optimization
273
+ class Prediction < ConfigObject
274
+ attribute :type, :string
275
+ attribute :content, :string
276
+
277
+ validates :type, inclusion: { in: %w[content] }
278
+ validates :content, presence: true
279
+ end
280
+
281
+ # Extra Headers
282
+ class Header < ConfigObject
283
+ attribute :http_referer, :string
284
+ attribute :x_title, :string
285
+
286
+ validates :http_referer, format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]), message: "must be a valid URL" }, allow_nil: true
287
+
288
+ alias_attribute :site_url, :http_referer
289
+ alias_attribute :app_name, :x_title
290
+
291
+ def self.alias_names
292
+ [ "site_url", "app_name" ]
293
+ end
294
+
295
+ def to_h
296
+ {
297
+ "HTTP-Referer" => http_referer,
298
+ "X-Title" => x_title
299
+ }.compact
300
+ end
301
+ end
302
+
303
+ # Main request object for OpenRouter API
304
+ class Request < ConfigObject
305
+ # Required (either messages or prompt)
306
+ attribute :messages, default: -> { [] }
307
+ attribute :prompt, :string
308
+
309
+ # Model specification
310
+ attribute :model, :string
311
+
312
+ # Response format
313
+ attribute :response_format
314
+
315
+ # Generation parameters
316
+ attribute :stop # Can be string or array
317
+ attribute :stream, :boolean, default: false
318
+ attribute :max_tokens, :integer
319
+ attribute :temperature, :float
320
+ attribute :seed, :integer
321
+ attribute :top_p, :float
322
+ attribute :top_k, :integer
323
+ attribute :frequency_penalty, :float
324
+ attribute :presence_penalty, :float
325
+ attribute :repetition_penalty, :float
326
+ attribute :logit_bias, default: -> { {} }
327
+ attribute :top_logprobs, :integer
328
+ attribute :min_p, :float
329
+ attribute :top_a, :float
330
+
331
+ # Tool calling
332
+ attribute :tools, default: -> { [] }
333
+ attribute :tool_choice
334
+
335
+ # Latency optimization
336
+ attribute :prediction
337
+
338
+ # OpenRouter-specific parameters
339
+ attribute :transforms, default: -> { [] }
340
+ attribute :models, default: -> { [] } # For fallback routing
341
+ attribute :route, :string
342
+ attribute :provider
343
+ attribute :user, :string
344
+ attribute :headers
345
+
346
+ # Validations
347
+ validate :validate_messages_or_prompt
348
+ validates :max_tokens, numericality: { greater_than: 0 }, allow_nil: true
349
+ validates :temperature, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 2 }, allow_nil: true
350
+ validates :top_p, numericality: { greater_than: 0, less_than_or_equal_to: 1 }, allow_nil: true
351
+ validates :top_k, numericality: { greater_than_or_equal_to: 1 }, allow_nil: true
352
+ validates :frequency_penalty, numericality: { greater_than_or_equal_to: -2, less_than_or_equal_to: 2 }, allow_nil: true
353
+ validates :presence_penalty, numericality: { greater_than_or_equal_to: -2, less_than_or_equal_to: 2 }, allow_nil: true
354
+ validates :repetition_penalty, numericality: { greater_than: 0, less_than_or_equal_to: 2 }, allow_nil: true
355
+ validates :min_p, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
356
+ validates :top_a, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, allow_nil: true
357
+ validates :route, inclusion: { in: %w[fallback] }, allow_nil: true
358
+
359
+ accepts_attributes_for :response_format, ResponseFormat
360
+ accepts_attributes_for :provider, ProviderPreferences
361
+ accepts_attributes_for :prediction, Prediction
362
+ accepts_attributes_for :tool_choice, ToolChoice
363
+ accepts_attributes_for :headers, Header
364
+
365
+ def build_parameters
366
+ to_h.deep_transform_values do |value|
367
+ case value
368
+ when Array
369
+ value.empty? ? nil : value
370
+ else
371
+ value
372
+ end
373
+ end.compact
374
+ end
375
+
376
+ # Setters with type coercion
377
+ def messages=(value)
378
+ if value.is_a?(Array)
379
+ super(value.map do |msg|
380
+ msg.is_a?(Message) ? msg : Message.new(msg)
381
+ end)
382
+ else
383
+ super(value)
384
+ end
385
+ end
386
+
387
+ def tools=(value)
388
+ if value.is_a?(Array)
389
+ super(value.map do |tool|
390
+ tool.is_a?(Tool) ? tool : Tool.new(tool)
391
+ end)
392
+ else
393
+ super(value)
394
+ end
395
+ end
396
+
397
+ def response_format=(value)
398
+ if value.is_a?(Hash) && !value.is_a?(ResponseFormat)
399
+ super(ResponseFormat.new(value))
400
+ else
401
+ super(value)
402
+ end
403
+ end
404
+
405
+ def provider=(value)
406
+ if value.is_a?(Hash) && !value.is_a?(ProviderPreferences)
407
+ super(ProviderPreferences.new(value))
408
+ else
409
+ super(value)
410
+ end
411
+ end
412
+
413
+ def prediction=(value)
414
+ if value.is_a?(Hash) && !value.is_a?(Prediction)
415
+ super(Prediction.new(value))
416
+ else
417
+ super(value)
418
+ end
419
+ end
420
+
421
+ def tool_choice=(value)
422
+ super(ToolChoice.from_value(value))
423
+ end
424
+
425
+ def headers=(value)
426
+ if value.is_a?(Hash) && !value.is_a?(Header)
427
+ super(Header.new(value))
428
+ else
429
+ super(value)
430
+ end
431
+ end
432
+
433
+ validate :validate_nested_objects
434
+
435
+ private
436
+
437
+ def validate_messages_or_prompt
438
+ if messages.blank? && prompt.blank?
439
+ errors.add(:base, "Either messages or prompt is required")
440
+ end
441
+
442
+ if messages.present? && prompt.present?
443
+ errors.add(:base, "Cannot specify both messages and prompt")
444
+ end
445
+ end
446
+
447
+ def validate_nested_objects
448
+ # Validate messages
449
+ if messages.present?
450
+ messages.each_with_index do |message, index|
451
+ next unless message.is_a?(Message)
452
+
453
+ unless message.valid?
454
+ message.errors.full_messages.each do |msg|
455
+ errors.add(:messages, "at index #{index}: #{msg}")
456
+ end
457
+ end
458
+ end
459
+ end
460
+
461
+ # Validate tools
462
+ if tools.present?
463
+ tools.each_with_index do |tool, index|
464
+ next unless tool.is_a?(Tool)
465
+
466
+ unless tool.valid?
467
+ tool.errors.full_messages.each do |msg|
468
+ errors.add(:tools, "at index #{index}: #{msg}")
469
+ end
470
+ end
471
+ end
472
+ end
473
+
474
+ # Validate response_format
475
+ if response_format.is_a?(ResponseFormat) && !response_format.valid?
476
+ response_format.errors.full_messages.each do |msg|
477
+ errors.add(:response_format, msg)
478
+ end
479
+ end
480
+
481
+ # Validate provider
482
+ if provider.is_a?(ProviderPreferences) && !provider.valid?
483
+ provider.errors.full_messages.each do |msg|
484
+ errors.add(:provider, msg)
485
+ end
486
+ end
487
+
488
+ # Validate prediction
489
+ if prediction.is_a?(Prediction) && !prediction.valid?
490
+ prediction.errors.full_messages.each do |msg|
491
+ errors.add(:prediction, msg)
492
+ end
493
+ end
494
+
495
+ # Validate headers
496
+ if headers.is_a?(Header) && !headers.valid?
497
+ headers.errors.full_messages.each do |msg|
498
+ errors.add(:headers, msg)
499
+ end
500
+ end
501
+ end
502
+ end
503
+ end
504
+ end
505
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ gem "ruby-openai", ">= 8.1.0"
5
+ require "openai"
6
+ rescue LoadError
7
+ raise LoadError, "The 'ruby-openai >= 8.1.0' gem is required for XAIProvider. Please add it to your Gemfile and run `bundle install`."
8
+ end
9
+
10
+ require "active_agent/action_prompt/action"
11
+ require_relative "base"
12
+ require_relative "response"
13
+ require_relative "stream_processing"
14
+ require_relative "message_formatting"
15
+ require_relative "tool_management"
16
+
17
+ module ActiveAgent
18
+ module GenerationProvider
19
+ # XAI (Grok) Generation Provider
20
+ # Uses OpenAI-compatible API format with xAI's endpoint
21
+ class XAIProvider < Base
22
+ include StreamProcessing
23
+ include MessageFormatting
24
+ include ToolManagement
25
+
26
+ XAI_API_HOST = "https://api.x.ai"
27
+
28
+ def initialize(config)
29
+ super
30
+ # Support both api_key and access_token for backwards compatibility
31
+ @access_token = config["api_key"] || config["access_token"] || ENV["XAI_API_KEY"] || ENV["GROK_API_KEY"]
32
+
33
+ unless @access_token
34
+ raise ArgumentError, "XAI API key is required. Set it in config as 'api_key', 'access_token', or via XAI_API_KEY/GROK_API_KEY environment variable."
35
+ end
36
+
37
+ # xAI uses OpenAI-compatible client with custom endpoint
38
+ @client = OpenAI::Client.new(
39
+ access_token: @access_token,
40
+ uri_base: config["host"] || XAI_API_HOST,
41
+ log_errors: Rails.env.development?
42
+ )
43
+
44
+ # Default to grok-2-latest but allow configuration
45
+ @model_name = config["model"] || "grok-2-latest"
46
+ end
47
+
48
+ def generate(prompt)
49
+ @prompt = prompt
50
+
51
+ with_error_handling do
52
+ chat_prompt(parameters: prompt_parameters)
53
+ end
54
+ end
55
+
56
+ def embed(prompt)
57
+ # xAI doesn't currently provide embedding models
58
+ raise NotImplementedError, "xAI does not currently support embeddings. Use a different provider for embedding tasks."
59
+ end
60
+
61
+ protected
62
+
63
+ # Override from StreamProcessing module - uses OpenAI format
64
+ def process_stream_chunk(chunk, message, agent_stream)
65
+ new_content = chunk.dig("choices", 0, "delta", "content")
66
+ if new_content && !new_content.blank?
67
+ message.generation_id = chunk.dig("id")
68
+ message.content += new_content
69
+ agent_stream&.call(message, new_content, false, prompt.action_name)
70
+ elsif chunk.dig("choices", 0, "delta", "tool_calls") && chunk.dig("choices", 0, "delta", "role")
71
+ message = handle_message(chunk.dig("choices", 0, "delta"))
72
+ prompt.messages << message
73
+ @response = ActiveAgent::GenerationProvider::Response.new(
74
+ prompt:,
75
+ message:,
76
+ raw_response: chunk,
77
+ raw_request: @streaming_request_params
78
+ )
79
+ end
80
+
81
+ if chunk.dig("choices", 0, "finish_reason")
82
+ finalize_stream(message, agent_stream)
83
+ end
84
+ end
85
+
86
+ # Override from MessageFormatting module to handle image format (if xAI adds vision support)
87
+ def format_image_content(message)
88
+ [ {
89
+ type: "image_url",
90
+ image_url: { url: message.content }
91
+ } ]
92
+ end
93
+
94
+ private
95
+
96
+ # Override from ParameterBuilder to add xAI-specific parameters if needed
97
+ def build_provider_parameters
98
+ params = {}
99
+
100
+ # Add any xAI-specific parameters here
101
+ # For now, xAI follows OpenAI's format closely
102
+
103
+ params
104
+ end
105
+
106
+ def chat_response(response, request_params = nil)
107
+ return @response if prompt.options[:stream]
108
+
109
+ message_json = response.dig("choices", 0, "message")
110
+ message_json["id"] = response.dig("id") if message_json["id"].blank?
111
+ message = handle_message(message_json)
112
+
113
+ update_context(prompt: prompt, message: message, response: response)
114
+
115
+ @response = ActiveAgent::GenerationProvider::Response.new(
116
+ prompt: prompt,
117
+ message: message,
118
+ raw_response: response,
119
+ raw_request: request_params
120
+ )
121
+ end
122
+
123
+ def handle_message(message_json)
124
+ ActiveAgent::ActionPrompt::Message.new(
125
+ generation_id: message_json["id"],
126
+ content: message_json["content"],
127
+ role: message_json["role"].intern,
128
+ action_requested: message_json["finish_reason"] == "tool_calls",
129
+ raw_actions: message_json["tool_calls"] || [],
130
+ requested_actions: handle_actions(message_json["tool_calls"]),
131
+ content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
132
+ )
133
+ end
134
+
135
+ def chat_prompt(parameters: prompt_parameters)
136
+ if prompt.options[:stream] || config["stream"]
137
+ parameters[:stream] = provider_stream
138
+ @streaming_request_params = parameters
139
+ end
140
+ chat_response(@client.chat(parameters: parameters), parameters)
141
+ end
142
+ end
143
+ end
144
+ end