Module: SavvyOpenrouter::RequestPlugins
- Defined in:
- lib/savvy_openrouter/request_plugins.rb
Overview
Optional chat / Responses request shaping (OpenRouter plugins). Pure Ruby.
require "savvy_openrouter/request_plugins"
SavvyOpenrouter::RequestPlugins.prepare_chat_body!(body, pdf_engine: "native")
Constant Summary collapse
- RESPONSE_HEALING_PLUGIN =
{ id: "response-healing" }.freeze
- FILE_PARSER_DEFAULT_ENGINE =
"cloudflare-ai"
Class Method Summary collapse
- .chat_messages_include_pdf_attachment?(messages) ⇒ Boolean
- .content_part_is_pdf_file?(part) ⇒ Boolean
-
.ensure_pdf_file_parser_plugin!(body, pdf_engine: nil) ⇒ Object
pdf_engine— optional override (e.g. from config: “native” for Gemini, “mistral-ocr” for scans). - .ensure_response_healing_for_chat!(body) ⇒ Object
- .ensure_response_healing_for_responses!(body) ⇒ Object
- .file_parser_pdf_engine_set?(plugin) ⇒ Boolean
- .message_includes_pdf_file?(msg) ⇒ Boolean
- .normalize_pdf_engine(pdf_engine) ⇒ Object
- .pdf_file_attachment?(file) ⇒ Boolean
- .pdf_url?(url) ⇒ Boolean
- .plugin_entry_id(entry) ⇒ Object
- .plugin_present?(plugins, id:) ⇒ Boolean
- .plugins_array_from(body) ⇒ Object
- .prepare_chat_body!(body, pdf_engine: nil) ⇒ Object
- .prepare_responses_body!(body) ⇒ Object
- .response_healing_plugin_present?(plugins) ⇒ Boolean
Class Method Details
.chat_messages_include_pdf_attachment?(messages) ⇒ Boolean
94 95 96 97 98 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 94 def () return false unless .is_a?(Array) .any? { |msg| (msg) } end |
.content_part_is_pdf_file?(part) ⇒ Boolean
109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 109 def content_part_is_pdf_file?(part) return false unless part.is_a?(Hash) type = (part[:type] || part["type"]).to_s return false unless type == "file" file = part[:file] || part["file"] return false unless file.is_a?(Hash) (file) end |
.ensure_pdf_file_parser_plugin!(body, pdf_engine: nil) ⇒ Object
pdf_engine — optional override (e.g. from config: “native” for Gemini, “mistral-ocr” for scans). When nil, uses FILE_PARSER_DEFAULT_ENGINE.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 59 def ensure_pdf_file_parser_plugin!(body, pdf_engine: nil) = body[:messages] || body["messages"] return unless () engine = normalize_pdf_engine(pdf_engine) plugins = plugins_array_from(body) idx = plugins.find_index { |p| plugin_entry_id(p) == "file-parser" } if idx return if file_parser_pdf_engine_set?(plugins[idx]) fp = plugins[idx].dup pdf_src = fp[:pdf] || fp["pdf"] merged_pdf = if pdf_src.is_a?(Hash) pdf_src.transform_keys(&:to_sym) else {} end merged_pdf[:engine] = engine fp[:pdf] = merged_pdf fp.delete("pdf") plugins[idx] = fp body[:plugins] = plugins else body[:plugins] = plugins + [{ id: "file-parser", pdf: { engine: engine } }] end body.delete("plugins") end |
.ensure_response_healing_for_chat!(body) ⇒ Object
32 33 34 35 36 37 38 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 32 def ensure_response_healing_for_chat!(body) plugins_arr = plugins_array_from(body) return if response_healing_plugin_present?(plugins_arr) body[:plugins] = plugins_arr + [RESPONSE_HEALING_PLUGIN.dup] body.delete("plugins") end |
.ensure_response_healing_for_responses!(body) ⇒ Object
40 41 42 43 44 45 46 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 40 def ensure_response_healing_for_responses!(body) plugins_arr = plugins_array_from(body) return if response_healing_plugin_present?(plugins_arr) body[:plugins] = plugins_arr + [RESPONSE_HEALING_PLUGIN.dup] body.delete("plugins") end |
.file_parser_pdf_engine_set?(plugin) ⇒ Boolean
142 143 144 145 146 147 148 149 150 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 142 def file_parser_pdf_engine_set?(plugin) return false unless plugin.is_a?(Hash) pdf = plugin[:pdf] || plugin["pdf"] return false unless pdf.is_a?(Hash) eng = (pdf[:engine] || pdf["engine"]).to_s !eng.strip.empty? end |
.message_includes_pdf_file?(msg) ⇒ Boolean
100 101 102 103 104 105 106 107 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 100 def (msg) return false unless msg.is_a?(Hash) content = msg[:content] || msg["content"] return false unless content.is_a?(Array) content.any? { |part| content_part_is_pdf_file?(part) } end |
.normalize_pdf_engine(pdf_engine) ⇒ Object
89 90 91 92 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 89 def normalize_pdf_engine(pdf_engine) s = pdf_engine.to_s.strip s.empty? ? FILE_PARSER_DEFAULT_ENGINE : s end |
.pdf_file_attachment?(file) ⇒ Boolean
121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 121 def (file) name = (file[:filename] || file["filename"]).to_s return true if name.match?(/\.pdf\z/i) data = file[:file_data] || file["file_data"] || file[:fileData] || file["fileData"] return false unless data.is_a?(String) return true if data.match?(%r{\Adata:application/pdf(?:;|\z)}i) pdf_url?(data) end |
.pdf_url?(url) ⇒ Boolean
133 134 135 136 137 138 139 140 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 133 def pdf_url?(url) return false unless url.match?(%r{\Ahttps?://}i) uri = URI.parse(url) uri.path.to_s.match?(/\.pdf\z/i) rescue URI::InvalidURIError false end |
.plugin_entry_id(entry) ⇒ Object
158 159 160 161 162 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 158 def plugin_entry_id(entry) return unless entry.is_a?(Hash) (entry[:id] || entry["id"]).to_s end |
.plugin_present?(plugins, id:) ⇒ Boolean
152 153 154 155 156 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 152 def plugin_present?(plugins, id:) return false unless plugins.is_a?(Array) plugins.any? { |p| plugin_entry_id(p) == id.to_s } end |
.plugins_array_from(body) ⇒ Object
48 49 50 51 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 48 def plugins_array_from(body) p = body[:plugins] || body["plugins"] p.is_a?(Array) ? p.dup : [] end |
.prepare_chat_body!(body, pdf_engine: nil) ⇒ Object
17 18 19 20 21 22 23 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 17 def prepare_chat_body!(body, pdf_engine: nil) return body unless body.is_a?(Hash) ensure_response_healing_for_chat!(body) if SavvyOpenrouter::Patterns.chat_structured_requested?(body) ensure_pdf_file_parser_plugin!(body, pdf_engine: pdf_engine) body end |
.prepare_responses_body!(body) ⇒ Object
25 26 27 28 29 30 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 25 def prepare_responses_body!(body) return body unless body.is_a?(Hash) ensure_response_healing_for_responses!(body) if SavvyOpenrouter::Patterns.responses_structured_requested?(body) body end |
.response_healing_plugin_present?(plugins) ⇒ Boolean
53 54 55 |
# File 'lib/savvy_openrouter/request_plugins.rb', line 53 def response_healing_plugin_present?(plugins) plugin_present?(plugins, id: "response-healing") end |