activeagent 0.6.2 → 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 +319 -61
  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 -65
  257. data/lib/active_agent/generation_provider/open_ai_provider.rb +0 -277
  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,277 +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
- @access_token ||= config["api_key"] || config["access_token"] || OpenAI.configuration.access_token || ENV["OPENAI_ACCESS_TOKEN"]
26
- @organization_id = config["organization_id"] || OpenAI.configuration.organization_id || ENV["OPENAI_ORGANIZATION_ID"]
27
- @admin_token = config["admin_token"] || OpenAI.configuration.admin_token || ENV["OPENAI_ADMIN_TOKEN"]
28
- @client = OpenAI::Client.new(
29
- access_token: @access_token,
30
- uri_base: @host,
31
- organization_id: @organization_id,
32
- admin_token: @admin_token,
33
- log_errors: Rails.env.development?
34
- )
35
-
36
- @model_name = config["model"] || "gpt-4o-mini"
37
- end
38
-
39
- def generate(prompt)
40
- @prompt = prompt
41
-
42
- with_error_handling do
43
- if @prompt.multimodal? || @prompt.content_type == "multipart/mixed"
44
- responses_prompt(parameters: responses_parameters)
45
- else
46
- chat_prompt(parameters: prompt_parameters)
47
- end
48
- end
49
- end
50
-
51
- def embed(prompt)
52
- @prompt = prompt
53
-
54
- with_error_handling do
55
- embeddings_prompt(parameters: embeddings_parameters)
56
- end
57
- end
58
-
59
- protected
60
-
61
- # Override from StreamProcessing module
62
- def process_stream_chunk(chunk, message, agent_stream)
63
- new_content = chunk.dig("choices", 0, "delta", "content")
64
- if new_content && !new_content.blank?
65
- message.generation_id = chunk.dig("id")
66
- message.content += new_content
67
- # Call agent_stream directly without the block to avoid double execution
68
- agent_stream&.call(message, new_content, false, prompt.action_name)
69
- elsif chunk.dig("choices", 0, "delta", "tool_calls") && chunk.dig("choices", 0, "delta", "role")
70
- message = handle_message(chunk.dig("choices", 0, "delta"))
71
- prompt.messages << message
72
- @response = ActiveAgent::GenerationProvider::Response.new(
73
- prompt:,
74
- message:,
75
- raw_response: chunk,
76
- raw_request: @streaming_request_params
77
- )
78
- end
79
-
80
- if chunk.dig("choices", 0, "finish_reason")
81
- finalize_stream(message, agent_stream)
82
- end
83
- end
84
-
85
- # Override from MessageFormatting module to handle OpenAI image format
86
- def format_image_content(message)
87
- [ {
88
- type: "image_url",
89
- image_url: { url: message.content }
90
- } ]
91
- end
92
-
93
- private
94
-
95
- # Override from ParameterBuilder to add web_search_options for Chat API
96
- def build_provider_parameters
97
- params = {}
98
-
99
- # Check if we're using a model that supports web_search_options in Chat API
100
- if chat_api_web_search_model? && @prompt.options[:web_search]
101
- params[:web_search_options] = build_web_search_options(@prompt.options[:web_search])
102
- end
103
-
104
- params
105
- end
106
-
107
- def chat_api_web_search_model?
108
- model = @prompt.options[:model] || @model_name
109
- [ "gpt-4o-search-preview", "gpt-4o-mini-search-preview" ].include?(model)
110
- end
111
-
112
- def build_web_search_options(web_search_config)
113
- options = {}
114
-
115
- if web_search_config.is_a?(Hash)
116
- options[:search_context_size] = web_search_config[:search_context_size] if web_search_config[:search_context_size]
117
-
118
- if web_search_config[:user_location]
119
- options[:user_location] = {
120
- type: "approximate",
121
- approximate: web_search_config[:user_location]
122
- }
123
- end
124
- end
125
-
126
- options
127
- end
128
-
129
- def chat_response(response, request_params = nil)
130
- return @response if prompt.options[:stream]
131
- message_json = response.dig("choices", 0, "message")
132
- message_json["id"] = response.dig("id") if message_json["id"].blank?
133
- message = handle_message(message_json)
134
-
135
- update_context(prompt: prompt, message: message, response: response)
136
-
137
- @response = ActiveAgent::GenerationProvider::Response.new(
138
- prompt: prompt,
139
- message: message,
140
- raw_response: response,
141
- raw_request: request_params
142
- )
143
- end
144
-
145
- def responses_response(response, request_params = nil)
146
- message_json = response["output"].find { |output_item| output_item["type"] == "message" }
147
- message_json["id"] = response.dig("id") if message_json["id"].blank?
148
-
149
- message = ActiveAgent::ActionPrompt::Message.new(
150
- generate_id: message_json["id"],
151
- content: message_json["content"].first["text"],
152
- role: message_json["role"].intern,
153
- action_requested: message_json["finish_reason"] == "tool_calls",
154
- raw_actions: message_json["tool_calls"] || [],
155
- content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
156
- )
157
-
158
- @response = ActiveAgent::GenerationProvider::Response.new(
159
- prompt: prompt,
160
- message: message,
161
- raw_response: response,
162
- raw_request: request_params
163
- )
164
- end
165
-
166
- def handle_message(message_json)
167
- ActiveAgent::ActionPrompt::Message.new(
168
- generation_id: message_json["id"],
169
- content: message_json["content"],
170
- role: message_json["role"].intern,
171
- action_requested: message_json["finish_reason"] == "tool_calls",
172
- raw_actions: message_json["tool_calls"] || [],
173
- requested_actions: handle_actions(message_json["tool_calls"]),
174
- content_type: prompt.output_schema.present? ? "application/json" : "text/plain"
175
- )
176
- end
177
-
178
- # handle_actions is now provided by ToolManagement module
179
-
180
- def chat_prompt(parameters: prompt_parameters)
181
- if prompt.options[:stream] || config["stream"]
182
- parameters[:stream] = provider_stream
183
- @streaming_request_params = parameters
184
- end
185
- chat_response(@client.chat(parameters: parameters), parameters)
186
- end
187
-
188
- def responses_prompt(parameters: responses_parameters)
189
- # parameters[:stream] = provider_stream if prompt.options[:stream] || config["stream"]
190
- responses_response(@client.responses.create(parameters: parameters), parameters)
191
- end
192
-
193
- 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)
194
- # Build tools array, combining action tools with built-in tools
195
- tools_array = build_tools_for_responses(tools)
196
-
197
- {
198
- model: model,
199
- input: ActiveAgent::GenerationProvider::ResponsesAdapter.new(@prompt).input,
200
- tools: tools_array.presence,
201
- text: structured_output
202
- }.compact
203
- end
204
-
205
- def build_tools_for_responses(action_tools)
206
- tools = []
207
-
208
- # Start with action tools (user-defined functions) if any
209
- tools.concat(action_tools) if action_tools.present?
210
-
211
- # Add built-in tools if specified in options[:tools]
212
- if @prompt.options[:tools].present?
213
- built_in_tools = @prompt.options[:tools]
214
- built_in_tools = [ built_in_tools ] unless built_in_tools.is_a?(Array)
215
-
216
- built_in_tools.each do |tool|
217
- next unless tool.is_a?(Hash)
218
-
219
- case tool[:type]
220
- when "web_search_preview", "web_search"
221
- web_search_tool = { type: "web_search_preview" }
222
- web_search_tool[:search_context_size] = tool[:search_context_size] if tool[:search_context_size]
223
- web_search_tool[:user_location] = tool[:user_location] if tool[:user_location]
224
- tools << web_search_tool
225
-
226
- when "image_generation"
227
- image_gen_tool = { type: "image_generation" }
228
- image_gen_tool[:size] = tool[:size] if tool[:size]
229
- image_gen_tool[:quality] = tool[:quality] if tool[:quality]
230
- image_gen_tool[:format] = tool[:format] if tool[:format]
231
- image_gen_tool[:compression] = tool[:compression] if tool[:compression]
232
- image_gen_tool[:background] = tool[:background] if tool[:background]
233
- image_gen_tool[:partial_images] = tool[:partial_images] if tool[:partial_images]
234
- tools << image_gen_tool
235
-
236
- when "mcp"
237
- mcp_tool = { type: "mcp" }
238
- mcp_tool[:server_label] = tool[:server_label] if tool[:server_label]
239
- mcp_tool[:server_description] = tool[:server_description] if tool[:server_description]
240
- mcp_tool[:server_url] = tool[:server_url] if tool[:server_url]
241
- mcp_tool[:connector_id] = tool[:connector_id] if tool[:connector_id]
242
- mcp_tool[:authorization] = tool[:authorization] if tool[:authorization]
243
- mcp_tool[:require_approval] = tool[:require_approval] if tool[:require_approval]
244
- mcp_tool[:allowed_tools] = tool[:allowed_tools] if tool[:allowed_tools]
245
- tools << mcp_tool
246
- end
247
- end
248
- end
249
-
250
- tools
251
- end
252
-
253
- def embeddings_parameters(input: prompt.message.content, model: "text-embedding-3-large")
254
- {
255
- model: model,
256
- input: input
257
- }
258
- end
259
-
260
- def embeddings_response(response, request_params = nil)
261
- message = ActiveAgent::ActionPrompt::Message.new(content: response.dig("data", 0, "embedding"), role: "assistant")
262
-
263
- @response = ActiveAgent::GenerationProvider::Response.new(
264
- prompt: prompt,
265
- message: message,
266
- raw_response: response,
267
- raw_request: request_params
268
- )
269
- end
270
-
271
- def embeddings_prompt(parameters:)
272
- params = embeddings_parameters
273
- embeddings_response(@client.embeddings(parameters: params), params)
274
- end
275
- end
276
- end
277
- 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