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,279 +0,0 @@
1
- begin
2
- gem "ruby-openai", ">= 8.1.0"
3
- require "openai"
4
- rescue LoadError
5
- raise LoadError, "The 'ruby-openai >= 8.1.0' gem is required for OpenAIProvider. Please add it to your Gemfile and run `bundle install`."
6
- end
7
-
8
- require "active_agent/action_prompt/action"
9
- require_relative "base"
10
- require_relative "response"
11
- require_relative "responses_adapter"
12
- require_relative "stream_processing"
13
- require_relative "message_formatting"
14
- require_relative "tool_management"
15
-
16
- module ActiveAgent
17
- module GenerationProvider
18
- class OpenAIProvider < Base
19
- include StreamProcessing
20
- include MessageFormatting
21
- include ToolManagement
22
- def initialize(config)
23
- super
24
- @host = config["host"] || nil
25
- @api_type = config["api_type"] || nil
26
- @access_token ||= config["api_key"] || config["access_token"] || OpenAI.configuration.access_token || ENV["OPENAI_ACCESS_TOKEN"]
27
- @organization_id = config["organization_id"] || OpenAI.configuration.organization_id || ENV["OPENAI_ORGANIZATION_ID"]
28
- @admin_token = config["admin_token"] || OpenAI.configuration.admin_token || ENV["OPENAI_ADMIN_TOKEN"]
29
- @client = OpenAI::Client.new(
30
- access_token: @access_token,
31
- uri_base: @host,
32
- organization_id: @organization_id,
33
- admin_token: @admin_token,
34
- api_type: @api_type,
35
- log_errors: Rails.env.development?
36
- )
37
-
38
- @model_name = config["model"] || "gpt-4o-mini"
39
- end
40
-
41
- def generate(prompt)
42
- @prompt = prompt
43
-
44
- with_error_handling do
45
- if @prompt.multimodal? || @prompt.content_type == "multipart/mixed"
46
- responses_prompt(parameters: responses_parameters)
47
- else
48
- chat_prompt(parameters: prompt_parameters)
49
- end
50
- end
51
- end
52
-
53
- def embed(prompt)
54
- @prompt = prompt
55
-
56
- with_error_handling do
57
- embeddings_prompt(parameters: embeddings_parameters)
58
- end
59
- end
60
-
61
- protected
62
-
63
- # Override from StreamProcessing module
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
- # Call agent_stream directly without the block to avoid double execution
70
- agent_stream&.call(message, new_content, false, prompt.action_name)
71
- elsif chunk.dig("choices", 0, "delta", "tool_calls") && chunk.dig("choices", 0, "delta", "role")
72
- message = handle_message(chunk.dig("choices", 0, "delta"))
73
- prompt.messages << message
74
- @response = ActiveAgent::GenerationProvider::Response.new(
75
- prompt:,
76
- message:,
77
- raw_response: chunk,
78
- raw_request: @streaming_request_params
79
- )
80
- end
81
-
82
- if chunk.dig("choices", 0, "finish_reason")
83
- finalize_stream(message, agent_stream)
84
- end
85
- end
86
-
87
- # Override from MessageFormatting module to handle OpenAI image format
88
- def format_image_content(message)
89
- [ {
90
- type: "image_url",
91
- image_url: { url: message.content }
92
- } ]
93
- end
94
-
95
- private
96
-
97
- # Override from ParameterBuilder to add web_search_options for Chat API
98
- def build_provider_parameters
99
- params = {}
100
-
101
- # Check if we're using a model that supports web_search_options in Chat API
102
- if chat_api_web_search_model? && @prompt.options[:web_search]
103
- params[:web_search_options] = build_web_search_options(@prompt.options[:web_search])
104
- end
105
-
106
- params
107
- end
108
-
109
- def chat_api_web_search_model?
110
- model = @prompt.options[:model] || @model_name
111
- [ "gpt-4o-search-preview", "gpt-4o-mini-search-preview" ].include?(model)
112
- end
113
-
114
- def build_web_search_options(web_search_config)
115
- options = {}
116
-
117
- if web_search_config.is_a?(Hash)
118
- options[:search_context_size] = web_search_config[:search_context_size] if web_search_config[:search_context_size]
119
-
120
- if web_search_config[:user_location]
121
- options[:user_location] = {
122
- type: "approximate",
123
- approximate: web_search_config[:user_location]
124
- }
125
- end
126
- end
127
-
128
- options
129
- end
130
-
131
- def chat_response(response, request_params = nil)
132
- return @response if prompt.options[:stream]
133
- message_json = response.dig("choices", 0, "message")
134
- message_json["id"] = response.dig("id") if message_json["id"].blank?
135
- message = handle_message(message_json)
136
-
137
- update_context(prompt: prompt, message: message, response: response)
138
-
139
- @response = ActiveAgent::GenerationProvider::Response.new(
140
- prompt: prompt,
141
- message: message,
142
- raw_response: response,
143
- raw_request: request_params
144
- )
145
- end
146
-
147
- def responses_response(response, request_params = nil)
148
- message_json = response["output"].find { |output_item| output_item["type"] == "message" }
149
- message_json["id"] = response.dig("id") if message_json["id"].blank?
150
-
151
- message = ActiveAgent::ActionPrompt::Message.new(
152
- generate_id: message_json["id"],
153
- content: message_json["content"].first["text"],
154
- role: message_json["role"].intern,
155
- action_requested: message_json["finish_reason"] == "tool_calls",
156
- raw_actions: message_json["tool_calls"] || [],
157
- content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
158
- )
159
-
160
- @response = ActiveAgent::GenerationProvider::Response.new(
161
- prompt: prompt,
162
- message: message,
163
- raw_response: response,
164
- raw_request: request_params
165
- )
166
- end
167
-
168
- def handle_message(message_json)
169
- ActiveAgent::ActionPrompt::Message.new(
170
- generation_id: message_json["id"],
171
- content: message_json["content"],
172
- role: message_json["role"].intern,
173
- action_requested: message_json["finish_reason"] == "tool_calls",
174
- raw_actions: message_json["tool_calls"] || [],
175
- requested_actions: handle_actions(message_json["tool_calls"]),
176
- content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
177
- )
178
- end
179
-
180
- # handle_actions is now provided by ToolManagement module
181
-
182
- def chat_prompt(parameters: prompt_parameters)
183
- if prompt.options[:stream] || config["stream"]
184
- parameters[:stream] = provider_stream
185
- @streaming_request_params = parameters
186
- end
187
- chat_response(@client.chat(parameters: parameters), parameters)
188
- end
189
-
190
- def responses_prompt(parameters: responses_parameters)
191
- # parameters[:stream] = provider_stream if prompt.options[:stream] || config["stream"]
192
- responses_response(@client.responses.create(parameters: parameters), parameters)
193
- end
194
-
195
- def responses_parameters(model: @prompt.options[:model] || @model_name, messages: @prompt.messages, temperature: @prompt.options[:temperature] || @config["temperature"] || 0.7, tools: @prompt.actions, structured_output: @prompt.output_schema)
196
- # Build tools array, combining action tools with built-in tools
197
- tools_array = build_tools_for_responses(tools)
198
-
199
- {
200
- model: model,
201
- input: ActiveAgent::GenerationProvider::ResponsesAdapter.new(@prompt).input,
202
- tools: tools_array.presence,
203
- text: structured_output
204
- }.compact
205
- end
206
-
207
- def build_tools_for_responses(action_tools)
208
- tools = []
209
-
210
- # Start with action tools (user-defined functions) if any
211
- tools.concat(action_tools) if action_tools.present?
212
-
213
- # Add built-in tools if specified in options[:tools]
214
- if @prompt.options[:tools].present?
215
- built_in_tools = @prompt.options[:tools]
216
- built_in_tools = [ built_in_tools ] unless built_in_tools.is_a?(Array)
217
-
218
- built_in_tools.each do |tool|
219
- next unless tool.is_a?(Hash)
220
-
221
- case tool[:type]
222
- when "web_search_preview", "web_search"
223
- web_search_tool = { type: "web_search_preview" }
224
- web_search_tool[:search_context_size] = tool[:search_context_size] if tool[:search_context_size]
225
- web_search_tool[:user_location] = tool[:user_location] if tool[:user_location]
226
- tools << web_search_tool
227
-
228
- when "image_generation"
229
- image_gen_tool = { type: "image_generation" }
230
- image_gen_tool[:size] = tool[:size] if tool[:size]
231
- image_gen_tool[:quality] = tool[:quality] if tool[:quality]
232
- image_gen_tool[:format] = tool[:format] if tool[:format]
233
- image_gen_tool[:compression] = tool[:compression] if tool[:compression]
234
- image_gen_tool[:background] = tool[:background] if tool[:background]
235
- image_gen_tool[:partial_images] = tool[:partial_images] if tool[:partial_images]
236
- tools << image_gen_tool
237
-
238
- when "mcp"
239
- mcp_tool = { type: "mcp" }
240
- mcp_tool[:server_label] = tool[:server_label] if tool[:server_label]
241
- mcp_tool[:server_description] = tool[:server_description] if tool[:server_description]
242
- mcp_tool[:server_url] = tool[:server_url] if tool[:server_url]
243
- mcp_tool[:connector_id] = tool[:connector_id] if tool[:connector_id]
244
- mcp_tool[:authorization] = tool[:authorization] if tool[:authorization]
245
- mcp_tool[:require_approval] = tool[:require_approval] if tool[:require_approval]
246
- mcp_tool[:allowed_tools] = tool[:allowed_tools] if tool[:allowed_tools]
247
- tools << mcp_tool
248
- end
249
- end
250
- end
251
-
252
- tools
253
- end
254
-
255
- def embeddings_parameters(input: prompt.message.content, model: "text-embedding-3-large")
256
- {
257
- model: model,
258
- input: input
259
- }
260
- end
261
-
262
- def embeddings_response(response, request_params = nil)
263
- message = ActiveAgent::ActionPrompt::Message.new(content: response.dig("data", 0, "embedding"), role: "assistant")
264
-
265
- @response = ActiveAgent::GenerationProvider::Response.new(
266
- prompt: prompt,
267
- message: message,
268
- raw_response: response,
269
- raw_request: request_params
270
- )
271
- end
272
-
273
- def embeddings_prompt(parameters:)
274
- params = embeddings_parameters
275
- embeddings_response(@client.embeddings(parameters: params), params)
276
- end
277
- end
278
- end
279
- end
@@ -1,385 +0,0 @@
1
- require "openai"
2
- require_relative "open_ai_provider"
3
-
4
- module ActiveAgent
5
- module GenerationProvider
6
- class OpenRouterProvider < OpenAIProvider
7
- def initialize(config)
8
- @config = config
9
- @access_token = config["api_key"] || config["access_token"] ||
10
- ENV["OPENROUTER_API_KEY"] || ENV["OPENROUTER_ACCESS_TOKEN"]
11
- @model_name = config["model"]
12
-
13
- # OpenRouter-specific configuration
14
- @app_name = config["app_name"] || default_app_name
15
- @site_url = config["site_url"] || default_site_url
16
- @enable_fallbacks = config["enable_fallbacks"] != false
17
- @fallback_models = config["fallback_models"] || []
18
- @transforms = config["transforms"] || []
19
- @provider_preferences = config["provider"] || {}
20
- @track_costs = config["track_costs"] != false
21
- @route = config["route"] || "fallback"
22
-
23
- # Data collection preference (allow, deny, or specific provider list)
24
- @data_collection = config["data_collection"] || @provider_preferences["data_collection"] || "allow"
25
-
26
- # Require parameters preference (defaults to false)
27
- @require_parameters = config["require_parameters"] || @provider_preferences["require_parameters"] || false
28
-
29
- # Additional OpenRouter provider routing options
30
- @only_providers = config["only"] || @provider_preferences["only"]
31
- @ignore_providers = config["ignore"] || @provider_preferences["ignore"]
32
- @quantizations = config["quantizations"] || @provider_preferences["quantizations"]
33
- @sort_preference = config["sort"] || @provider_preferences["sort"]
34
- @max_price = config["max_price"] || @provider_preferences["max_price"]
35
-
36
- # Initialize OpenAI client with OpenRouter base URL
37
- @client = OpenAI::Client.new(
38
- uri_base: "https://openrouter.ai/api/v1",
39
- access_token: @access_token,
40
- log_errors: Rails.env.development?,
41
- extra_headers: openrouter_headers
42
- )
43
- end
44
-
45
- def generate(prompt)
46
- @prompt = prompt
47
-
48
- with_error_handling do
49
- parameters = build_openrouter_parameters
50
- response = execute_with_fallback(parameters)
51
- process_openrouter_response(response)
52
- end
53
- rescue => e
54
- handle_openrouter_error(e)
55
- end
56
-
57
- protected
58
-
59
- def build_provider_parameters
60
- # Start with base OpenAI parameters
61
- params = super
62
-
63
- # Add OpenRouter-specific parameters
64
- add_openrouter_params(params)
65
- end
66
-
67
- def format_content_item(item)
68
- # Handle OpenRouter-specific content formats
69
- if item.is_a?(Hash)
70
- case item[:type] || item["type"]
71
- when "file"
72
- # Convert file type to image_url for OpenRouter PDF support
73
- file_data = item.dig(:file, :file_data) || item.dig("file", "file_data")
74
- if file_data
75
- {
76
- type: "image_url",
77
- image_url: {
78
- url: file_data
79
- }
80
- }
81
- else
82
- item
83
- end
84
- else
85
- # Use default formatting for other types
86
- super
87
- end
88
- else
89
- super
90
- end
91
- end
92
-
93
- private
94
-
95
- def default_app_name
96
- if defined?(Rails) && Rails.application
97
- Rails.application.class.name.split("::").first
98
- else
99
- "ActiveAgent"
100
- end
101
- end
102
-
103
- def default_site_url
104
- # First check ActiveAgent config
105
- return config["default_url_options"]["host"] if config.dig("default_url_options", "host")
106
-
107
- # Then check Rails routes default_url_options
108
- if defined?(Rails) && Rails.application&.routes&.default_url_options&.any?
109
- host = Rails.application.routes.default_url_options[:host]
110
- port = Rails.application.routes.default_url_options[:port]
111
- protocol = Rails.application.routes.default_url_options[:protocol] || "https"
112
-
113
- if host
114
- url = "#{protocol}://#{host}"
115
- url += ":#{port}" if port && port != 80 && port != 443
116
- return url
117
- end
118
- end
119
-
120
- # Finally check ActionMailer config as fallback
121
- if defined?(Rails) && Rails.application&.config&.action_mailer&.default_url_options
122
- options = Rails.application.config.action_mailer.default_url_options
123
- host = options[:host]
124
- port = options[:port]
125
- protocol = options[:protocol] || "https"
126
-
127
- if host
128
- url = "#{protocol}://#{host}"
129
- url += ":#{port}" if port && port != 80 && port != 443
130
- return url
131
- end
132
- end
133
-
134
- nil
135
- end
136
-
137
- def openrouter_headers
138
- headers = {}
139
- headers["HTTP-Referer"] = @site_url if @site_url
140
- headers["X-Title"] = @app_name if @app_name
141
- headers
142
- end
143
-
144
- def build_openrouter_parameters
145
- parameters = prompt_parameters
146
-
147
- # Handle multiple models for fallback
148
- if @fallback_models.present?
149
- parameters[:models] = [ @model_name ] + @fallback_models
150
- parameters[:route] = @route
151
- end
152
-
153
- # Add transforms if specified
154
- parameters[:transforms] = @transforms if @transforms.present?
155
-
156
- # Add provider preferences (always include if we have data_collection or other settings)
157
- # Check both configured and runtime data_collection/require_parameters values
158
- runtime_data_collection = prompt&.options&.key?(:data_collection)
159
- runtime_require_parameters = prompt&.options&.key?(:require_parameters)
160
- runtime_provider_options = prompt&.options&.keys&.any? { |k| [ :only, :ignore, :quantizations, :sort, :max_price ].include?(k) }
161
-
162
- if @provider_preferences.present? || @data_collection != "allow" || @require_parameters != false ||
163
- @only_providers.present? || @ignore_providers.present? || @quantizations.present? ||
164
- @sort_preference.present? || @max_price.present? ||
165
- runtime_data_collection || runtime_require_parameters || runtime_provider_options
166
- parameters[:provider] = build_provider_preferences
167
- end
168
-
169
- # Add plugins (e.g., for PDF processing)
170
-
171
- parameters[:plugins] = prompt.options[:plugins] if prompt.options[:plugins].present?
172
- parameters[:models] = prompt.options[:fallback_models] if prompt.options[:enable_fallbacks] && prompt.options[:fallback_models].present?
173
- parameters
174
- end
175
-
176
- def build_provider_preferences
177
- prefs = {}
178
- prefs[:order] = @provider_preferences["order"] if @provider_preferences["order"]
179
-
180
- # Require parameters can be overridden at runtime
181
- require_parameters = prompt.options[:require_parameters] if prompt&.options&.key?(:require_parameters)
182
- require_parameters = @require_parameters if require_parameters.nil?
183
- prefs[:require_parameters] = require_parameters if require_parameters != false
184
-
185
- prefs[:allow_fallbacks] = @enable_fallbacks
186
-
187
- # Data collection can be:
188
- # - "allow" (default): Allow all providers to collect data
189
- # - "deny": Deny all providers from collecting data
190
- # - Array of provider names: Only allow these providers to collect data
191
- # Check prompt options first (runtime override), then fall back to configured value
192
- data_collection = prompt.options[:data_collection] if prompt&.options&.key?(:data_collection)
193
- data_collection ||= @data_collection
194
- prefs[:data_collection] = data_collection
195
-
196
- # Additional OpenRouter provider routing options - check runtime overrides first
197
- only_providers = prompt.options[:only] if prompt&.options&.key?(:only)
198
- only_providers ||= @only_providers
199
- prefs[:only] = only_providers if only_providers.present?
200
-
201
- ignore_providers = prompt.options[:ignore] if prompt&.options&.key?(:ignore)
202
- ignore_providers ||= @ignore_providers
203
- prefs[:ignore] = ignore_providers if ignore_providers.present?
204
-
205
- quantizations = prompt.options[:quantizations] if prompt&.options&.key?(:quantizations)
206
- quantizations ||= @quantizations
207
- prefs[:quantizations] = quantizations if quantizations.present?
208
-
209
- sort_preference = prompt.options[:sort] if prompt&.options&.key?(:sort)
210
- sort_preference ||= @sort_preference
211
- prefs[:sort] = sort_preference if sort_preference.present?
212
-
213
- max_price = prompt.options[:max_price] if prompt&.options&.key?(:max_price)
214
- max_price ||= @max_price
215
- prefs[:max_price] = max_price if max_price.present?
216
-
217
- prefs.compact
218
- end
219
-
220
- def add_openrouter_params(params)
221
- # Add OpenRouter-specific routing parameters
222
- if @enable_fallbacks && @fallback_models.present?
223
- params[:models] = [ @model_name ] + @fallback_models
224
- params[:route] = @route
225
- end
226
-
227
- # Add transforms
228
- params[:transforms] = @transforms if @transforms.present?
229
-
230
- # Add provider configuration (always include if we have data_collection or other settings)
231
- # Check both configured and runtime data_collection/require_parameters values
232
- runtime_data_collection = prompt&.options&.key?(:data_collection)
233
- runtime_require_parameters = prompt&.options&.key?(:require_parameters)
234
- runtime_provider_options = prompt&.options&.keys&.any? { |k| [ :only, :ignore, :quantizations, :sort, :max_price ].include?(k) }
235
-
236
- if @provider_preferences.present? || @data_collection != "allow" || @require_parameters != false ||
237
- @only_providers.present? || @ignore_providers.present? || @quantizations.present? ||
238
- @sort_preference.present? || @max_price.present? ||
239
- runtime_data_collection || runtime_require_parameters || runtime_provider_options
240
- params[:provider] = build_provider_preferences
241
- end
242
-
243
- # Add plugins (e.g., for PDF processing)
244
- if prompt.options[:plugins].present?
245
- params[:plugins] = prompt.options[:plugins]
246
- end
247
-
248
- params
249
- end
250
-
251
- def execute_with_fallback(parameters)
252
- parameters[:stream] = provider_stream if prompt.options[:stream] || config["stream"]
253
-
254
- response = @client.chat(parameters: parameters)
255
- # Log if fallback was used
256
- if response.respond_to?(:headers) && response.headers["x-model"] != @model_name
257
- Rails.logger.info "[OpenRouter] Fallback model used: #{response.headers['x-model']}" if defined?(Rails)
258
- end
259
-
260
- response
261
- end
262
-
263
- def process_openrouter_response(response)
264
- # Process as normal OpenAI response first
265
- if prompt.options[:stream]
266
- return @response
267
- end
268
-
269
- # Extract standard response
270
- message_json = response.dig("choices", 0, "message")
271
- message_json["id"] = response.dig("id") if message_json && message_json["id"].blank?
272
- message = handle_message(message_json) if message_json
273
-
274
- update_context(prompt: prompt, message: message, response: response) if message
275
- # Create response with OpenRouter metadata
276
- @response = ActiveAgent::GenerationProvider::Response.new(
277
- prompt: prompt,
278
- message: message,
279
- raw_response: response
280
- )
281
-
282
- # OpenRouter includes provider and model info directly in the response body
283
- if response["provider"] || response["model"]
284
- @response.metadata = {
285
- provider: response["provider"],
286
- model_used: response["model"],
287
- fallback_used: response["model"] != @model_name
288
- }.compact
289
- end
290
-
291
- # Track costs if enabled
292
- track_usage(response) if @track_costs && response["usage"]
293
-
294
- @response
295
- end
296
-
297
- def add_openrouter_metadata(response, headers)
298
- return unless response.respond_to?(:metadata=)
299
-
300
- response.metadata = {
301
- provider: headers["x-provider"],
302
- model_used: headers["x-model"],
303
- trace_id: headers["x-trace-id"],
304
- fallback_used: headers["x-model"] != @model_name,
305
- ratelimit: {
306
- requests_limit: headers["x-ratelimit-requests-limit"],
307
- requests_remaining: headers["x-ratelimit-requests-remaining"],
308
- requests_reset: headers["x-ratelimit-requests-reset"],
309
- tokens_limit: headers["x-ratelimit-tokens-limit"],
310
- tokens_remaining: headers["x-ratelimit-tokens-remaining"],
311
- tokens_reset: headers["x-ratelimit-tokens-reset"]
312
- }.compact
313
- }.compact
314
- end
315
-
316
- def track_usage(response)
317
- return nil unless @track_costs
318
- return nil unless response["usage"]
319
-
320
- usage = response["usage"]
321
- model = response.dig("model") || @model_name
322
-
323
- # Calculate costs (simplified - would need actual pricing data)
324
- cost_info = {
325
- model: model,
326
- prompt_tokens: usage["prompt_tokens"],
327
- completion_tokens: usage["completion_tokens"],
328
- total_tokens: usage["total_tokens"]
329
- }
330
-
331
- # Log usage information
332
- if defined?(Rails)
333
- Rails.logger.info "[OpenRouter] Usage: #{cost_info.to_json}"
334
-
335
- # Store in cache if available
336
- if Rails.cache
337
- cache_key = "openrouter:usage:#{Date.current}"
338
- Rails.cache.increment("#{cache_key}:tokens", usage["total_tokens"])
339
- Rails.cache.increment("#{cache_key}:requests")
340
- end
341
- end
342
-
343
- cost_info
344
- end
345
-
346
- def handle_openrouter_error(error)
347
- error_message = error.message || error.to_s
348
-
349
- case error_message
350
- when /rate limit/i
351
- handle_rate_limit_error(error)
352
- when /insufficient credits|payment required/i
353
- handle_insufficient_credits(error)
354
- when /no available provider/i
355
- handle_no_provider_error(error)
356
- when /timeout/i
357
- handle_timeout_error(error)
358
- else
359
- # Fall back to parent error handling
360
- raise GenerationProviderError, error, error.backtrace
361
- end
362
- end
363
-
364
- def handle_rate_limit_error(error)
365
- Rails.logger.error "[OpenRouter] Rate limit exceeded: #{error.message}" if defined?(Rails)
366
- raise GenerationProviderError, "OpenRouter rate limit exceeded. Please retry later."
367
- end
368
-
369
- def handle_insufficient_credits(error)
370
- Rails.logger.error "[OpenRouter] Insufficient credits: #{error.message}" if defined?(Rails)
371
- raise GenerationProviderError, "OpenRouter account has insufficient credits."
372
- end
373
-
374
- def handle_no_provider_error(error)
375
- Rails.logger.error "[OpenRouter] No available provider: #{error.message}" if defined?(Rails)
376
- raise GenerationProviderError, "No available provider for the requested model."
377
- end
378
-
379
- def handle_timeout_error(error)
380
- Rails.logger.error "[OpenRouter] Request timeout: #{error.message}" if defined?(Rails)
381
- raise GenerationProviderError, "OpenRouter request timed out."
382
- end
383
- end
384
- end
385
- end